• (二)juc线程高级特性——CountDownLatch / Callable / Lock


    5. CountDownLatch 闭锁

    Java 5.0 在 java.util.concurrent 包中提供了多种并发容器类来改进同步容器的性能。

    CountDownLatch 一个同步辅助类,在完成一组正在其他线程中执行的操作之前,它允许一个或多个线程一直等待。

    闭锁可以延迟线程的进度直到其到达终止状态,闭锁可以用来确保某些活动直到其他活动都完成才继续执行:

    •  确保某个计算在其需要的所有资源都被初始化之后才继续执行;
    •  确保某个服务在其依赖的所有其他服务都已经启动之后才启动;
    •  等待直到某个操作所有参与者都准备就绪再继续执行。

    如下:要求在创建的5个线程都执行完毕之后,再调用线程main方法输出耗费时间,使用wait,notify和notifyAll方法也可实现,但JDK不推荐。这里使用CountDownLatch。

    复制代码
    /*
     * CountDownLatch :闭锁,在完成某些运算时,只有其他所有线程的运算全部完成,当前运算才继续执行
     */
    public class TestCountDownLatch {
    
        public static void main(String[] args) {
            //CountDownLatch的构造函数接收一个int类型的参数作为计数器,如果你想等待N个点完成,这里就传入N。
            final CountDownLatch latch = new CountDownLatch(5);
            LatchDemo ld = new LatchDemo(latch);
    
            long start = System.currentTimeMillis();
    
            for (int i = 0; i < 5; i++) {
                new Thread(ld).start();
            }
    
            try {
                //阻塞当前线程,直到N变成零。
                latch.await();
            } catch (InterruptedException e) {
            }
    
            long end = System.currentTimeMillis();
    
            System.out.println("耗费时间为:" + (end - start));
        }
    
    }
    
    class LatchDemo implements Runnable {
    
        private CountDownLatch latch;
        public LatchDemo(CountDownLatch latch) {
            this.latch = latch;
        }
        @Override
        public void run() {
            try {
                for (int i = 0; i < 10; i++) {
                    if (i % 2 == 0) {
                        System.out.println(i);
                    }
                }
            } finally {  //计数器减一
                latch.countDown();
            }
        }
    }
    复制代码

    结果:

    6. 创建执行线程的方式三:实现 Callable 接口

    Thread类和Runnable接口都不允许声明检查型异常,也不能定义返回值。Thread类和Runnable接口都不允许声明检查型异常,也不能定义返回值。

    不能声明抛出检查型异常这个问题比较麻烦。public void run()方法契约意味着你必须捕获并处理检查型异常。即使你小心地保存了异常信息(在捕获异常时)以便稍后检查,但也不能保证这个类(Runnable对象)的所有使用者都读取异常信息。你也可以修改Runnable实现的getter,让它们都能抛出任务执行中的异常。但这种方法除了繁琐也不是十分安全可靠,你不能强迫使用者调用这些方法,程序员很可能会调用join()方法等待线程结束然后就不管了。

    Java 5.0 在 java.util.concurrent 提供了一个新的创建执行线程的方式:Callable 接口。Callable接口定义了方法public T call() throws Exception。我们可以在Callable实现中声明强类型的返回值,甚至是抛出异常。

    Future是Java 1.5中引入的接口,当你提交一个Callable对象给线程池时,将得到一个Future对象,并且它和你传入的Callable有相同的结果类型声明。这个对象取代了Java 1.5之前直接操作具体Thread实例的做法。过去你不得不用Thread.join()或者Thread.join(long millis)等待任务完成。

    Callable 需要依赖FutureTask(Future子类RunnableFuture的实现类) ,FutureTask 也可以用作闭锁。

    复制代码
    /*
     * 一、创建执行线程的方式三:实现 Callable 接口。 相较于实现 Runnable 接口的方式,方法可以有返回值,并且可以抛出异常。
     * 
     * 二、执行 Callable 方式,需要 FutureTask 实现类的支持,用于接收运算结果。  FutureTask 是  Future 接口的实现类
     */
    public class TestCallable {
        
        public static void main(String[] args) {
            ThreadDemo1 td = new ThreadDemo1();
            
            //1.执行 Callable 方式,需要 FutureTask 实现类的支持,用于接收运算结果。
            FutureTask<Integer> result = new FutureTask<>(td);
            
            new Thread(result).start();
            
            //2.接收线程运算后的结果
            try {
                Integer sum = result.get();  //FutureTask 可用于 闭锁
                System.out.println(sum);
                System.out.println("------------------------------------");
            } catch (InterruptedException | ExecutionException e) {
                e.printStackTrace();
            }
        }
    }
    
    class ThreadDemo1 implements Callable<Integer>{
    
        @Override
        public Integer call() throws Exception {
            int sum = 0;        
            for (int i = 0; i <= 100000; i++) {
                sum += i;
            }    
            return sum;
        }  
    }
    
    /*class ThreadDemo implements Runnable{
        @Override
        public void run() {
        }    
    }*/
    复制代码

    7. 同步锁 Lock

    在 Java 5.0 之前,协调共享对象的访问时可以使用的机制只有 synchronized 和 volatile 。Java 5.0 后增加了一些新的机制,但并不是一种替代内置锁的方法,而是当内置锁不适用时,作为一种可选择的高级功能。

    ReentrantLock 实现了 Lock 接口,并提供了与synchronized 相同的互斥性和内存可见性。但相较于synchronized 提供了更高的处理锁的灵活性。

    复制代码
    /*
     * 用于解决多线程安全问题的方式:
     * 
     * synchronized:隐式锁
     * 1. 同步代码块
     * 
     * 2. 同步方法
     * 
     * jdk 1.5 后:
     * 3. 同步锁 Lock
     * 注意:是一个显示锁,需要通过 lock() 方法上锁,必须通过 unlock() 方法进行释放锁
     */
    public class TestLock {    
        public static void main(String[] args) {
            Ticket ticket = new Ticket();
            
            new Thread(ticket, "1号窗口").start();
            new Thread(ticket, "2号窗口").start();
            new Thread(ticket, "3号窗口").start();
        }
    }
    
    class Ticket implements Runnable{  
        private int tick = 100; 
     
        private Lock lock = new ReentrantLock();
    
        @Override
        public void run() {
            while(true){         
                lock.lock(); //上锁     
                try{
                    if(tick > 0){
                        try {
                            Thread.sleep(200);
                        } catch (InterruptedException e) {
                        }                    
                        System.out.println(Thread.currentThread().getName() + " 完成售票,余票为:" + --tick);
                    }
                }finally{
                    lock.unlock(); //释放锁
                }
            }
        }    
    }
  • 相关阅读:
    如何在fragment里面嵌套viewpager?
    textview限制字数,结尾显示。。。。。
    限制imageview大小
    iOS开发核心动画之画图板
    iOS开发核心动画之九宫格解锁
    iOS开发核心动画之Quartz2D绘图
    iOS开发核心动画之触摸手指识别
    iOS开发网络多线程之断点下载
    iOS开发网络多线程之网络请求文件解析
    iOS开发网络多线程之Runloop无限循环
  • 原文地址:https://www.cnblogs.com/cxxjohnson/p/10432880.html
Copyright © 2020-2023  润新知