谈谈Java线程同步机制,线程锁

小知识,大挑战!本文正在参与“程序员必备小知识”创作活动。

线程同步

1. 线程同步机制简介

线程同步机制是一套用于协调线程之间的数据访问的机制.该机制可以保障线程安全.

Java 平台提供的线程同步机制包括: 锁, volatile 关键字, final 关键字,static 关键字,以及相关的 API,如 Object.wait()/Object.notify()等

2. 锁概述

线程安全问题的产生前提是多个线程并发访问共享数据.将多个线程对共享数据的并发访问转换为串行访问,即一个共享数据一次只能被一个线程访问.锁就是复用这种思路来保障线程安全的

锁(Lock)可以理解为对共享数据进行保护的一个许可证. 对于同一个许可证保护的共享数据来说,任何线程想要访问这些共享数据必须先持有该许可证. 一个线程只有在持有许可证的情况下才能对这些共享数据进行访问; 并且一个许可证一次只能被一个线程持有; 许可证线程在结束对共享数据的访问后必须释放其持有的许可证

一线程在访问共享数据前必须先获得锁; 获得锁的线程称为锁的持有线程; 一个锁一次只能被一个线程持有. 锁的持有线程在获得锁之后 和释放锁之前这段时间所执行的代码称为临界区(CriticalSection).

锁具有排他性(Exclusive), 即一个锁一次只能被一个线程持有.这种锁称为排它锁或互斥锁(Mutex).

image.png

JVM 把锁分为内部锁和显示锁两种. 内部锁通过 synchronized关键字实现; 显示锁通过 java.concurrent.locks.Lock 接口的实现类实现的

锁的作用

锁可以实现对共享数据的安全访问. 保障线程的原子性,可见性与有序性.

锁是通过互斥保障原子性. 一个锁只能被一个线程持有, 这就保证临界区的代码一次只能被一个线程执行.使得临界区代码所执行的操作自然而然的具有不可分割的特性,即具备了原子性

可见性的保障是通过写线程冲刷处理器的缓存和读线程刷新处理器缓存这两个动作实现的. 在 java 平台中,锁的获得隐含着刷新处理器缓存的动作,锁的释放隐含着冲刷处理器缓存的动作.

锁能够保障有序性.写线程在临界区所执行的在读线程所执行的临界区看来像是完全按照源码顺序执行的.

注意:

使用锁保障线程的安全性,必须满足以下条件:

这些线程在访问共享数据时必须使用同一个锁

即使是读取共享数据的线程也需要使用同步锁

锁相关的概念

1) 可重入性

可重入性(Reentrancy)描述这样一个问题: 一个线程持有该锁的时候能再次(多次)申请该锁

void

methodA(){

申请 a 锁

methodB();

释放 a 锁

}
复制代码
void methodB(){

申请 a 锁

....

释放 a 锁

}
复制代码

如果一个线程持有一个锁的时候还能够继续成功申请该锁,称该锁是可重入的, 否则就称该锁为不可重入的

2) 锁的争用与调度

Java 平台中内部锁属于非公平锁, 显示 Lock 锁既支持公平锁又支持非公平锁

3) 锁的粒度

一个锁可以保护的共享数据的数量大小称为锁的粒度.锁保护共享数据量大,称该锁的粒度粗, 否则就称该锁的粒度细.

锁的粒度过粗会导致线程在申请锁时会进行不必要的等待.锁的粒度过细会增加锁调度的开销.

猜你喜欢

转载自juejin.im/post/7017347674693173278