本文共总结了5种方式,通过代码举例的方式进行展示。5种方式为:synchronized加wait/notify方式、ReentrantLock加Condition方式、闭锁的方式、栅栏的方式、信号量的方式。
最后还介绍了join和yield的使用。
1、synchronized加wait/notify方式
/** * wait和notify的使用 * wait和notify必须应用在synchronized块或方法内 * 下面的代码向跳交谊舞一样互相控制着对方的输出 */ public class MutiThread_WaitNotify { public static void main(String[] args) { final Object lock = new Object(); Thread a = new Thread(new Runnable(){ @Override public void run(){ synchronized (lock){ try{ lock.wait(); System.out.println("A-1"); lock.notify(); lock.wait(); System.out.println("A-2"); lock.notify(); lock.wait(); System.out.println("A-3"); lock.notify(); }catch(InterruptedException e){ e.printStackTrace(); } } } }); Thread b = new Thread(new Runnable(){ @Override public void run(){ synchronized (lock){ try{ System.out.println("B-1"); lock.notify(); lock.wait(); System.out.println("B-2"); lock.notify(); lock.wait(); System.out.println("B-3"); lock.notify(); lock.wait(); System.out.println("B-4"); }catch(InterruptedException e){ e.printStackTrace();; } } } }); a.start(); b.start(); } }
2、ReentrantLock加Condition方式
/** * ReentrantLock和Condition的使用 * 在使用Conditioin的await和signal时,必须将这两个方法写在ReentrantLock的lock方法之后 */ public class MutiThread_ReentrantLock_Condition { public static void main(String[] args) { ReentrantLock lock = new ReentrantLock(); Condition condition = lock.newCondition(); int i=1; for(; i<=6; i++){ final int k = i; Thread t1 = new Thread(new Runnable() { @Override public void run() { try{ lock.lock(); System.out.println("ThreadNo:A" + k + " is locked"); // 通过condition.await将线程阻塞 condition.await(); }catch(InterruptedException e){ e.printStackTrace(); }finally{ lock.unlock(); System.out.println("ThreadNo:A"+k + " is unlocked"); } } }); Thread t2 = new Thread(new Runnable() { @Override public void run() { if(k == 6){ try{ lock.lock(); System.out.println("All Threads is signaled"); // 通过condition.signalAll唤醒所有线程 condition.signalAll(); }catch(Exception e){ e.printStackTrace(); }finally{ lock.unlock(); } }else{ System.out.println("threads can't signaled, wait a moment."); } } }); t1.start(); try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } t2.start(); } } }
3、闭锁方式
import java.util.concurrent.CountDownLatch; /** * 闭锁的使用 * 闭锁用于等待事件,当闭锁到达结束状态(本例中是闭锁的计数器值减为0)之前,所有线程都等待,当闭锁到达结束状态时,所有线程都通过 * 闭锁是一次性的,当闭锁到达结束状态后,将不会被重置,这个锁会永远打开并允许所有线程通过。 * 可以将代码中的NUM变量值变为2和4,分别试试什么效果 */ public class MutiThread_CountDownLatch { public static void main(String[] args) { // 定义闭锁,并设置闭锁的计数器值为3 CountDownLatch lock = new CountDownLatch(3); // 循环定义3个线程 int NUM = 3; for(int i=1; i<=NUM; i++){ final int k = i; Thread a = new Thread(new Runnable(){ @Override public void run(){ try{ Thread.sleep(k * 1000); System.out.println("ThreadNo:A"+k); // 每个线程在休眠指定时间后将闭锁的计数器值减1,当闭锁的计数器值减到0时,闭所将被打开,从而使第二个循环中的所有线程才能通过 lock.countDown(); // 打印闭锁计数器的值 System.out.println("ThreadNo:A"+k+"; getCount:"+lock.getCount()); }catch(InterruptedException e){ e.printStackTrace(); } } }); a.start(); } // 循环定义2个线程 for(int i=1; i<=2; i++){ final int k = i; Thread b = new Thread(new Runnable(){ @Override public void run(){ try{ System.out.println("ThreadNo:B"+k+" is waiting..."); // 当闭锁的计数器值不为0时,线程将在此处被中断 lock.await(); // 当闭锁的计数器值等于0时,闭锁将被打开,所有等待的线程都将被唤醒 System.out.println("ThreadNo:B"+k+" is notify"); }catch(InterruptedException e){ e.printStackTrace(); } } }); b.start(); } } }
4、栅栏的方式
import java.util.concurrent.BrokenBarrierException; import java.util.concurrent.CyclicBarrier; /** * 栅栏的使用 * 栅栏用于等待线程,所有线程必须同时到达栅栏,才能继续执行 * 栅栏不是一次性的,可以被重置。 * 可以将代码中的NUM变量值变为5和7,分别试试什么效果 */ public class MutiThread_CyclicBarrier { public static void main(String[] args) { // 定义栅栏,并设置栅栏需要等待的线程数为6 CyclicBarrier barrier = new CyclicBarrier(6); int NUM = 100; for(int i=1; i<=NUM; i++){ final int k = i; Thread t = new Thread(new Runnable() { @Override public void run() { try{ Thread.sleep(k * 1000); System.out.println("ThreadNo:"+k+" is waiting, getNumberWaiting:" + barrier.getNumberWaiting()); // 栅栏设置的等待线程数为6,当线程数不够6个时,所有线程将在此等待 barrier.await(); // 当线程数达到6个时,栅栏将被打开,所有线程都将被唤醒 System.out.println("ThreadNo:"+k+" is notify"); // 栅栏被重置,以便下次继续使用 barrier.reset(); }catch(InterruptedException e){ e.printStackTrace(); } catch (BrokenBarrierException e) { e.printStackTrace(); } } }); t.start(); } } }
5、信号量的方式
import java.util.ArrayList; import java.util.List; import java.util.concurrent.Semaphore; /** * 信号量的使用 * 信号量用于控制同时访问某个资源的线程数量,信号量还可以用于实现某个资源池。 * 信号量管理者一组虚拟的许可,线程在执行操作时首先要获得许可,如果信号量的许可数量为0,那么accquire将阻塞直到有许可为止 * 信号量不是一次性的,当信号链的许可用完之后,可以通过release释放许可 */ public class MutiThread_Semaphore { public static void main(String[] args) { // 定义信号量,并设置信号量的允许发放的最大许可数量为6 final Semaphore semaphore = new Semaphore(6); // 定义集合,当信号量未发放的许可数量大于0则允许线程向集合内添加元素 final List<String> set = new ArrayList<>(); int i = 1; while(true){ final int k = i++; Thread t = new Thread(new Runnable() { @Override public void run() { boolean res = false; try{ System.out.println("ThreadNo:A"+k+", availablePermits:"+semaphore.availablePermits()); // 当信号量允许发放的许可数量大于0,则会向集合内添加元素,否则将被中断于此 semaphore.acquire(); res = set.add("1"); System.out.println("ThreadNo:A"+k+" add item success"); }catch(InterruptedException e){ e.printStackTrace(); }finally{ if(!res){ semaphore.release(); } } } }); Thread t2 = new Thread(new Runnable() { @Override public void run() { if(semaphore.availablePermits() == 0){ // 如果信号量允许发放的许可数量等于0,则释放制定数量的许可 semaphore.release(3); //释放3个许可 System.out.println("ThreadNo:B"+k+" releasePermitNum:"+semaphore.availablePermits()); } } }); t.start(); t2.start(); System.out.println("the num of set:"+set.size()); try { Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); } } } }
A、join的使用
/** * join的使用 * 实现当调用join的线程执行完毕后,其他线程才能执行 */ public class MutiThread_Join { public static void main(String[] args) { Thread a = new Thread(new Runnable(){ @Override public void run(){ printNumber("A"); } }); Thread b = new Thread(new Runnable(){ @Override public void run(){ printNumber("B"); } }); try{ a.start(); // a线程执行完毕后,b线程才能执行 a.join(); b.start(); }catch(InterruptedException e){ e.printStackTrace();; } } public static void printNumber(String s){ System.out.println(s+" print:"+s); } }
B、yield的使用
/** * yield,当一个线程中调用了这个方法后,这个线程就会把自己的CPU执行时间让给自己或其它线程, * 注意是让给自己或其它线程,并不是单纯让给其他线程。yield执行后,能让当前线程由运行状态 * 进入到就绪状态,将自己的CPU时间片让出来,让出来之后有可能是其它线程执行,也有可能是该线程 * 继续执行。优先级高的线程并不一定是首先执行,而是首先执行的概率会高一些。优先级在大量线程 * 执行的时候才能体现的出来。 */ public class MutiThread_yield { public static void main(String[] args) { Thread t1 = new Thread(new Runnable() { @Override public void run() { for(int i=0;i<10;i++){ System.out.println("ThreadNo:A"+i); Thread.yield(); } } }); Thread t2 = new Thread(new Runnable() { @Override public void run() { for(int i=0;i<10;i++){ System.out.println("ThreadNo:B"+i); Thread.yield(); } } }); t1.setPriority(Thread.MIN_PRIORITY); t2.setPriority(Thread.MAX_PRIORITY); t1.start(); t2.start(); } }