Java高并发程序设计笔记(三)之Volatile与Lock

时间:2022-03-25 23:51:23

volatile关键字

为了在适当的场合确保线程间的有序性,原子性和一致性,Java使用了一些特殊的关键字或者操作来声明,告诉虚拟机,这些地方要尤其注意,不能随意变化优化目标的指令。关键字volatile就是其中之一。

当声明了volatile变量时就等于告诉了虚拟机,这个变量极有可能被某些程序或者线程修改。为了确保这个变量被修改后,应用程序内的所有线程都能看到这个改动,虚拟机必须采取一些特殊的手段,保证这个变量的可变性。

volatile保证可见性的原理是在每次访问变量时都会进行一次刷新,因此每次访问都是主内存中最新的版本。所以volatile关键字的作用之一就是保证变量修改的实时可见性。

volatile并不能保证线程安全,它只能确保一个线程修改了数据以后,其他线程能够看得到这个改动,但当两个数据同时修改了某一个数据时,却依然会产生冲突。

synchronized锁

synchronized的作用是实现线程的同步
用法:
指定对象加锁:对给定的对象加锁,进入代码前要获得对象的锁。
直接用于实例方法:相当于对当前实例加锁,进入同步代码前要获得当前实例的锁。
直接作用于静态方法:相当于对当前类加锁,进入同步代码前要获得当前类的锁。

使用Runnable接口创建两个线程,并且这两个线程都指向同一个Runnable接口实例(instance对象),这样才能保证两个线程在工作时,用到的是同一个锁,从而保证线程安全。也可以将synchronized作用于静态方法,这样即使两个线程指向不同的Runnable对象,但由于方法块指向的是当前类的锁,而不是当前实例,因此,线程间可以同步。

除了保证线程安全之外,synchronized还可以保证线程间的可见性和有序性,从可见性的角度上来说可以完全替代volatile,就有序性而言,被synchronized限制的多个线程是串行执行的。

重入锁ReentrantLock

重入锁完全可以代替synchronized关键字。与synchronized相比,重入锁有着显示的操作过程,开发人员必须手动指定何时加锁何时释放锁,,正因为这样,重入锁逻辑控制的灵活性要高于synchronized

公平锁:重入锁允许对公平性进行设置,有如下构造函数

public ReentrantLock(boolean fair)

公平锁的一大特点:不会产生饥饿现象,它会按照时间的优先次序,保证先到者先得,后到者后得,只要排队,最终是可以等到资源的。而如果用synchronized进行所控制,那么产生的锁就是非公平的。

要实现一个公平锁要维护一个有序队列,因此公平锁的实现成本比较高,性能也相对低下,默认锁是非公平的。

Condition条件

Condition和wait(),notify()的作用是类似的,但是wait( )和notify( )是和synchronized关键字合作使用的,而Condition是与重入锁关联的,利用Condition对象,我们就可以让线程在合适的时间等待,或者某个特定的时间得到通知,继续执行。

Condition接口提供的基本方法如下

void await();
void awaitUninterruptibly();
void signal();
void signalAll();

await( )方法会是当前线程等待,同时释放资源,当其他线程使用signal( )或者signalAll( )方法时 ,线程会重新获得锁并继续执行,这和Object.wait( )很相似 。

awaitUninterruptibly( )方法与await( )方法基本相同,但是它并不在等待过程中响应中断。

signal( )方法用于唤醒一个等待中的线程,相对的signalAll方法会唤醒所有等待的线程,这和Object.notify( )很相似。

和Object.wait( )以及Object.notify( )方法一样,当线程使用Condition.await( )时要求线程持有相关的重入锁,当Condition.await( )调用后,线程会释放这把锁,同理,在Condition.signal( )方法调用时,也要求线程先获得相关的锁,在调用signal( )方法后系统会从当前的Condition对象的等待队列中唤醒一个线程,然后释放相关的锁。

读写锁:ReadWriteLock

如果使用重入锁或者内部锁,那么从理论上来说所有的读读操作,读写操作和写写操作都是串行执行的。
读写分离锁可以有效的帮助减少锁的竞争,以提升系统性能。
读写锁的访问约束情况:
读读不互斥
读写互斥
写写互斥
如果在系统中,读次数远大于写次数,那么读写锁就可以发挥最大的功效,提升系统的性能。

倒计时器:CountDownLatch

CountDownLatch是一个多线程控制工具类,Latch依偎门闩,这个简称为倒计数器。这个工具类常用于控制等待线程,它可以让一个线程等待直到倒计时结束再开始执行。使用CountDownLatch.countdown( )方法,也就是通知CountDownLatch,一个线程已经完成任务,倒计时器可以减一了。CountDownLatch.await( )方法要求主线程等待所有的检查任务全部完成主线程才能继续执行。

循环栅栏:CyclicBarrier

CyclicBarrier是另一个多线程并发控制工具,和CountDownLatch类似,也可以实现多线程之间的计数等待,但功能比CountDownLatch更复杂更强大。

CyclicBarrier用于阻止线程继续执行,要求线程在栅栏处等待。Cyclic意为循环,也就是说这个计数器可以反复使用。比如,假设将计数器设置为10,那么走起第一批10个线程后,计数器就回归为0,然后紧接着凑齐下一批10个线程,这就是循环栅栏的含义。