實現多個執行緒依次執行
阿新 • • 發佈:2019-01-30
概述
執行緒間的同步是開發中可能遇到的,有時候我們需要控制它們的執行順序來滿足我們的需求,今天總結一下幾種實現執行緒同步的方式。
方式一(Thread的join方法)
/** * 實現執行緒順序執行方式1: * 使用Thread物件的join方法,該方法的意思是當呼叫join方法的執行緒 * 執行完畢後,當前執行緒才執行 * 該方式只能執行一輪列印,不能實現類似ABCABC的多輪列印, */ public class TestThread { public static void main(String[] args) { final Alternate alternate = new Alternate(); final Thread a = new Thread(new Runnable() { @Override public void run() { alternate.printA(); } },"A"); a.start(); final Thread b = new Thread(new Runnable() { @Override public void run() { try { a.join(); alternate.printB(); } catch (InterruptedException e) { e.printStackTrace(); } } },"B"); b.start(); Thread c = new Thread(new Runnable() { @Override public void run() { try { b.join(); alternate.printC(); } catch (InterruptedException e) { e.printStackTrace(); } } },"C"); c.start(); } static class Alternate{ public void printA(){ for (int i = 1; i <= 3 ; i++) { System.out.println(Thread.currentThread().getName() + i); } } public void printB(){ for (int i = 1; i <= 3 ; i++) { System.out.println(Thread.currentThread().getName() + i); } } public void printC(){ for (int i = 1; i <= 3 ; i++) { System.out.println(Thread.currentThread().getName() + i); } } } }
方式二(等待喚醒機制Lock)
/** * 實現執行緒順序執行方式2: * 使用同步鎖的等待喚醒機制 * 可以實現多輪列印 */ public class TestThread2 { public static void main(String[] args) { final Alternate alternate = new Alternate(); new Thread(new Runnable() { @Override public void run() { for (int i = 1; i <= 2; i++) { alternate.printA(); } } }, "A").start(); new Thread(new Runnable() { @Override public void run() { for (int i = 1; i <= 2; i++) { alternate.printB(); } } }, "B").start(); new Thread(new Runnable() { @Override public void run() { for (int i = 1; i <= 2; i++) { alternate.printC(); } } }, "C").start(); } static class Alternate { private int num = 1; private Lock lock = new ReentrantLock(); Condition condition1 = lock.newCondition(); Condition condition2 = lock.newCondition(); Condition condition3 = lock.newCondition(); public void printA() { lock.lock(); try { if (num != 1) { condition1.await(); } for (int i = 1; i <= 3; i++) { System.out.println(Thread.currentThread().getName() + i); } num = 2; condition2.signal(); } catch (InterruptedException e) { e.printStackTrace(); } finally { lock.unlock(); } } public void printB() { lock.lock(); try { if (num != 2) { condition2.await(); } for (int i = 1; i <= 3; i++) { System.out.println(Thread.currentThread().getName() + i); } num = 3; condition3.signal(); } catch (InterruptedException e) { e.printStackTrace(); } finally { lock.unlock(); } } public void printC() { lock.lock(); try { if (num != 3) { condition3.await(); } for (int i = 1; i <= 3; i++) { System.out.println(Thread.currentThread().getName() + i); } num = 3; condition1.signal(); } catch (InterruptedException e) { e.printStackTrace(); } finally { lock.unlock(); } } } }
方式三(等待喚醒機制synchronized)
/** * 實現執行緒順序執行方式3: * 同步方法結合wait和notify * 可以實現多輪列印 */ public class TestThread3 { public static void main(String[] args) { final Alternate alternate = new Alternate(); new Thread(new Runnable() { @Override public void run() { for (int i = 1; i <= 2; i++) { alternate.printA(); } } }, "A").start(); new Thread(new Runnable() { @Override public void run() { for (int i = 1; i <= 2; i++) { alternate.printB(); } } }, "B").start(); new Thread(new Runnable() { @Override public void run() { for (int i = 1; i <= 2; i++) { alternate.printC(); } } }, "C").start(); } static class Alternate { private int num = 1; public synchronized void printA() { try { if (num != 1) { wait(); } for (int i = 1; i <= 3; i++) { System.out.println(Thread.currentThread().getName() + i); } num = 2; notifyAll(); } catch (InterruptedException e) { e.printStackTrace(); } } public synchronized void printB() { try { if (num != 2) { wait(); } for (int i = 1; i <= 3; i++) { System.out.println(Thread.currentThread().getName() + i); } num = 3; notifyAll(); } catch (InterruptedException e) { e.printStackTrace(); } } public synchronized void printC() { try { if (num != 3) { wait(); } for (int i = 1; i <= 3; i++) { System.out.println(Thread.currentThread().getName() + i); } num = 3; notifyAll(); } catch (InterruptedException e) { e.printStackTrace(); } } } }
方式四(CountDownLatch)
/**
*CountDownLatch:在完成一組正在其他執行緒中執行的操作之前,
* 它允許一個或多個執行緒一直等待。
*重點方法:
* await():當CountDownLatch的計數為0之前,await所在的執行緒才是掛起的
*/
public class TestThread4 {
//保證A和B之前的順序
static CountDownLatch latch = new CountDownLatch(1);
//保證B和C之間的關係
static CountDownLatch latch2 = new CountDownLatch(1);
public static void main(String[] args) {
final Alternate alternate = new Alternate();
new Thread(new Runnable() {
@Override
public void run() {
for (int i = 0; i < 2; i++) {
alternate.printA();
}
}
}, "A").start();
new Thread(new Runnable() {
@Override
public void run() {
for (int i = 0; i < 2; i++) {
alternate.printB();
}
}
}, "B").start();
new Thread(new Runnable() {
@Override
public void run() {
for (int i = 0; i < 2; i++) {
alternate.printC();
}
}
}, "C").start();
}
static class Alternate {
public void printA() {
for (int i = 1; i <= 3; i++) {
System.out.println(Thread.currentThread().getName() + i);
}
latch.countDown();
}
public void printB() {
try {
latch.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
for (int i = 1; i <= 3; i++) {
System.out.println(Thread.currentThread().getName() + i);
}
latch2.countDown();
}
public void printC() {
try {
latch2.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
for (int i = 1; i <= 3; i++) {
System.out.println(Thread.currentThread().getName() + i);
}
}
}
}
引出問題
在第三種方式中,我們可以看到wait方法是放在同步方法裡的,那麼為什麼要放在同步方法裡,加入我們的方法不是同步的,會丟擲下面的異常
Exception in thread "B" java.lang.IllegalMonitorStateException
at java.lang.Object.notifyAll(Native Method)
at com.example.client.TestThread3$Alternate.printA(TestThread3.java:48)
at com.example.client.TestThread3$1.run(TestThread3.java:15)
at java.lang.Thread.run(Thread.java:748)