Thinking in java学习笔记20:第二十一章(并发-下)

继续上一章内容

7.新类库中的构件

书中讲的是java SE5版本中,引入java.util.concurrent包的一些新类,虽然对于现在而言,可能不新的,但还是学习一下。

7.1 CountDownLatch

CountDownLatch被用来同步一个或多个任务,强制它们等待其他任务执行的操作完成。

它的工作原理:通过一个计数器来实现,计数器的初始值是可自设(通常是线程的数量)。每当一个线程(任务)执行完毕后,计数器的值就-1,当计数器的值为0时,表示所有线程都执行完毕,然后在闭锁上等待的线程就可以恢复工作了。

public class ThreadMain {
    //创建计数器
    private static CountDownLatch latch=new CountDownLatch(3);
    public static void main(String[] args){
        ExecutorService exec= Executors.newCachedThreadPool();
        for(int i=0;i<5;i++) {
            exec.execute(new ThreadTest(latch,i));
        }
        exec.shutdown();
        try {
            latch.await();//主线程等待计数器归0
        } catch (InterruptedException e) {
            e.printStackTrace();
        }finally {
            System.out.println("沙丁鱼3个子线程执行完毕了,继续执行主线程");
        }
    }
}
public class ThreadTest implements Runnable {
    private CountDownLatch latch;
    private int time;

    public ThreadTest(CountDownLatch latch, int time) {
        this.latch = latch;
        this.time = time;
    }

    @Override
    public void run() {
        try {
            Thread.sleep(time*1000);
            System.out.println("沙丁鱼的子线程:"+Thread.currentThread().getName()+"执行");
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            latch.countDown();//执行完,CountDownLatch计数器减一
            Thread.yield();
        }
    }
}

主要方法有:

1.latch.await();等待计数器归0,再继续执行。

2.latch.countDown(); 计数器减1;

这里有6个线程,1个主线程,5个子线程。我这里特地设置了线程不同休眠时间来更明显观察,可以看出,计数器为3,那么当有3个子线程执行完毕后,继续主线程执行(不会等待剩下两个子线程)

 

7.2 CyclicBarrier

CyclicBarrier和CountDownLatch很像,他们都是希望等待其它任务完成,再继续执行的,不过不同的地方就在于CyclicBarrier可以多次使用,而CountDownLatch由于是计数器的工作原理,只能触发一次。

public class ThreadCyclicBarrierMain {
    //创建计数器
    private static CyclicBarrier barrier=new CyclicBarrier(3,new Runnable() {
        @Override
        public void run() {
            //这里是完成3个线程后执行的
            System.out.println("沙丁鱼3个子线程执行完毕了,继续执行主线程");
        }
    });
    public static void main(String[] args){
        ExecutorService exec= Executors.newCachedThreadPool();
        for(int i=0;i<5;i++) {
            exec.execute(new ThreadCyclicBarrierTest(barrier,i));
        }
        //System.out.println("沙丁鱼3个子线程执行完毕了,继续执行主线程");
        exec.shutdown();
    }
}
public class ThreadCyclicBarrierTest implements Runnable {
    private CyclicBarrier barrier;
    private int time;

    public ThreadCyclicBarrierTest(CyclicBarrier barrier, int time) {
        this.barrier = barrier;
        this.time = time;
    }

    @Override
    public void run() {
        try {
            Thread.sleep(time*1000);
            System.out.println("沙丁鱼的子线程:"+Thread.currentThread().getName()+"执行(这里叫到达栅栏)");
            barrier.await();//到达栅栏
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (BrokenBarrierException e) {
            e.printStackTrace();
        } finally {
            Thread.yield();
        }
    }
}

1.barrier.await();//到达栅栏

2.构造方法:public CyclicBarrier(int parties, Runnable barrierAction)   参数是parties(个数)和barrierAction(最后一个到达线程要做的任务)

 PS:这边使用CyclicBarrier有两个抛出要写。BrokenBarrierException 异常意味着冲破栅栏,也就是说栅栏(CyclicBarrier)已经被破坏.

 

7.3 使用ScheduledExecutor

其实还有队列的两节,但队列这里暂时不说了,以后再补。

ScheduledExecutor实质上和之前讲的CachedThreadPoolExcutor和FixedThreadPool同一用法,都是线程池管理

但是区别在于ScheduledExecutor可以定时控制,它通过schedule()(运行一次)或scheduleAtFixedRae()(每隔规定的时间重复执行)来执行。

public class ThreadScheduledMain {
    public static void main(String[] args){
        ScheduledExecutorService exec= Executors.newScheduledThreadPool(2);
        for(int i=0;i<5;i++) {
            exec.schedule(new ThreadScheeduledTest(i),1, TimeUnit.SECONDS);
        }
        exec.shutdown();
    }
}
public class ThreadScheeduledTest implements Runnable {
    private int time;

