首页 > 编程 > Java > 正文

java中并发常用工具类

2019-11-09 16:29:14
字体:
来源:转载
供稿:网友

前言:在你无聊的时候,想想比你优秀还努力的人,也许就不觉的无聊了

今天下午没事干把买的java并发编程艺术这本书拿出来看了看,看了下也记不住,还是好记性不如烂笔头,今天讲四个并发中可能会用到的工具类,分别是:

CountDownLatch

CyclicBarrier

Semaphore

Exchanger

CountDownLatch

countDownLatch允许一个或多个线程等待其他线程完成操作.比如说有三个线程分别是老二,老大,老爸,这三个线程必须是老二吃好了,老大吃,老大吃完了,老爸吃,在20年钱,农村家里穷,一般有好吃的都是先留个最小的,然后才给其他兄弟姐妹吃,都不吃了,才由我们的父母吃,所以父母都不容易了,为了儿女,虽然是多个线程但是确实线性的,

我同事面试别人就问过好几次这个问题,在java或者android中常用的有2个方式实现

第一种方式:

使用jdk中Thread自带的函数join实现,join()用于当前执行线程等待join线程执行结束,其实实现原理是不停检查join线程是否存活,如果join线程存活则让当前线程永远等待,其中,wait(0)表示永远等待下去,join在jdk中的是实现方式如下:

/**     * Waits for this thread to die.     *     * <p> An invocation of this method behaves in exactly the same     * way as the invocation     *     * <blockquote>     * {@linkplain #join(long) join}{@code (0)}     * </blockquote>     *     * @throws  InterruptedException     *          if any thread has interrupted the current thread. The     *          <i>interrupted status</i> of the current thread is     *          cleared when this exception is thrown.     */    public final void join() throws InterruptedException {        join(0);    }直到join线程中止后,线程的this.notifyAll()方法会被调用,调用notifyAll()方法是在JVM里实现的,现在把上面的例子用代码实现下:
package com.thread;public class RunnableJob {   public static void main(String[] args) throws InterruptedException {	   Worker runnableJob = new Worker();       Thread t1 = new Thread(runnableJob, "老二");       Thread t2 = new Thread(runnableJob, "老大");       Thread t3 = new Thread(runnableJob, "老爸");       t1.start();         t1.join();         t2.start();         t2.join();         t3.start();         t3.join();         System.out.PRintln("主线程执行完毕----");    }}class Worker implements Runnable{	public void run() {        Thread thread = Thread.currentThread();        try {            Thread.sleep(5000);            System.out.println(thread.getName()+"吃完了");        } catch (InterruptedException e) {            e.printStackTrace();        }    }}log:

CountDownLatch是一个同步计数器,现在有个需求,在古代是男人先吃饭,特别是有客人在的时候,女人不能上桌,等待男人吃完,女人才能上桌吃饭,

package com.thread;import java.util.concurrent.CountDownLatch;public class CountDownLatchTest {	public static void main(String[] args) {		 final CountDownLatch countDownLatch = new CountDownLatch(3); 		 new Thread("老二"){             public void run() {                 try {                    Thread.sleep(3000);                    System.out.println(Thread.currentThread().getName()+"吃完了");                    countDownLatch.countDown();                } catch (InterruptedException e) {                    e.printStackTrace();                }             };         }.start();         new Thread("老大"){             public void run() {                 try {                    Thread.sleep(3000);                    System.out.println(Thread.currentThread().getName()+"吃完了");                    countDownLatch.countDown();                } catch (InterruptedException e) {                    e.printStackTrace();                }             };         }.start();         new Thread("老爸"){             public void run() {                 try {                    Thread.sleep(3000);                    System.out.println(Thread.currentThread().getName()+"吃完了");                    countDownLatch.countDown();                } catch (InterruptedException e) {                    e.printStackTrace();                }             };         }.start();         System.out.println("等待三个男人吃完,女人才能上桌吃饭,等....");         try {			countDownLatch.await();		} catch (InterruptedException e) {			e.printStackTrace();		}         System.out.println("女人们可以上桌吃饭了");	}}运行结果:

你会发现CountDownLatch和join还是有区别:

相同点:都能等待一个或者多个线程执行完成操作,比如等待三个线程执行完毕后,第四个线程才能执行

不同点:join能让线程按我们预想的的顺序执行,比如线程1执行完了,线程2才能执行,线程2执行完,线程3才能执行,但是CountDownLatch就做不到.如果查看CountDownLatch源码,发现这源码还是很少的,没几行代码,方法也就几个:CountDownLatch的构造函数接受一个int类型的参数作为计时器,如果你想等待N个点完成,就在这里传入N

当我们调用CountDownLatch的countDown方法时,N就会减1,CountDownLatch的await方法会阻塞当前线程,直到N变为零(也就是线程都执行完了),由于countDown方法可以用在任何地方,所以这里说的N个点,可以是N个线程,也可以是1个线程里的N个执行步骤。用在多线程时,只需把这个CountDownLatch的引用传递到线程中即可,

