在使用Lock之前,我們使用的最多的同步方式應該是synchronized關鍵字來實現同步方式了。配合Object的wait()、notify()系列方法可以實現等待/通知模式。Condition接口也提供了類似Object的監視器方法,與Lock配合可以實現等待/通知模式
下面用的是lock鎖
首先我們需要明白condition對象是依賴於lock對象的,condition對象需要通過lock對象進行創建出來(調用Lock對象的newCondition()方法)。condition的使用需要在調用方法前獲取鎖。
1.傳統版
- 題目:一個初始值爲0的變量,兩個線程一個加1一個減1,輪詢5輪
-
- 線程 操作(方法) 資源類
-
- 判斷 幹活 通知
-
- 防止虛假喚醒機制(多線程判斷用while不用if)
package JUC;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
class ShareData {
private int number = 0;
Lock lock = new ReentrantLock();
private Condition condition = lock.newCondition();
public void increment() throws InterruptedException {
lock.lock();
try {
//1.判斷
while (number != 0) {//線程判斷用while
//等待不能生產
condition.await();
}
//2.幹活
number++;
System.out.println(Thread.currentThread().getName() + "\t" + number);
//3.通知喚醒
condition.signalAll();
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
public void decrement() throws InterruptedException {
lock.lock();
try {
//1.判斷
while (number == 0) {//線程判斷用while
//等待不能生產
condition.await();
}
//2.幹活
number--;
System.out.println(Thread.currentThread().getName() + "\t" + number);
//3.通知喚醒
condition.signalAll();
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
}
/**
* 題目:一個初始值爲0的變量,兩個線程一個加1一個減1,輪詢5輪
* <p>
* 1. 線程 操作(方法) 資源類
* 2. 判斷 幹活 通知
* 3.防止虛假喚醒機制(多線程判斷用while不用if)
*/
public class ProdConsumer_TraditonDemo {
public static void main(String[] args) {
ShareData shareData = new ShareData();
new Thread(() -> {
for (int j = 0; j < 5; j++) {
try {
shareData.increment();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}, "AA").start();
new Thread(() -> {
for (int j = 0; j < 5; j++) {
try {
shareData.decrement();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}, "BB").start();
}
}
2.Condition精準喚醒( condition.signal() )
/**
- 題目:多線程之間按順序調用,實現A->B->C三個線程啓動,需求如下:
- AA打印5次,BB打印10次,CC打印15次
- 緊接着
- AA打印5次,BB打印10次,CC打印15次
- 。。。
- 。。。
- 。。。
- 來10輪
*/
package JUC;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
/**
* 題目:多線程之間按順序調用,實現A->B->C三個線程啓動,需求如下:
* AA打印5次,BB打印10次,CC打印15次
* 緊接着
* AA打印5次,BB打印10次,CC打印15次
* 。。。
* 。。。
* 。。。
* 來10輪
*/
class ShareResource{//資源類
private int number = 1; //AA就是1,BB就是2,CC就是3
private Lock lock = new ReentrantLock();
private Condition c1 = lock.newCondition();
private Condition c2 = lock.newCondition();
private Condition c3 = lock.newCondition();
//1.判斷
public void print5(){
lock.lock();
try {
//1.判斷
while(number!=1){
c1.await();
}
//2.幹活
for (int i = 1; i <= 5; i++) {
System.out.println(Thread.currentThread().getName() + "\t" + i);
}
//3.通知
number = 2;
c2.signal();
}catch (Exception e){
e.printStackTrace();
}finally {
lock.unlock();
}
}
public void print10(){
lock.lock();
try {
//1.判斷
while(number!=2){
c2.await();
}
//2.幹活
for (int i = 1; i <= 10; i++) {
System.out.println(Thread.currentThread().getName() + "\t" + i);
}
//3.通知
number = 3;
c3.signal();
}catch (Exception e){
e.printStackTrace();
}finally {
lock.unlock();
}
}
public void print15(){
lock.lock();
try {
//1.判斷
while(number!=3){
c3.await();
}
//2.幹活
for (int i = 1; i <= 15; i++) {
System.out.println(Thread.currentThread().getName() + "\t" + i);
}
//3.通知
number = 1;
c1.signal();
}catch (Exception e){
e.printStackTrace();
}finally {
lock.unlock();
}
}
}
public class SyncAndReentrantLockDemo {
public static void main(String []args){
ShareResource shareResource = new ShareResource();
new Thread(()->{
for (int i = 1; i < 10; i++) {
shareResource.print5();
}
},"AA").start();
new Thread(()->{
for (int i = 1; i < 10; i++) {
shareResource.print10();
}
},"BB").start();
new Thread(()->{
for (int i = 1; i < 10; i++) {
shareResource.print15();
}
},"CC").start();
}
}
結果:只截取了一輪
AA 1
AA 2
AA 3
AA 4
AA 5
BB 1
BB 2
BB 3
BB 4
BB 5
BB 6
BB 7
BB 8
BB 9
BB 10
CC 1
CC 2
CC 3
CC 4
CC 5
CC 6
CC 7
CC 8
CC 9
CC 10
CC 11
CC 12
CC 13
CC 14
CC 15