    public ThreadScheeduledTest(int time) {
        this.time = time;
    }

    @Override
    public void run() {
        try {
            Thread.sleep(time*1000);
            System.out.println("沙丁鱼的子线程:"+Thread.currentThread().getName()+"执行");
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            Thread.yield();
        }
    }
}

1.这里我限制线程池最多两个线程 newScheduledThreadPool(2);,所以结果的名称只有thread-1和thread-2

2. ScheduledExecutorService exec= Executors.newScheduledThreadPool(2);这里注意,我使用的是ScheduledExecutorService 而不是ExecutorService。

3.exec.schedule(new ThreadScheeduledTest(i),1, TimeUnit.SECONDS);的使用,

<V> ScheduledFuture<V> schedule(Callable<V> callable, long delay, TimeUnit unit)

三个参数,callable、延迟时间、延迟的时间单位(秒,分,时,天等)。

 

周期性运行

public class ThreadScheduledMain {
    public static void main(String[] args){
        ScheduledExecutorService exec= Executors.newScheduledThreadPool(2);
       exec.scheduleAtFixedRate(new ThreadScheeduledTest(),1,5, TimeUnit.SECONDS);

    }
}
public class ThreadScheeduledTest implements Runnable {

    @Override
    public void run() {
        try {
            System.out.println("沙丁鱼的子线程:"+Thread.currentThread().getName()+"执行");
        } finally {
            Thread.yield();
        }
    }
}

1.对于周期性执行,注意shutdown方法的放置,如果要一直运行,就别把shutdown直接放在下一句。

2.exec.scheduleAtFixedRate(new ThreadScheeduledTest(),1,5, TimeUnit.SECONDS);

ScheduledFuture<?> scheduleAtFixedRate(Runnable command, long initialDelay, long period, TimeUnit unit)
创建并执行在给定的初始延迟之后,以给定的时间间隔执行周期性动作。即在 initialDelay 初始延迟后,initialDelay+period 执行第一次,initialDelay + 2 * period  执行第二次,依次类推。

也就是实际案例中,先等1s运行了一个线程,后面都是等5s运行一个线程。

 

7.4 Semaphore

什么是Semaphore?

Semaphore 称之为 计数信号量。

与正常的锁的区别:

正常的锁(Lock、synchronized锁):在任何时刻只允许一个任务访问同一项资源

Semaphore :允许n个任务同时访问同一项资源。

感觉实际用到的不多,我也有点懒得写代码了,以后用到,再去看Semaphore这个类吧。

 

7.5 Exchanger

Exchanger是两个任务之间交换对象的栅栏。

在我的理解中简单来书哦就是两个线程之间交换数据的封装工具类

public class ThreadExchangerMain {
    public static void main(String[] args){
        Exchanger<Integer> exchanger = new Exchanger<Integer>();
        ExecutorService exec= Executors.newCachedThreadPool();
        exec.execute(new ThreadMe(exchanger));
        exec.execute(new ThreadYou(exchanger));
        exec.shutdown();
    }
}
public class ThreadMe implements Runnable {
    private Exchanger<Integer> exchanger;
    private static int ID = 0;
    ThreadMe(Exchanger<Integer> exchanger) {
        this.exchanger = exchanger;
    }
    @Override
    public void run() {
        try {
            TimeUnit.SECONDS.sleep(3);//线程睡3s
            System.out.println("沙丁鱼的子线程ThreadMe:"+Thread.currentThread().getName()+"执行");
            ID=9999;
            System.out.println("沙丁鱼的子线程ThreadMe:"+Thread.currentThread().getName()+"现在的ID:"+ID);
            ID=exchanger.exchange(ID);
            System.out.println("沙丁鱼的子线程ThreadMe:"+Thread.currentThread().getName()+"交换后的ID:"+ID);
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            Thread.yield();
        }
    }
}
public class ThreadYou implements Runnable {
    private Exchanger<Integer> exchanger;
    private static int ID = 0;
    ThreadYou(Exchanger<Integer> exchanger) {
        this.exchanger = exchanger;
    }
    @Override
    public void run() {
        try {
            TimeUnit.SECONDS.sleep(3);//线程睡3s
            System.out.println("沙丁鱼的子线程ThreadYou:"+Thread.currentThread().getName()+"执行");
            ID=1000;
            System.out.println("沙丁鱼的子线程ThreadYou:"+Thread.currentThread().getName()+"现在的ID:"+ID);
            ID=exchanger.exchange(ID);
            System.out.println("沙丁鱼的子线程ThreadYou:"+Thread.currentThread().getName()+"交换后的ID:"+ID);
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            Thread.yield();
        }
    }
}

这里的exchange类起到了一个中间传递的作用。

先两个线程都执行到了要交换数据的步骤后再统一执行。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值