多線程之間是搶佔資源的,使用線程通信可以達到線程按序執行的目的
線程共享資源類, 首先創建一個資源類, 包含三個打印的方法以及首次打印的字符串
多個線程訪問,方法加synchronized同步鎖
class Resource {
String currentPrint = "AA"; // 初始打印,打印之後賦值爲下一個要打印的內容
/**
* 打印AA
* @param next 下一個要打印的字符串
* @throws InterruptedException
*/
public synchronized void printAA(String next) throws InterruptedException {
// 不是要打印的內容, 阻塞線程
while (!Objects.equals(currentPrint, "AA")) {
wait();
}
for (int i = 0; i < 3; i++) {
System.out.println(Thread.currentThread().getName() + ": AA");
}
currentPrint= next;
// 喚醒其他線程
notifyAll();
}
/**
* 打印BB
* @param next 下一個要打印的字符串
* @throws InterruptedException
*/
public synchronized void printBB(String next) throws InterruptedException {
// 不是要打印的內容, 阻塞線程
while (!Objects.equals(currentPrint, "BB")) {
wait();
}
for (int i = 0; i < 3; i++) {
System.out.println(Thread.currentThread().getName() + ": BB");
}
currentPrint= next;
// 喚醒其他線程
notifyAll();
}
/**
* 打印CC
* @throws InterruptedException
*/
public synchronized void printCC() throws InterruptedException {
// 不是要打印的內容, 阻塞線程
while (!Objects.equals(currentPrint, "CC")) {
wait();
}
for (int i = 0; i < 3; i++) {
System.out.println(Thread.currentThread().getName() + ": CC");
}
}
}
創建線程執行程序進行交替打印
public class AABBCC {
public static void main(String[] args) {
ThreadPoolExecutor threadPoolExecutor = null;
try {
// 創建一個最大長度爲3的線程池
threadPoolExecutor = new ThreadPoolExecutor(3, 3, 0,
TimeUnit.SECONDS, new LinkedBlockingDeque<Runnable>(10), Executors.defaultThreadFactory(), new ThreadPoolExecutor.AbortPolicy());
Resource resource = new Resource();
// 將要執行的任務插入到集合中
List<Runnable> list = new ArrayList<>();
list.add(()->{try { resource.printAA("BB"); } catch (InterruptedException e) { e.printStackTrace(); }});
list.add(()->{try { resource.printBB("CC"); } catch (InterruptedException e) { e.printStackTrace(); }});
list.add(()->{try { resource.printCC();} catch (InterruptedException e) {e.printStackTrace();}});
for (int i = 0; i < list.size(); i++) {
threadPoolExecutor.execute(list.get(i));
}
} catch (Exception e) {
e.printStackTrace();
} finally {
// 關閉線程池
if (threadPoolExecutor != null) {
threadPoolExecutor.shutdown();
}
}
}
}
執行結果
資源類冗餘代碼進行優化
/**
* 打印字符串
* @param currentPrintParam 當前
* @param next 下一個
* @param times 打印次數
* @throws InterruptedException
*/
public synchronized void printStr(String currentPrintParam,String next, int times) throws InterruptedException {
// 不是要打印的內容, 阻塞線程
while (!Objects.equals(currentPrint, currentPrintParam)) {
wait();
}
for (int i = 0; i < times; i++) {
System.out.println(Thread.currentThread().getName() + ": "+ currentPrint);
}
currentPrint = next;
// 喚醒其他線程
notifyAll();
}
調用方法替換
Resource resource = new Resource();
// 將要執行的任務插入到集合中
List<Runnable> list = new ArrayList<>();
list.add(()->{try { resource.printStr("AA", "BB", 3); } catch (InterruptedException e) { e.printStackTrace(); }});
list.add(()->{try { resource.printStr("BB", "CC", 3); } catch (InterruptedException e) { e.printStackTrace(); }});
list.add(()->{try { resource.printStr("CC", "", 3);} catch (InterruptedException e) {e.printStackTrace();}});
for (int i = 0; i < list.size(); i++) {
threadPoolExecutor.execute(list.get(i));
}
執行結果
JDK5在java.util.concurrent包下提供了Lock併發鎖
資源類代碼新增
Lock lock = new ReentrantLock(); // 併發鎖
Condition aCondition = lock.newCondition();
Condition bCondition = lock.newCondition();
Condition cCondition = lock.newCondition();
/**
* 打印字符串
* @param currentPrintParam 當前線程打印
* @param nextPrint 下一個線程打印
* @param currentCondition 當前線程
* @param nextCondition 下一個線程
* @param times 打印次數
* @throws InterruptedException
*/
public void lockPrintStr(String currentPrintParam, String nextPrint, Condition currentCondition, Condition nextCondition, int times) {
lock.lock();
try {
// 不是要打印的內容 阻塞線程
while (!Objects.equals(currentPrint, currentPrintParam)) {
currentCondition.await();
}
for (int i = 0; i < times; i++) {
System.out.println(Thread.currentThread().getName() + ": "+ currentPrint);
}
currentPrint = nextPrint;
// 喚醒下一個打印條件的線程
if (!Objects.isNull(nextCondition)) {
nextCondition.signal();
}
} catch (Exception e) {
e.printStackTrace();
} finally {
// 釋放鎖
if (lock != null) {
lock.unlock();
}
}
}
調用新增的方法
Resource resource = new Resource();
// 將要執行的任務插入到集合中
List<Runnable> list = new ArrayList<>();
list.add(()->{resource.lockPrintStr("AA", "BB", resource.aCondition, resource.bCondition, 3);});
list.add(()->{resource.lockPrintStr("BB", "CC", resource.bCondition, resource.cCondition, 3);});
list.add(()->{resource.lockPrintStr("CC", "", resource.cCondition, null, 3);});
for (int i = 0; i < list.size(); i++) {
threadPoolExecutor.execute(list.get(i));
}
執行結果
多次執行的結果中可以看出,由三個線程交替打印AABBCC
總結:
- 線程通信其中兩種方式 Object類下wait()、notify()/notifyAll(),以及JDK5以後提供的Lock下Condition內部類的await()、signal()方法。
- 使用wait/notifyAll進行線程通信喚醒喚醒了所有線程,增加了上下文的切換時間,使用await/signal可以實現精準喚醒,java集合框架中的隊列就採用了後者實現線程通信