Java-JUC(零):Java:现有线程T1/T2/T3,如何确保T1执行完成之后执行T2,T3在T2执行完成之后执行。
要实现多个线程执行完成先后,就要知道如何实现线程之间的等待,java线程等待实现是join。java的jdk中join方法实现如下:
1 public final synchronized void join(long millis) 2 throws InterruptedException { 3 long base = System.currentTimeMillis(); 4 long now = 0; 5 6 if (millis < 0) { 7 throw new IllegalArgumentException("timeout value is negative"); 8 } 9 10 if (millis == 0) { 11 while (isAlive()) { 12 wait(0); 13 } 14 } else { 15 while (isAlive()) { 16 long delay = millis - now; 17 if (delay <= 0) { 18 break; 19 } 20 wait(delay); 21 now = System.currentTimeMillis() - base; 22 } 23 } 24 }
实现需求的方案一:
1 public class TestMain { 2 public static void main(String[] args) throws InterruptedException { 3 Thread T1 = new MyThread("T1"); 4 Thread T2 = new MyThread("T2"); 5 Thread T3 = new MyThread("T3"); 6 7 System.out.println("T1 start."); 8 T1.start(); 9 T1.join(); 10 System.out.println("T1 complete."); 11 12 System.out.println("T2 start."); 13 T2.start(); 14 T2.join(); 15 System.out.println("T2 complete."); 16 17 System.out.println("T3 start."); 18 T3.start(); 19 T3.join(); 20 System.out.println("T3 complete."); 21 } 22 } 23 24 class MyThread extends Thread { 25 public MyThread(String name) { 26 setName(name); 27 } 28 29 @Override 30 public void run() { 31 for (int i = 0; i < 5; i++) { 32 System.out.println(Thread.currentThread().getName() + ": " + i); 33 try { 34 // do something... 35 Thread.sleep(100); 36 } catch (InterruptedException e) { 37 e.printStackTrace(); 38 } 39 } 40 } 41 }
实现需求的方案二:
public class Test2Main { public static void main(String[] args) { final Thread T1 = new Thread(new Runnable() { @Override public void run() { System.out.println("T1..."); } }); final Thread T2 = new Thread(new Runnable() { @Override public void run() { try { T1.join(); }catch (InterruptedException ex){ ex.printStackTrace(); } System.out.println("T2..."); } }); final Thread T3 = new Thread(new Runnable() { @Override public void run() { try { T2.join(); }catch (InterruptedException ex){ ex.printStackTrace(); } System.out.println("T3..."); } }); T3.start(); T2.start(); T1.start(); } }
实现方案三:使用ReentrantLock来解决, 还有个state整数用来判断轮到谁执行了
import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; public class ABC { private static Lock lock = new ReentrantLock();//通过JDK5中的锁来保证线程的访问的互斥 private static int state = 0; static class ThreadA extends Thread { @Override public void run() { for (int i = 0; i < 10;) { lock.lock(); if (state % 3 == 0) { System.out.print("A"); state++; i++; } lock.unlock(); } } } static class ThreadB extends Thread { @Override public void run() { for (int i = 0; i < 10;) { lock.lock(); if (state % 3 == 1) { System.out.print("B"); state++; i++; } lock.unlock(); } } } static class ThreadC extends Thread { @Override public void run() { for (int i = 0; i < 10;) { lock.lock(); if (state % 3 == 2) { System.out.print("C"); state++; i++; } lock.unlock(); } } } public static void main(String[] args) { new ThreadA().start(); new ThreadB().start(); new ThreadC().start(); } }
使用lock来保证只有一个线程在输出操作, 要保证了state不会被两个线程同时修改, 思路简单
实现方案四:还可以使用condition, condition的效率可能会更高一些, await会释放lock锁, condition的await和signal与object的wait和notify方法作用类似
import java.util.concurrent.locks.Condition; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; public class ABC2 { private static Lock lock = new ReentrantLock(); private static int count = 0; private static Condition A = lock.newCondition(); private static Condition B = lock.newCondition(); private static Condition C = lock.newCondition(); static class ThreadA extends Thread { @Override public void run() { lock.lock(); try { for (int i = 0; i < 10; i++) { while (count % 3 != 0) A.await(); // 会释放lock锁 System.out.print("A"); count++; B.signal(); // 唤醒相应线程 } } catch (InterruptedException e) { e.printStackTrace(); } finally { lock.unlock(); } } } static class ThreadB extends Thread { @Override public void run() { lock.lock(); try { for (int i = 0; i < 10; i++) { while (count % 3 != 1) B.await(); System.out.print("B"); count++; C.signal(); } } catch (InterruptedException e) { e.printStackTrace(); } finally { lock.unlock(); } } } static class ThreadC extends Thread { @Override public void run() { lock.lock(); try { for (int i = 0; i < 10; i++) { while (count % 3 != 2) C.await(); System.out.println("C"); count++; A.signal(); } } catch (InterruptedException e) { e.printStackTrace(); } finally { lock.unlock(); } } } public static void main(String[] args) throws InterruptedException { new ThreadA().start(); new ThreadB().start(); ThreadC threadC = new ThreadC(); threadC.start(); threadC.join(); System.out.println(count); } }
实现方案五:使用信号量也可以, 这个思路最简单, 整个代码也比较简洁
import java.util.concurrent.Semaphore; public class ABC3 { private static Semaphore A = new Semaphore(1); private static Semaphore B = new Semaphore(1); private static Semaphore C = new Semaphore(1); static class ThreadA extends Thread { @Override public void run() { try { for (int i = 0; i < 10; i++) { A.acquire(); System.out.print("A"); B.release(); } } catch (InterruptedException e) { e.printStackTrace(); } } } static class ThreadB extends Thread { @Override public void run() { try { for (int i = 0; i < 10; i++) { B.acquire(); System.out.print("B"); C.release(); } } catch (InterruptedException e) { e.printStackTrace(); } } } static class ThreadC extends Thread { @Override public void run() { try { for (int i = 0; i < 10; i++) { C.acquire(); System.out.println("C"); A.release(); } } catch (InterruptedException e) { e.printStackTrace(); } } } public static void main(String[] args) throws InterruptedException { B.acquire(); C.acquire(); // 开始只有A可以获取, BC都不可以获取, 保证了A最先执行 new ThreadA().start(); new ThreadB().start(); new ThreadC().start(); } }
注意:
lock是需要lock所有者去释放的, 即谁lock, 谁释放, 不可以跨线程, 会报java.lang.IllegalMonitorStateException;
semaphore是没有所有者的说法, 可以跨线程释放和获取.
方案三、四、五转自《[Java多线程]ABC三个线程顺序输出的问题》
基础才是编程人员应该深入研究的问题,比如:
1)List/Set/Map内部组成原理|区别
2)mysql索引存储结构&如何调优/b-tree特点、计算复杂度及影响复杂度的因素。。。
3)JVM运行组成与原理及调优
4)Java类加载器运行原理
5)Java中GC过程原理|使用的回收算法原理
6)Redis中hash一致性实现及与hash其他区别
7)Java多线程、线程池开发、管理Lock与Synchroined区别
8)Spring IOC/AOP 原理;加载过程的。。。
【+加关注】。