当然阻塞还有一个重载的方法就是await(long timeout, TimeUnit unit) 阻塞的时间,超过这个时间就不等待了.

注意点:

计时器必须大于等于0,只是等于0的时候,计时器就是0,调用await()方法时,不会阻塞当前线程,CountDownLatch不可能重新初始化或者修改CountDownLatch对象的内部 计数器的值,不可逆性.

Semaphore

semaphore(信号量)是用来控制同时访问特定资源的线程数量,它通过协调各个线程,以保证合理的使用公共资源.这个跟队列有点像,画图理解更直观,

代码:

package com.thread;import java.util.concurrent.ExecutorService;import java.util.concurrent.Executors;import java.util.concurrent.Semaphore;public class SemaphoreDemo {	public static void main(String[] args) {          ExecutorService service = Executors.newCachedThreadPool();          final Semaphore semaphore = new Semaphore(2);          final int count = 5;          for (int i = 0; i < count; i++) {              Runnable runnable = new Runnable() {                  public void run() {                      try {                          semaphore.acquire();                          System.out.println("线程:" + Thread.currentThread().getName()+"开始下载");                          Thread.sleep(5000);                      } catch (InterruptedException e) {                          e.printStackTrace();                      } finally {                          System.out.println("线程:" + Thread.currentThread().getName() + "下载完毕");                          semaphore.release();                      }                  }              };              service.execute(runnable);//放在线程池中去维护        }          service.shutdown();      }  }运行结果:

发现我们并发下载的数量是只允许2个线程,当访问总线程大于2时,其他线程就要等待,Semaphore类常用的方法:

acquire():获取一个许可证,

release():释放许可证,这时候有多余的线程就加入到线程池中

tryAcquire():尝试获取许可证

intavailblePermits():返回此信号量当前可用的许可证数

intgetQueueLength():返回正在等待获取许可证的线程数

hasQueuedThreads():是否有线程正在等待获取许可证

reducePermits(int reduction):减少reduction个许可证,

getQueueThreads():返回所有等待获取许可证的线程集合

Semaphore类的构造函数中传入的数,表示同时并发访问控制在多少个线程.

Exchanger

exchanger是一个用于线程间协作的工具类,Exchanger用于进行线程间的数据交换,它提供一个同步点,在这个同步点,二个线程可以交换彼此的数据.在这二个线程通过exchange方法交换数据,如果第一个线程先执行exchange()方法,它会一直等待第二个线程也执行exchange方法,当二个线程都达到同步点时,这二个线程就可以交换数据,将本线程生产出来的数据传递给对方.

打个比方吧,发现我们那农村结婚是需要30,40万,这对一般的家庭确实是一个很大的钱,因为农村没读书的孩子多是 21~23之间就要讨老婆了,这么年轻自己打工赚到30万还是很少很少的,哪女孩一般会说等你赚到了30万马上给你结婚,于是只好苦逼的年复一年的工作,每个月把打工的钱给女孩,到了30万马上通知男方说可以结婚了,

现在用代码实现下:

package com.thread;import java.util.Random;import java.util.concurrent.Exchanger;public class ExchangerDemo {	 public static void main(String[] args) {  	        Exchanger<Integer> exchanger = new Exchanger<>();  	        new Girl(exchanger).start();  	        new Man(exchanger).start();  	    }  }/** * 男人 * @author admin */class Man extends Thread {      Exchanger<Integer> exchanger = null;      public Man(Exchanger<Integer> exchanger) {          super();          this.exchanger = exchanger;      }      @Override      public void run() {          Random rand = new Random();        int money = 0;        for(int i=0;i<4;i++){        	money+=100000;//年薪在10万以内        	try {				exchanger.exchange(money);//存钱			} catch (InterruptedException e) {				e.printStackTrace();			}        }    }  }  /** * 女人 */class Girl extends Thread {      Exchanger<Integer> exchanger = null;      int money = 0;    public Girl(Exchanger<Integer> exchanger) {          super();          this.exchanger = exchanger;      }      @Override      public void run() {      	for(int i=0;i<4;i++){    		try {				money = exchanger.exchange(money) ;				System.out.println(money>300000?"亲爱的"+money+"万我们可以结婚了":money+"块这么少,"+"臭屌丝活该单身,还不去赚钱娶老婆");			} catch (InterruptedException e) {				e.printStackTrace();			}    	}    }  } 

运行结果:

Exchanger主要用于二个线程之间交换数据,注意,只能是2个线程,如果有一个线程没执行exchange()方法,则会一直等待,线程就处于阻塞状态了!如果怕一直等待,可以设置时间:exchange()有一个重载的方法.

exchange(V x, long timeout, TimeUnit unit)


发表评论 共有条评论
用户名: 密码:
验证码: 匿名发表