前言为何要应用Java线程同步?Java容许多线程并发管制,当多个线程同时操作一个可共享的资源变量时,将会导致数据不精确,相互之间产生抵触,因而退出同步锁以防止在该线程没有实现操作之前,被其余线程的调用,从而保障了该变量的唯一性和准确性。但其并发编程的基本,就是使线程间进行正确的通信。其中两个比拟重要的关键点,如下:Java中提供了很多线程同步操作,比方:synchronized关键字、waitnotifyAll、ReentrantLock、Condition、一些并发包下的工具类、1 ReentrantLock可重入锁
自JDK5开始,新增了Lock接口以及它的一个实现类ReentrantLock。ReentrantLock可重入锁是J.U.C包内置的一个锁对象,能够用来实现同步,根本应用办法如下:下面例子示意同一时间段只能有1个线程执行execute办法,输入如下:可重入锁中可重入示意的意义在于对于同一个线程,能够持续调用加锁的办法,而不会被挂起。可重入锁外部保护一个计数器,对于同一个线程调用lock办法,计数器+1,调用unlock办法,计数器-1。举个例子再次阐明一下可重入的意思:在一个加锁办法execute中调用另外一个加锁办法anotherLock并不会被挂起,能够间接调用(调用execute办法时计数器+1,而后外部又调用了anotherLock办法,计数器+1,变成了2):
输入:
2 synchronized
synchronized跟ReentrantLock一样,也反对可重入锁。然而它是一个关键字,是一种语法级别的同步形式,称为内置锁:输入后果跟ReentrantLock一样,这个例子阐明内置锁能够作用在办法上。synchronized关键字也能够润饰静态方法,此时如果调用该静态方法,将会锁住整个类。同步是一种高开销的操作,因而应该尽量减少同步的内容。通常没有必要同步整个办法,应用synchronized代码块同步要害代码即可。synchronized跟ReentrantLock相比,有几点局限性:所以,Lock的操作与synchronized相比,灵活性更高,而且Lock提供多种形式获取锁,有Lock、ReadWriteLock接口,以及实现这两个接口的ReentrantLock类、ReentrantReadWriteLock类。
对于Lock对象和synchronized关键字抉择的考量:在性能考量上来说,如果竞争资源不强烈,两者的性能是差不多的,而当竞争资源十分强烈时(即有大量线程同时竞争),此时Lock的性能要远远优于synchronized。所以说,在具体应用时要依据适当状况抉择。3 Condition条件对象
Condition条件对象的意义在于对于一个曾经获取Lock锁的线程,如果还须要期待其余条件能力继续执行的状况下,才会应用Condition条件对象。Condition能够代替传统的线程间通信,用await()替换wait(),用signal()替换notify(),用signalAll()替换notifyAll()。这个例子中thread1执行到condition.await()时,以后线程会被挂起,直到thread2调用了condition.signalAll()办法之后,thread1才会从新被激活执行。传统线程的通信形式,Condition都能够实现。Condition的弱小之处在于它能够为多个线程间建设不同的Condition。4 wait¬ifynotifyAll形式
Java线程的状态转换图与相干办法,如下:
线程状态转换图
在图中,红框标识的局部办法,能够认为已过期,不再应用。上图中的办法可能参加到线程同步中的办法,如下:1.wait、notify、notifyAll办法:线程中通信能够应用的办法。线程中调用了wait办法,则进入阻塞状态,只有等另一个线程调用与wait同一个对象的notify办法。这里有个非凡的中央,调用wait或者notify,前提是须要获取锁,也就是说,须要在同步块中做以上操作。这里须要留神的是调用waitnotifyAll办法的时候肯定要取得以后线程的锁,否则会产生IllegalMonitorStateException异样。2.join办法:该办法次要作用是在该线程中的run办法完结后,才往下执行。3.yield办法:线程自身的调度办法,应用时线程能够在run办法执行结束时,调用该办法,告知线程已能够出让CPU资源。4.sleep办法:通过sleep(millis)使线程进入休眠一段时间,该办法在指定的工夫内无奈被唤醒,同时也不会开释对象锁;sleep办法通知操作系统至多在指定工夫内不需为线程调度器为该线程调配执行工夫片,并不开释锁(如果以后曾经持有锁)。实际上,调用sleep办法时并不要求持有任何锁。所以,sleep办法并不需要持有任何模式的锁,也就不须要包裹在synchronized中。5 ThreadLocal
ThreadLocal是一种把变量放到线程本地的形式来实现线程同步的。比方:SimpleDateFormat不是一个线程平安的类,能够应用ThreadLocal实现同步,如下:ThreadLocal与同步机制的比照抉择:6 volatile润饰变量
volatile关键字为域变量的拜访提供了一种免锁机制,应用volatile润饰域相当于通知虚拟机该域可能会被其余线程更新,因而每次应用该域就要从新计算,而不是应用寄存器中的值,volatile不会提供任何原子操作,它也不能用来润饰final类型的变量。多线程中的非同步问题次要呈现在对域的读写上,如果让域本身防止这个问题,则就不须要批改操作该域的办法。用final域,有锁爱护的域和volatile域能够防止非同步的问题。7 Semaphore信号量
Semaphore信号量被用于管制特定资源在同一个工夫被拜访的个数。相似连接池的概念,保障资源能够被正当的应用。能够应用结构器初始化资源个数:输入:
8 并发包下的工具类8.1 CountDownLatch
CountDownLatch是一个计数器,它的构造方法中须要设置一个数值,用来设定计数的次数。每次调用countDown()办法之后,这个计数器都会减去1,CountDownLatch会始终阻塞着调用await()办法的线程,直到计数器的值变为0。输入:
8.2 CyclicBarrier
CyclicBarrier阻塞调用的线程,直到条件满足时,阻塞的线程同时被关上。
相比CountDownLatch,CyclicBarrier是能够被循环应用的,而且遇到线程中断等状况时,还能够利用reset()办法,重置计数器,从这些方面来说,CyclicBarrier会比CountDownLatch更加灵便一些。9 应用原子变量实现线程同步
有时须要应用线程同步的根本原因在于对一般变量的操作不是原子的。那么什么是原子操作呢?在java.util.concurrent.atomic包中提供了创立原子类型变量的工具类,应用该类能够简化线程同步。比方:其中AtomicInteger以原子形式更新int的值:
10 AbstractQueuedSynchronizer
AQS是很多同步工具类的根底,比方:ReentrantLock里的偏心锁和非偏心锁,Semaphore里的偏心锁和非偏心锁,CountDownLatch里的锁等他们的底层都是应用AbstractQueuedSynchronizer实现的。基于AbstractQueuedSynchronizer自定义实现一个独占锁:11 应用阻塞队列实现线程同步
后面几种同步形式都是基于底层实现的线程同步,然而在理论开发当中,该当尽量远离底层构造。本节次要是应用LinkedBlockingQueue<E>来实现线程的同步。
0人点赞
日记本
但其并发编程的基本,就是使线程间进行正确的通信。其中两个比拟重要的关键点,如下: Java中提供了很多线程同步操作,比方:synchronized关键字、waitnotifyAll、ReentrantLock、Condition、一些并发包下的工具类、 自JDK5开始,新增了Lock接口以及它的一个实现类ReentrantLock。ReentrantLock可重入锁是J.U.C包内置的一个锁对象,能够用来实现同步,根本应用办法如下: 下面例子示意同一时间段只能有1个线程执行execute办法,输入如下: 可重入锁中可重入示意的意义在于对于同一个线程,能够持续调用加锁的办法,而不会被挂起。可重入锁外部保护一个计数器,对于同一个线程调用lock办法,计数器+1,调用unlock办法,计数器-1。 举个例子再次阐明一下可重入的意思:在一个加锁办法execute中调用另外一个加锁办法anotherLock并不会被挂起,能够间接调用(调用execute办法时计数器+1,而后外部又调用了anotherLock办法,计数器+1,变成了2): 输入: synchronized跟ReentrantLock一样,也反对可重入锁。然而它是一个关键字,是一种语法级别的同步形式,称为内置锁: 输入后果跟ReentrantLock一样,这个例子阐明内置锁能够作用在办法上。synchronized关键字也能够润饰静态方法,此时如果调用该静态方法,将会锁住整个类。 同步是一种高开销的操作,因而应该尽量减少同步的内容。通常没有必要同步整个办法,应用synchronized代码块同步要害代码即可。 synchronized跟ReentrantLock相比,有几点局限性: 所以,Lock的操作与synchronized相比,灵活性更高,而且Lock提供多种形式获取锁,有Lock、ReadWriteLock接口,以及实现这两个接口的ReentrantLock类、ReentrantReadWriteLock类。 对于Lock对象和synchronized关键字抉择的考量: 在性能考量上来说,如果竞争资源不强烈,两者的性能是差不多的,而当竞争资源十分强烈时(即有大量线程同时竞争),此时Lock的性能要远远优于synchronized。所以说,在具体应用时要依据适当状况抉择。 Condition条件对象的意义在于对于一个曾经获取Lock锁的线程,如果还须要期待其余条件能力继续执行的状况下,才会应用Condition条件对象。 Condition能够代替传统的线程间通信,用await()替换wait(),用signal()替换notify(),用signalAll()替换notifyAll()。 这个例子中thread1执行到condition.await()时,以后线程会被挂起,直到thread2调用了condition.signalAll()办法之后,thread1才会从新被激活执行。 传统线程的通信形式,Condition都能够实现。Condition的弱小之处在于它能够为多个线程间建设不同的Condition。 Java线程的状态转换图与相干办法,如下: 线程状态转换图 在图中,红框标识的局部办法,能够认为已过期,不再应用。上图中的办法可能参加到线程同步中的办法,如下: 1.wait、notify、notifyAll办法:线程中通信能够应用的办法。线程中调用了wait办法,则进入阻塞状态,只有等另一个线程调用与wait同一个对象的notify办法。这里有个非凡的中央,调用wait或者notify,前提是须要获取锁,也就是说,须要在同步块中做以上操作。 这里须要留神的是调用waitnotifyAll办法的时候肯定要取得以后线程的锁,否则会产生IllegalMonitorStateException异样。 2.join办法:该办法次要作用是在该线程中的run办法完结后,才往下执行。 3.yield办法:线程自身的调度办法,应用时线程能够在run办法执行结束时,调用该办法,告知线程已能够出让CPU资源。 4.sleep办法:通过sleep(millis)使线程进入休眠一段时间,该办法在指定的工夫内无奈被唤醒,同时也不会开释对象锁; sleep办法通知操作系统至多在指定工夫内不需为线程调度器为该线程调配执行工夫片,并不开释锁(如果以后曾经持有锁)。实际上,调用sleep办法时并不要求持有任何锁。 所以,sleep办法并不需要持有任何模式的锁,也就不须要包裹在synchronized中。 ThreadLocal是一种把变量放到线程本地的形式来实现线程同步的。比方:SimpleDateFormat不是一个线程平安的类,能够应用ThreadLocal实现同步,如下: ThreadLocal与同步机制的比照抉择: volatile关键字为域变量的拜访提供了一种免锁机制,应用volatile润饰域相当于通知虚拟机该域可能会被其余线程更新,因而每次应用该域就要从新计算,而不是应用寄存器中的值,volatile不会提供任何原子操作,它也不能用来润饰final类型的变量。 多线程中的非同步问题次要呈现在对域的读写上,如果让域本身防止这个问题,则就不须要批改操作该域的办法。用final域,有锁爱护的域和volatile域能够防止非同步的问题。 Semaphore信号量被用于管制特定资源在同一个工夫被拜访的个数。相似连接池的概念,保障资源能够被正当的应用。能够应用结构器初始化资源个数: 输入: CountDownLatch是一个计数器,它的构造方法中须要设置一个数值,用来设定计数的次数。每次调用countDown()办法之后,这个计数器都会减去1,CountDownLatch会始终阻塞着调用await()办法的线程,直到计数器的值变为0。 输入: CyclicBarrier阻塞调用的线程,直到条件满足时,阻塞的线程同时被关上。 相比CountDownLatch,CyclicBarrier是能够被循环应用的,而且遇到线程中断等状况时,还能够利用reset()办法,重置计数器,从这些方面来说,CyclicBarrier会比CountDownLatch更加灵便一些。 有时须要应用线程同步的根本原因在于对一般变量的操作不是原子的。那么什么是原子操作呢? 在java.util.concurrent.atomic包中提供了创立原子类型变量的工具类,应用该类能够简化线程同步。比方:其中AtomicInteger以原子形式更新int的值: AQS是很多同步工具类的根底,比方:ReentrantLock里的偏心锁和非偏心锁,Semaphore里的偏心锁和非偏心锁,CountDownLatch里的锁等他们的底层都是应用AbstractQueuedSynchronizer实现的。 基于AbstractQueuedSynchronizer自定义实现一个独占锁: 后面几种同步形式都是基于底层实现的线程同步,然而在理论开发当中,该当尽量远离底层构造。本节次要是应用LinkedBlockingQueue<E>来实现线程的同步。 0人点赞 日记本1 ReentrantLock可重入锁
2 synchronized
3 Condition条件对象
4 wait¬ifynotifyAll形式
5 ThreadLocal
6 volatile润饰变量
7 Semaphore信号量
8 并发包下的工具类
8.1 CountDownLatch
8.2 CyclicBarrier
9 应用原子变量实现线程同步
10 AbstractQueuedSynchronizer
11 应用阻塞队列实现线程同步