什么是线程安全?
线程安全是指保证多线程环境下共享的、可修改的状态的正确性。
保证线程安全的两个办法:
- 封装:将对象的内部状态隐藏、保护起来。
- 不可变:final变量产生了某种程度地不可变(immutable)效果,可以用于保护只读数据。
线程安全需要保证几个基本特性:
- 原子性:相关操作不会中途被其他线程干扰,一般通过同步机制实现。
- 可见性:一个线程修改了某个共享变量,其状态能够立即被其他线程知晓,通常被解释为将线程本地状态反映到主内存上,volatile就是负责保证可见性的。
- 有序性:保证线程内串行语义,避免指令重排。
synchronized
关键字synchronized可以用来修饰方法和代码块,修饰方法也就相当于将方法放在代码块中。synchronized修饰实例方法时(对象锁),相当于synchronized (this) {}
。修饰静态方法时(类锁,也叫静态锁),相当于synchronized (ClassName.class) {}
。
对象有锁计数器,一个任务可以多次获得对象的锁,每获取一次,计数加1,每释放一次,计数减1,当计数为0时,其他任务才能获取该对象的锁(可重入性)。
Lock
java.util.concurrent.locks.Lock
被synchronized修饰的代码块,如果有一个线程获得了对应的锁,其他线程只能一直等待,直到锁被释放。原线程释放锁的情况有:
- 执行完synchronized修饰的代码块,然后释放锁。
- 线程执行发生异常,JVM会让线程自动释放锁。
其他时候,如果代码块执行时间很长,或者等待IO、sleep等发生阻塞,一直没有释放锁,其他线程只能一直等待。
本文学习完毕之后,你将能够理解Lock的优势包括:
- 可以让尝试获得锁的其他线程只等待一定时间。
- 读操作和写操作、写操作和写操作会发生冲突,但是读操作和读操作不发生冲突,Lock就可以允许这种可以同时进行的操作。(读写锁)
- Lock可以知道线程有没有成功获取到锁。
- 当竞争资源非常激烈时,Lock的性能会更好。
- sychronized是不可中断锁,Lock可中断。(可中断锁)
- Lock可设置为公平锁,按照请求锁的顺序分配锁。(公平锁)
- 可以和条件变量配合使用,对共享数据的多种状态进行监控。
但是
- Lock对象必须被显式地创建、锁定和释放。
private Lock lock = new ReentrantLock();
public void lockedMethod() {
lock.lock();;
try{