本文共 10406 字,大约阅读时间需要 34 分钟。
一个场景,两个线程数数,同时启动两个线程,线程A数1、2、3,然后线程B数4、5、6,最后线程A数7、8、9,程序结束,这涉及到线程之间的通信。
public class ConditionTest { static class NumberWrapper { public int value = 1; } public static void main(String[] args) { //初始化可重入锁 final Lock lock = new ReentrantLock(); //第一个条件当屏幕上输出到3 final Condition reachThreeCondition = lock.newCondition(); //第二个条件当屏幕上输出到6 final Condition reachSixCondition = lock.newCondition(); //NumberWrapper只是为了封装一个数字,一边可以将数字对象共享,并可以设置为final //注意这里不要用Integer, Integer 是不可变对象 final NumberWrapper num = new NumberWrapper(); //初始化A线程 Thread threadA = new Thread(new Runnable() { @Override public void run() { //需要先获得锁 lock.lock(); System.out.println("ThreadA获得lock"); try { System.out.println("threadA start write"); //A线程先输出前3个数 while (num.value <= 3) { System.out.println(num.value); num.value++; } //输出到3时要signal,告诉B线程可以开始了 reachThreeCondition.signal(); } finally { lock.unlock(); System.out.println("ThreadA释放lock"); } lock.lock(); try { //等待输出6的条件 System.out.println("ThreadA获得lock"); reachSixCondition.await(); System.out.println("threadA start write"); //输出剩余数字 while (num.value <= 9) { System.out.println(num.value); num.value++; } } catch (InterruptedException e) { e.printStackTrace(); } finally { lock.unlock(); System.out.println("ThreadA释放lock"); } } }); Thread threadB = new Thread(new Runnable() { @Override public void run() { try { lock.lock(); System.out.println("ThreadB获得lock"); Thread.sleep(5000);//是await方法释放了锁 while (num.value <= 3) { //等待3输出完毕的信号 reachThreeCondition.await(); } } catch (InterruptedException e) { e.printStackTrace(); } finally { lock.unlock(); System.out.println("ThreadB释放lock"); } try { lock.lock(); System.out.println("ThreadB获得lock"); //已经收到信号,开始输出4,5,6 System.out.println("threadB start write"); while (num.value <= 6) { System.out.println(num.value); num.value++; } //4,5,6输出完毕,告诉A线程6输出完了 reachSixCondition.signal(); } finally { lock.unlock(); System.out.println("ThreadB释放lock"); } } }); //启动两个线程 threadB.start(); threadA.start(); }}
创建方式:通过Lock创建,Lock.newCondition();
常用方法:
总结:Condition必须与Lock一起使用(wait()、notify()必须与synchronized一起使用,否则运行会报错java.lang.IllegalMonitorStateException),相比于wait与notify更加的灵活,可以设置各种情形,如上例中的到达3和到达6两个条件。
执行结果:
public class CountDownLatchTest { public static void main(String[] args) { final CountDownLatch c = new CountDownLatch(3);//总数3 Thread t1 = new Thread(new Runnable(){ @Override public void run() { try { System.out.println("开始等"); c.await();//阻塞,等待countDown,当countDown到0就执行后面的完事了 System.out.println("完事"); } catch (InterruptedException e) { e.printStackTrace(); } } }); Thread t2 = new Thread(new Runnable(){ @Override public void run() { for(int i=3;i>0;i--){ c.countDown();//减1 System.out.println(i); } } }); t1.start(); t2.start(); }}
创建方式:直接创建,new CountDownLatch(int num);
常用方法:
总结:适用于一个线程等待其他线程的情景。
执行结果:
开始等321完事
与CountDownLatch有什么区别?
另外:
public class MainMission { private CyclicBarrier barrier; private final static int threadCounts = 5; public void runMission() { ExecutorService exec=Executors.newFixedThreadPool(threadCounts); //new 的时候要传入数字,我发现,这个类似semaphore,如果位置不足,线程会抢位置。数字要是threadCounts+1为主线程留一个位子,但实际测试中发现,只要等于threadCount就可以 barrier=new CyclicBarrier(threadCounts+1); for(int i=0;i<5;i++){ exec.execute(new Mission(barrier)); } try { barrier.await(); } catch (InterruptedException e) { e.printStackTrace(); } catch (BrokenBarrierException e) { e.printStackTrace(); } try { Thread.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("所有任务都执行完了"); exec.shutdown();//如果不关闭,程序一直处于运行状态 } public static void main(String[] args) { MainMission m = new MainMission(); m.runMission(); }}class Mission implements Runnable{ private CyclicBarrier barrier; public Mission(CyclicBarrier barrier){ this.barrier = barrier; } @Override public void run() { System.out.println(Thread.currentThread().getName()+"开始执行任务"); try { int sleepSecond = new Random().nextInt(10)*1000; System.out.println(Thread.currentThread().getName()+"要执行"+sleepSecond+"秒任务"); Thread.sleep(sleepSecond); } catch (InterruptedException e) { e.printStackTrace(); } try { barrier.await(); } catch (InterruptedException e) { e.printStackTrace(); } catch (BrokenBarrierException e) { e.printStackTrace(); } System.out.println(System.currentTimeMillis()); System.out.println(Thread.currentThread().getName()+"执行完毕"); }}
创建方式:直接创建,new CyclicBarrier(int num);
常用方法:
总结:想想一下百米跑,所有运动员都就位之后才会发令起跑,线程调用await意味着说,我准备好了。
执行结果:
pool-1-thread-1开始执行任务pool-1-thread-3开始执行任务pool-1-thread-2开始执行任务pool-1-thread-3要执行0秒任务pool-1-thread-1要执行4000秒任务pool-1-thread-2要执行7000秒任务pool-1-thread-4开始执行任务pool-1-thread-5开始执行任务pool-1-thread-4要执行4000秒任务pool-1-thread-5要执行6000秒任务16087134287061608713428706pool-1-thread-4执行完毕1608713428706pool-1-thread-5执行完毕1608713428706pool-1-thread-1执行完毕1608713428706pool-1-thread-3执行完毕pool-1-thread-2执行完毕所有任务都执行完了
下面是一个上厕所的例子,厕所位置有限,想用得排队了。实现使用的就是信号量,可以看出信号量可以用来做限流。
public class MySemaphore implements Runnable{ Semaphore position; private int id; public MySemaphore(int i,Semaphore s){ this.id=i; this.position=s; } @Override public void run() { try{ if(position.availablePermits()>0){ System.out.println("顾客["+this.id+"]进入厕所,有空位"); } else{ System.out.println("顾客["+this.id+"]进入厕所,没空位,排队"); } position.acquire();//只有在acquire之后才能真正的获得了position System.out.println("#########顾客["+this.id+"]获得坑位"); Thread.sleep((int)(Math.random()*100000)); System.out.println("@@@@@@@@@顾客["+this.id+"]使用完毕"); position.release(); }catch(Exception e){ e.printStackTrace(); } } public static void main(String args[]){ ExecutorService list=Executors.newCachedThreadPool(); Semaphore position=new Semaphore(2); for(int i=0;i<10;i++){ list.submit(new MySemaphore(i+1,position)); } list.shutdown(); position.acquireUninterruptibly(2); System.out.println("使用完毕,需要清扫了"); position.release(2); }}
创建方式:直接创建,new Semaphore(int num);
常用方法:
总结:抢位置。
执行结果:
顾客[1]进入厕所,有空位顾客[3]进入厕所,有空位#########顾客[1]获得坑位顾客[2]进入厕所,有空位顾客[4]进入厕所,没空位,排队#########顾客[3]获得坑位顾客[5]进入厕所,没空位,排队顾客[6]进入厕所,没空位,排队顾客[7]进入厕所,没空位,排队顾客[8]进入厕所,没空位,排队顾客[9]进入厕所,没空位,排队顾客[10]进入厕所,没空位,排队@@@@@@@@@顾客[3]使用完毕#########顾客[2]获得坑位@@@@@@@@@顾客[1]使用完毕#########顾客[4]获得坑位@@@@@@@@@顾客[2]使用完毕#########顾客[5]获得坑位@@@@@@@@@顾客[5]使用完毕#########顾客[6]获得坑位@@@@@@@@@顾客[4]使用完毕#########顾客[7]获得坑位@@@@@@@@@顾客[6]使用完毕#########顾客[8]获得坑位@@@@@@@@@顾客[7]使用完毕
创建方式:
非公平锁:当锁处于无线程占有的状态,此时其他线程和在队列中等待的线程都可以抢占该锁。
公平锁:当锁处于无线程占有的状态,在其他线程抢占该锁的时候,都需要先进入队列中等待。tryLock()方法:尝试去获取锁,如果没有获取到直接返回,不等待。
细节看这个吧,
创建方式:new ReentrantReadWriteLock();
常用方法:
总结:
Callable接口支持返回执行结果,此时需要调用FutureTask.get()方法实现,此方法会阻塞主线程直到获取结果;当不调用此方法时,主线程不会阻塞!
与Runnable对比:
八,线程池
提供的线程池有几种://有数量限制的线程池ExecutorService service=Executors.newFixedThreadPool(4);//没有数量限制的线程池ExecutorService service=Executors.newCachedThreadPool();//单线程池ExecutorService service=Executors.newSingleThreadExecutor();
他们都是通过下面这个线程池实现的
有数量线程池的实现方式
public static ExecutorService newFixedThreadPool(int nThreads) { return new ThreadPoolExecutor( nThreads/*核心线程数*/, nThreads/*最高线程数*/, 0L/*高出核心线程数的线程最高存活时间*/, TimeUnit.MILLISECONDS/*高出核心线程数的线程最高存活时间单位*/, new LinkedBlockingQueue()/*任务队列*/);}
转载地址:http://qecuz.baihongyu.com/