JUC并发编程

146 阅读18分钟

JUC

JUC是Java.util.concurrent下面的一个工具包

线程和进程的概念

进程与线程

进程:指系统中正在运行的一个应用程序;程序一旦运行就是进程;进程是资源分配的最小单位
线程:系统分配处理器时间资源的基本单位,或者说进程之类独立执行的一个单元执行流。线程是程序执行的最小单位

线程的状态

NEW(新建)
RUNNABLE(准备就绪)
BLOCKED(阻塞)
WAITING(不见不散)
TIMED_WAITING(过时不候)
TERMINATED(终结)

wait和sleep的区别

(1)、sleep是Thread的静态方法,wait是Object的方法,任何对象实例都能调用
(2)、sleep不会释放锁,它也不需要占用锁,wait会释放锁,但调用它的前提是当前线程占有锁(即代码要在synchronized中)
(3)、它们都可以被interrupted方法中断

并发和并行

并发:同一时刻多个线程在访问同一资源,多个线程对一个点
并行:多项工作一起执行,之后再汇总

管程(Monitor)

是一种同步机制,保证同一个时间,只有一个线程能访问被保护的数据或者代码。JVM同步是基于进入和退出进行操作的,就是使用的管程对象实现的

用户线程和守护线程

用户线程:自定义线程
守护线程:在后台默默执行的(垃圾回收)

public class Test {
    public static void main(String[] args) {
        Thread thread = new Thread(() -> {
            System.out.println(Thread.currentThread().getName() + "--是否守护线程:" + Thread.currentThread().isDaemon());
            while (true) {
            }
        }, "线程1");
        //设置守护线程
        thread.setDaemon(true);
        thread.start();
        System.out.println(Thread.currentThread().getName() + "over");
    }
}

总结:如果是用户线程,主线程结束了,用户线程还在运行,jvm存活。如果没有用户线程了,都是守护线程,jvm结束。

Lock接口

synchronized关键字

synchronized是Java中的关键字,是一种同步锁

  • 修饰的对象
  1. 代码块,被修饰的代码块称为同步语句块,作用范围是大括号{}的代码,作用的对象是调用这个代码块的对象
  2. 方法,被修饰的方法称为同步方法,作用范围整个方法,作用的对象是调用这个方法的对象
  3. 静态方法,作用范围整个静态方法,作用对象是这个类的所有对象
  4. 修饰一个类,作用范围是synchronized后面括起来的部分,作用对象是这个类的所有对象

synchronized锁实现卖票

//1.创建资源类,定义属性和方法
class Ticket {
    //票数
    private int number = 30;

    //操作方法:卖票
    public synchronized void sale() {
        //判断:是否有票
        if (number > 0) {
            number--;
            System.out.println(Thread.currentThread().getName() + "剩下票数:" + (number));
        }
    }
}

public class SaleTicket {
    public static void main(String[] args) {
        Ticket ticket = new Ticket();
        //创建三个线程
        new Thread(new Runnable() {
            @Override
            public void run() {
                for (int i = 0; i < 40; i++) {
                    ticket.sale();
                }
            }
        }, "AA").start();
        new Thread(new Runnable() {
            @Override
            public void run() {
                for (int i = 0; i < 40; i++) {
                    ticket.sale();
                }
            }
        }, "BB").start();
        new Thread(new Runnable() {
            @Override
            public void run() {
                for (int i = 0; i < 40; i++) {
                    ticket.sale();
                }
            }
        }, "CC").start();
    }
}

ReentrantLock可重入锁实现卖票

class LTicket {
    //创建可重入锁
    private final ReentrantLock lock = new ReentrantLock();
    private int number = 30;

    public void sale() {
        //上锁
        lock.lock();
        try {
            if (number > 0) {
                number--;
                System.out.println(Thread.currentThread().getName() + "剩下票数:" + (number));
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            //解锁
            lock.unlock();
        }
    }
}

public class LSaleTicket {
    public static void main(String[] args) {
        LTicket lTicket = new LTicket();
        new Thread(() -> {
            for (int i = 0; i < 40; i++) {
                lTicket.sale();
            }
        }, "AA").start();
        new Thread(() -> {
            for (int i = 0; i < 40; i++) {
                lTicket.sale();
            }
        }, "BB").start();
        new Thread(() -> {
            for (int i = 0; i < 40; i++) {
                lTicket.sale();
            }
        }, "CC").start();
    }
}

Lock和synchronized区别

  1. Lock是一个接口,而synchronized是Java中的关键字,synchronized是内置的Java语言实现
  2. synchronized在发生异常的时,会自动释放线程占有的锁,因此不会导致死锁现象发生;而Lock在发生异常时,如果没有主动通过unlock()去释放锁,则很可能造成死锁现象,因此使用Lock时需要在Finally块中释放锁
  3. Lock可以让等待的线程响应中断,而synchronized却不行,使用synchronized时,等待的线程会一直等待下去,不能够响应中断
  4. 通过Lock可以知道有没有成功释放锁,而synchronized却无法办到。
  5. Lock可以提高多个线程进行读操作的效率

线程间的通信

  • 多线程的编程步骤
  1. 创建资源类,创建属性和操作方法
  2. 在资源类操作方法(1)判断、(2)干活、(3)通知
  3. 创建多线程调用资源类的方法
  4. 防止虚假唤醒问题(在while中进行判断)

synchronized实现线程通信(交替执行)

class Share {
    //初始值
    private int num = 0;

    //+1的方法
    public synchronized void incr() throws InterruptedException {
        //第二步 判断 干活 通知
        //判断num是否是0,如果不是,等待
        if (num != 0) {
            this.wait();
        }
        //如果num是0,就+1操作
        num++;
        System.out.println(Thread.currentThread().getName() + "::" + num);
        //通知其他线程
        this.notifyAll();
    }

    //-1的方法
    public synchronized void decr() throws InterruptedException {
        //判断
        if (num != 1) {
            this.wait();
        }
        //干活
        num--;
        System.out.println(Thread.currentThread().getName() + "::" + num);
        //通知
        this.notifyAll();
    }
}

public class ThreadDemo1 {
    public static void main(String[] args) {
        //第三步 创建多线程 调用资源类的操作方法
        Share share = new Share();
        //创建线程
        new Thread(() -> {
            for (int i = 0; i < 10; i++) {
                try {
                    share.incr();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }, "AA").start();
        new Thread(() -> {
            for (int i = 0; i < 10; i++) {
                try {
                    share.decr();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }, "BB").start();
    }
}

问题: 如果不止两个线程,再次新增两个线程实现递增和递减,会出现线程虚假唤醒问题;因为wait()方法在哪里睡就会在哪里醒

解决: if判断需要修改成while判断

Lock实现线程通信(交替执行)

class Share {
    //初始值
    private int num = 0;

    private Lock lock = new ReentrantLock();

    private Condition condition = lock.newCondition();

    //+1的方法
    public void incr() throws InterruptedException {
        //第二步 判断 干活 通知
        //判断num是否是0,如果不是,等待
        lock.lock();
        try {
            while (num != 0) {
                condition.await();
            }
            //如果num是0,就+1操作
            num++;
            System.out.println(Thread.currentThread().getName() + "::" + num);
            //通知其他线程
            condition.signalAll();
        } finally {
            lock.unlock();
        }

    }

    //-1的方法
    public void decr() throws InterruptedException {
        lock.lock();
        try {
            while (num != 1) {
                condition.await();
            }
            //如果num是0,就+1操作
            num--;
            System.out.println(Thread.currentThread().getName() + "::" + num);
            //通知其他线程
            condition.signalAll();
        } finally {
            lock.unlock();
        }
    }
}

public class ThreadDemo2 {
    public static void main(String[] args) {
        //第三步 创建多线程 调用资源类的操作方法
        Share share = new Share();
        //创建线程
        new Thread(() -> {
            for (int i = 0; i < 10; i++) {
                try {
                    share.incr();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }, "AA").start();
        new Thread(() -> {
            for (int i = 0; i < 10; i++) {
                try {
                    share.decr();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }, "BB").start();

        new Thread(() -> {
            for (int i = 0; i < 10; i++) {
                try {
                    share.incr();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }, "CC").start();
        new Thread(() -> {
            for (int i = 0; i < 10; i++) {
                try {
                    share.decr();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }, "DD").start();
    }
}

线程间定制化通信

//第一步 创建资源类
class ShareResource {
    //定义标志位
    private int flag = 1;
    //创建Lock锁
    private Lock lock = new ReentrantLock();
    //创建三个Condition
    private Condition condition1 = lock.newCondition();
    private Condition condition2 = lock.newCondition();
    private Condition condition3 = lock.newCondition();

    //打印5次
    public void print5(int loop) {
        lock.lock();
        try {
            while (flag != 1) {
                condition1.await();
            }
            for (int i = 1; i <= 5; i++) {
                System.out.println(Thread.currentThread().getName() + "值:" + i + "循环轮数" + loop);
            }
            flag = 2;
            condition2.signal();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }

    public void print10(int loop) {
        lock.lock();
        try {
            while (flag != 2) {
                condition2.await();
            }
            for (int i = 1; i <= 10; i++) {
                System.out.println(Thread.currentThread().getName() + "值:" + i + "循环轮数" + loop);
            }
            flag = 3;
            condition3.signal();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }

    public void print15(int loop) {
        lock.lock();
        try {
            while (flag != 3) {
                condition3.await();
            }
            for (int i = 1; i <= 15; i++) {
                System.out.println(Thread.currentThread().getName() + "值:" + i + "循环轮数" + loop);
            }
            flag = 1;
            condition1.signal();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }
}

public class ThreadDemo3 {
    public static void main(String[] args) {
        ShareResource shareResource = new ShareResource();
        new Thread(() -> {
            for (int i = 1; i <= 10; i++) {
                shareResource.print5(i);
            }
        }, "AA").start();
        new Thread(() -> {
            for (int i = 1; i <= 10; i++) {
                shareResource.print10(i);
            }
        }, "BB").start();
        new Thread(() -> {
            for (int i = 1; i <= 10; i++) {
                shareResource.print15(i);
            }
        }, "CC").start();
    }
}

集合的线程安全

ArrayList

public class ListDemo {
    public static void main(String[] args) {
        /**
         * 线程不安全,
         * 故障现象:java.util.ConcurrentModificationException
         * 导致原因:类似签到->A员工签到,签到一半B员工去抢夺签到,就会导致并发修改异常
         * 一个人正在写入,另外一个人过来抢夺,导致数据不一致异常
         * 解决:
         * 1.new Vector<>();
         * 2.Collections.synchronizedList(new ArrayList<>());
         * 3.new CopyOnWriteArrayList();
         * new CopyOnWriteArrayList() 底层实现:写时复制、读写分离的思想
         * 往一个容器添加元素的时候,不直接往当前容器Object[]添加,而是先将当前容器复制一份
         * ,然后对新的容器里面添加元素,添加完元素后,再将原容器的引用地址指向新的容器。这样做的
         * 好处是可以对容器进行并发的读,而不需要加锁,因为当前容器不会添加任何元素,所以这也是一种
         * 读写分离的好思想,读和写不同的容器
         * 优化建议:
         */
        List<String> list = new ArrayList<>();
        for (int i = 0; i < 50; i++) {
            new Thread(() -> {
                list.add(UUID.randomUUID().toString().substring(0, 8));
                System.out.println(list);
            }, String.valueOf(i)).start();
        }
    }
}

HashSet

public class SetDemo {
    public static void main(String[] args) {
        /**
         * 线程不安全,
         * 故障现象:java.util.ConcurrentModificationException
         * 解决:
         * 1.Collections.synchronizedSet(new HashSet<>());
         * 2.new CopyOnWriteArraySet();
         */
        Set<String> set = new HashSet<>();
        for (int i = 0; i < 50; i++) {
            new Thread(() -> {
                set.add(UUID.randomUUID().toString().substring(0, 8));
                System.out.println(set);
            }, String.valueOf(i)).start();
        }
    }
}

HashMap

public class MapDemo {
    public static void main(String[] args) {
        /**
         * 线程不安全,
         * 故障现象:java.util.ConcurrentModificationException
         * 解决:
         * 1.new ConcurrentHashMap();
         */
        Map<String, String> map = new HashMap<>();
        for (int i = 0; i < 50; i++) {
            String key = String.valueOf(i);
            new Thread(() -> {
                map.put(key, UUID.randomUUID().toString().substring(0, 8));
                System.out.println(map);
            }, String.valueOf(i)).start();
        }
    }
}

多线程锁

synchronized实现同步的基础:Java中的每一个对象都可以作为锁
具体表现为以下三种形式:

  • 对于普通同步方法,锁的是当前实例对象
  • 对于静态同步方法,锁的是当前类的Class对象
  • 对于同步方法块,锁的是synchronized括号里配置的对象

公平锁和非公平锁

非公平锁(默认):效率高,但是会出现线程饿死情况
Lock lock = new ReentrantLock();
Lock lock = new ReentrantLock(false);
公平锁:效率相对低,不会出现线程饿死情况
Lock lock = new ReentrantLock(true);

可重入锁

synchronized(隐式)和Lock(显式)都是可重入锁

synchronized实现可重入锁

public class Test {
    public static void main(String[] args) throws InterruptedException {
        Object o = new Object();
        synchronized (o) {
            System.out.println("外层");
            synchronized (o) {
                System.out.println("中层");
                synchronized (o) {
                    System.out.println("内层");
                }
            }
        }
    }
}

Lock实现可重入锁

public class Test {
    public static void main(String[] args) throws InterruptedException {
        Lock lock = new ReentrantLock();
        lock.lock();
        try {
            System.out.println("外层");
            lock.lock();
            try {
                System.out.println("中层");
                lock.lock();
                try {
                    System.out.println("内层");
                } finally {
                    lock.unlock();
                }
            } finally {
                lock.unlock();
            }
        } finally {
            lock.unlock();
        }
    }
}

死锁

两个或者两个以上进程在执行过程中,因为争夺资源而造成一种互相等待的现象,如果没有外力干涉,他们无法再继续执行下去

代码实现

public class Test {
    static Object a = new Object();
    static Object b = new Object();

    public static void main(String[] args) throws InterruptedException {
        new Thread(() -> {
            synchronized (a) {
                System.out.println(Thread.currentThread().getName() + "持有锁a,试图获取锁b");
                try {
                    TimeUnit.SECONDS.sleep(1);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                synchronized (b) {
                    System.out.println(Thread.currentThread().getName() + "获取锁b");
                }
            }
        }, "AA").start();

        new Thread(() -> {
            synchronized (b) {
                System.out.println(Thread.currentThread().getName() + "持有锁b,试图获取锁a");
                try {
                    TimeUnit.SECONDS.sleep(1);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                synchronized (a) {
                    System.out.println(Thread.currentThread().getName() + "获取锁a");
                }
            }
        }, "BB").start();
    }
}

Java多线程

多线程创建的几种方式

  1. 继承Thread
public class Test {
    public static void main(String[] args) {
        System.out.println("main方法...start");
        Thread thread = new Thread01();
        thread.start();
        System.out.println("main方法...end");
    }

    public static class Thread01 extends Thread {
        @Override
        public void run() {
            System.out.println("当前线程---" + Thread.currentThread().getName());
            int num = 10 / 2;
            System.out.println("运行结果---" + num);
        }
    }
}
  1. 实现Runnable接口
public class Test {
    public static void main(String[] args) {
        System.out.println("main方法...start");
        Runnable01 runnable01 = new Runnable01();
        Thread thread = new Thread(runnable01);
        thread.start();
        System.out.println("main方法...end");
    }

    public static class Runnable01 implements Runnable {
        @Override
        public void run() {
            System.out.println("当前线程---" + Thread.currentThread().getName());
            int num = 10 / 2;
            System.out.println("运行结果---" + num);
        }
    }
}
  1. 实现Callable接口+FutureTask(可以拿到返回结果,可以处理异常)
public class Test {
    public static void main(String[] args) {
        System.out.println("main方法...start");
        FutureTask<Object> futureTask = new FutureTask<>(new Callable01());
        Thread thread = new Thread(futureTask);
        thread.start();
        Object o = null;
        try {
            //阻塞等待 等待整个线程执行完成,获取返回结果
            o = futureTask.get();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        }
        //需要等待获取到返回结果才会进行打印
        System.out.println("main方法...end==>" + o);
    }

    /**
     * Callable<Object> ==>Object对应需要返回的结果
     */
    public static class Callable01 implements Callable<Object> {
        @Override
        public Object call() throws Exception {
            System.out.println("当前线程---" + Thread.currentThread().getName());
            int num = 10 / 2;
            System.out.println("运行结果---" + num);
            return num;
        }
    }
}
  1. 线程池(推荐使用) 以上三种方式创建线程不推荐使用,如果多个请求每次new Thread();可能会导致我们资源耗尽;我们应该将所有的异步线程任务交给线程池执行
/**
* 根据Executors来创建线程池
*/
public class Test {
    /**
     * 当前系统中池的数量应该只会有一两个(根据自己的业务场景来定);每个异步任务,提交给线程池让它自己去执行
     * Executors.newFixedThreadPool(10) 获取一个定长线程池
     */
    public static ExecutorService executor = Executors.newFixedThreadPool(10);
    
    /**
     * 七个参数
     * int corePoolSize :核心线程数【一直存在,除非设置allowCoreThreadTimeOut】,线程池创建好之后就准备就绪的线程数量,就等待来接收异步线程任务去执行
     * int maximumPoolSize :最大线程数;控制资源(CPU密集型[CPU核数+1]、IO密集型[CPU核数*2、CPU核数/(1-阻塞系数[0.8~0.9])])
     * long keepAliveTime : 存活时间(如果当前线程数量大于核心数量,释放空闲线程[maximumPoolSize-corePoolSize]需要的时间)
     * TimeUnit unit : 时间单位
     * BlockingQueue<Runnable> workQueue : 阻塞队列。如果任务有很多,就会将目前对的任务放在队列,只要有线程空闲了,就会从队列中取出新的任务继续执行
     * ThreadFactory threadFactory : 线程创建工厂
     * RejectedExecutionHandler handler : 如果队列满了,按照我们指定的拒绝策略拒绝执行任务(new AbortPolicy()[默认,直接抛出异常阻止系统正常运行]、new DiscardPolicy()[直接丢弃进来的任务]、new DiscardOldestPolicy()[丢弃等待最久的任务]、new CallerRunsPolicy()[不抛弃任务,也不丢弃任务,将任务回退给调用者])
     * <p>
     * 执行顺序:
     * 1.线程池创建好,准备core数量的核心线程,准备接收任务
     * 2.如果核心线程数满了,再进来的任务就放入阻塞队列,空闲的线程就自己会去阻塞队列里面获取任务执行
     * 3.阻塞队列如果也满了,就会直接开新线程执行,最大开到max指定的数量
     * 4.如果max满了,就会使用拒绝策略拒绝任务
     * 5.max没满都执行完了,有很多空闲线程;就会在指定的时间(keepAliveTime)以后释放maximumPoolSize-corePoolSize这些线程
     * <p>
     * 提示:
     * new LinkedBlockingDeque<>() 默认是Integer.MAX_VALUE。可能导致内存不够 需要根据压测结果自己指定
     */
    public static ThreadPoolExecutor executor01 = new ThreadPoolExecutor(10
            , 200
            , 10
            , TimeUnit.SECONDS
            , new LinkedBlockingDeque<>()
            , Executors.defaultThreadFactory()
            , new ThreadPoolExecutor.AbortPolicy());

    public static void main(String[] args) {
        System.out.println("main方法...start");
//        executor.execute(); //无法获取任务返回值
        Future<Object> submit = executor.submit(new Callable01());//可以获取任务返回值
        Object o = null;
        try {
            o = submit.get();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        }
        System.out.println("main方法...end == >" + o);
    }

    /**
     * Callable<Object> ==>Object对应需要返回的结果
     */
    public static class Callable01 implements Callable<Object> {
        @Override
        public Object call() throws Exception {
            System.out.println("当前线程---" + Thread.currentThread().getName());
            int num = 10 / 2;
            System.out.println("运行结果---" + num);
            return num;
        }
    }
}

线程池几种类型

  • Executors.newCachedThreadPool(); //可缓存的线程池 core是0,所有都可以回收
  • Executors.newFixedThreadPool(10); //定长线程池,固定大小不可回收
  • Executors.newSingleThreadExecutor(); // core是1 单线程池
  • Executors.newScheduledThreadPool(10);//定时任务线程池

创建线程几种方式的区别

  • 第1、2两种方式不能获取返回值,第3种可以获取返回值
  • 第1、2、3种方式不能进行资源控制
  • 第4种可以进行资源控制,性能稳定

使用线程池的优点

  • 降低资源消耗
  • 提高响应速度
  • 提供线程的可管理性

线程上下文切换(Thread Context Switch)

因为以下一些原因导致 cpu 不再执行当前的线程,转而执行另一个线程的代码

  • 线程的 cpu 时间片用完
  • 垃圾回收
  • 有更高优先级的线程需要运行
  • 线程自己调用了 sleep、yield、wait、join、park、synchronized、lock 等方法

当 Context Switch 发生时,需要由操作系统保存当前线程的状态,并恢复另一个线程的状态,Java 中对应的概念 就是程序计数器(Program Counter Register),它的作用是记住下一条 jvm 指令的执行地址,是线程私有的

  • 状态包括程序计数器、虚拟机栈中每个栈帧的信息,如局部变量、操作数栈、返回地址等
  • Context Switch 频繁发生会影响性能

start与run

public class Test {
    public static void main(String[] args) {
        Thread thread = new Thread("t1") {
            @Override
            public void run() {
                System.out.println("running...");
                try {
                    Thread.sleep(2000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        };
        // thread.run();
        thread.start();
        System.out.println("do other things...");
    }
}

小结

  • 直接调用 run 是在主线程中执行了 run,没有启动新的线程
  • 使用 start 是启动新的线程,通过新的线程间接执行 run 中的代码

sleep与yield

sleep

  • 调用sleep会让当前线程从Running进入Timed Waiting状态(阻塞)
  • 其它线程可以使用 interrupt 方法打断正在睡眠的线程,这时 sleep 方法会抛出 InterruptedException
  • 睡眠结束后的线程未必会立刻得到执行
  • 建议用TimeUnit的sleep代替Thread的sleep来获得更好的可读性

yield

  • 调用yield会让当前线程从Running进入Runnable就绪状态,然后调度执行其它线程
  • 具体的实现依赖于操作系统的任务调度器

线程优先级

  • 线程优先级会提示(hint)调度器优先调度该线程,但它仅仅是一个提示,调度器可以忽略它
  • 如果cpu比较忙,那么优先级高的线程会获得更多的时间片,但cpu闲时,优先级几乎没作用

JUC强大辅助类

减少计数CountDownLatch

public class Test {

    //6个同学陆续离开教室之后,班长锁门
    public static void main(String[] args) throws InterruptedException {
        //创建CountDownLatch对象,设置初始值
        CountDownLatch countDownLatch = new CountDownLatch(6);
        //6个同学陆续离开教室之后
        for (int i = 1; i <= 6; i++) {
            new Thread(() -> {
                System.out.println(Thread.currentThread().getName() + "号同学离开了教室");
                //计数 -1
                countDownLatch.countDown();
            }, String.valueOf(i)).start();
        }
        //等待
        countDownLatch.await();
        System.out.println(Thread.currentThread().getName() + "班长锁门走人");
    }

}

循环栅栏CyclicBarrier

public class Test {

    //集齐7颗龙珠召唤神龙
    public static void main(String[] args) throws InterruptedException {
        //创建CyclicBarrier对象,设置初始值
        CyclicBarrier cyclicBarrier = new CyclicBarrier(7, () -> {
            System.out.println("***集齐7颗龙珠就可以召唤神龙");
        });
        //集齐7颗龙珠过程
        for (int i = 1; i <= 7; i++) {
            new Thread(() -> {
                System.out.println(Thread.currentThread().getName() + "星龙珠被收集到了");
                //等待
                try {
                    cyclicBarrier.await();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } catch (BrokenBarrierException e) {
                    e.printStackTrace();
                }
            }, String.valueOf(i)).start();
        }
    }

}

信号量Semaphore

public class Test {

    //模拟5件商品出售与退货
    public static void main(String[] args) throws InterruptedException {
        //创建Semaphore,设置许可数量
        Semaphore semaphore = new Semaphore(5);
        //模拟6辆汽车
        for (int i = 1; i <= 10; i++) {
            new Thread(() -> {
                try {
                    //出售
                    semaphore.acquire();
                    System.out.println(Thread.currentThread().getName() + "号商品已出售");
                    //模拟退货
                    TimeUnit.SECONDS.sleep(new Random().nextInt(5));
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } finally {
                    //退货
                    semaphore.release();
                    System.out.println(Thread.currentThread().getName() + "号商品已退货");
                }
            }, String.valueOf(i)).start();
        }
    }

}

读写锁

一个资源可以被多个线程访问,或者可以被一个写线程访问,但是不能同时存在读写线程,读写互斥,读读共享的

//资源类
class MyCache {
    //创建map集合
    private volatile Map<String, Object> map = new HashMap<>();

    //创建读写锁对象
    private ReadWriteLock readWriteLock = new ReentrantReadWriteLock();

    //放数据
    public void put(String key, Object value) {
        //添加写锁
        readWriteLock.writeLock().lock();
        try {
            System.out.println(Thread.currentThread().getName() + "正在写操作" + key);
            //暂停一会
            TimeUnit.MICROSECONDS.sleep(300);
            //放数据
            map.put(key, value);
            System.out.println(Thread.currentThread().getName() + "写完了" + key);
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            readWriteLock.writeLock().unlock();
        }
    }

    //放数据
    public Object get(String key) {
        //添加读锁
        readWriteLock.readLock().lock();
        Object result = null;
        try {
            System.out.println(Thread.currentThread().getName() + "正在读取操作" + key);
            //暂停一会
            TimeUnit.MICROSECONDS.sleep(300);
            //放数据
            result = map.get(key);
            System.out.println(Thread.currentThread().getName() + "取完了" + key);
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            readWriteLock.readLock().unlock();
        }
        return result;
    }
}

public class ReadWriteLockDemo {

    public static void main(String[] args) {
        MyCache myCache = new MyCache();
        //创建线程放数据
        for (int i = 0; i < 5; i++) {
            final int num = i;
            new Thread(() -> {
                myCache.put(num + "", num + "");
            }, String.valueOf(i)).start();
        }

        //创建线程取数据
        for (int i = 0; i < 5; i++) {
            final int num = i;
            new Thread(() -> {
                myCache.get(num + "");
            }, String.valueOf(i)).start();
        }
    }
}
  • 读写锁对象:ReadWriteLock readWriteLock = new ReentrantReadWriteLock();
  • 读锁(共享锁):readWriteLock.readLock().lock();
  • 写锁(独占锁):readWriteLock.writeLock().lock()

优点:

  1. 读读可以共享,提升性能,可以同时多人进行读操作

缺点:

  1. 造成锁饥饿,一直读,没有写操作
  2. 读的时候,不能写,只有读完成之后,才可以写,写操作可以读

锁降级

  • 写锁降级为读锁
public class Test {

    public static void main(String[] args) throws InterruptedException {
        ReentrantReadWriteLock readWriteLock = new ReentrantReadWriteLock();
        ReentrantReadWriteLock.ReadLock readLock = readWriteLock.readLock();
        ReentrantReadWriteLock.WriteLock writeLock = readWriteLock.writeLock();
        writeLock.lock();
        System.out.println("获取写锁");
        readLock.lock();
        System.out.println("获取读锁");
        writeLock.unlock();
        readLock.unlock();
    }

}

BlockingQueue阻塞队列

当队列是空的时,从队列中获取元素的操作是阻塞 队列是满的时,向队列中添加元素的操作是阻塞

常用阻塞队列

  • 数组结构组成的有界阻塞队列 BlockingQueue<String> arrayBlockingQueue = new ArrayBlockingQueue<>(3);
  • 由链表结构组成的有界阻塞队列 BlockingQueue<String> linkedBlockingQueue = new LinkedBlockingQueue<>();
  • 不存储任何元素的阻塞队列,即放一个取一个 BlockingQueue<String> synchronousQueue = new SynchronousQueue<>();

核心方法

方法类型抛出异常特殊值阻塞超时
插入add(e)offer(e)put(e)offer(e,time,unit)
移除remove(e)poll()take()poll(time,unit)
检查element()peek()不可用不可用

image.png

CompletableFuture异步编排

创建异步对象

提供了四个静态方法创建异步操作

  • public static CompletableFuture runAsync(Runnable runnable) //无返回值
  • public static CompletableFuture runAsync(Runnable runnable,Executor executor) //无返回值,可传入自定义的线程池对象
public class Test {

    public static ExecutorService executor = Executors.newFixedThreadPool(10);

    public static void main(String[] args) {
        System.out.println("main方法...start");
        CompletableFuture.runAsync(() -> {
            System.out.println("当前线程---" + Thread.currentThread().getName());
            int num = 10 / 2;
            System.out.println("运行结果---" + num);
        }, executor);
        System.out.println("main方法...end");
    }

}
  • public static CompletableFuture supplyAsync(Supplier supplier)//有返回值
  • public static CompletableFuture supplyAsync(Supplier supplier,Executor executor)//返回值,可传入自定义的线程池对象
public class Test {

    public static ExecutorService executor = Executors.newFixedThreadPool(10);

    public static void main(String[] args) {
        System.out.println("main方法...start");
        CompletableFuture<Integer> future = CompletableFuture.supplyAsync(() -> {
            System.out.println("当前线程---" + Thread.currentThread().getName());
            int num = 10 / 2;
            System.out.println("运行结果---" + num);
            return num;
        }, executor);
        Integer integer = 0;
        try {
            integer = future.get();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        }
        System.out.println("main方法...end" + integer);
    }

}

异步完成回调与异常感知

whenComplete只可以进行感知结果和异常,但是无法对结果进行修改

  • public CompletableFuture whenComplete(BiConsumer<? super T, ? super Throwable> action) 执行当前任务的线程继续执行
public class Test {

    public static ExecutorService executor = Executors.newFixedThreadPool(10);

    public static void main(String[] args) {
        System.out.println("main方法...start");
        CompletableFuture<Integer> future = CompletableFuture.supplyAsync(() -> {
            System.out.println("当前线程---" + Thread.currentThread().getName());
            int num = 10 / 2;
            System.out.println("运行结果---" + num);
            return num;
        }, executor).whenComplete((res, exception) -> {
            System.out.println("结果是-->" + res + "异常是-->" + exception);
        });
        Integer integer = 0;
        try {
            integer = future.get();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        }
        System.out.println("main方法...end" + integer);
    }

}
  • public CompletableFuture whenCompleteAsync(BiConsumer<? super T, ? super Throwable> action) 继续提交给线程池进行处理
  • public CompletableFuture whenCompleteAsync(BiConsumer<? super T, ? super Throwable> action, Executor executor) 继续提交给线程池进行处理,指定自定义线程池
  • public CompletableFuture exceptionally(Function<Throwable, ? extends T> fn) 处理异常情况
public class Test {

    public static ExecutorService executor = Executors.newFixedThreadPool(10);

    public static void main(String[] args) {
        System.out.println("main方法...start");
        CompletableFuture<Integer> future = CompletableFuture.supplyAsync(() -> {
            System.out.println("当前线程---" + Thread.currentThread().getName());
            int num = 10 / 0;
            System.out.println("运行结果---" + num);
            return num;
        }, executor).whenComplete((res, exception) -> {
            System.out.println("结果是-->" + res + "异常是-->" + exception);
        }).exceptionally((exception) -> {
            //可以感知异常,进行设置默认值
            return 10;
        });
        Integer integer = 0;
        try {
            integer = future.get();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        }
        System.out.println("main方法...end" + integer);
    }

}

handle可对结果进行后续处理

public class Test {

    public static ExecutorService executor = Executors.newFixedThreadPool(10);

    public static void main(String[] args) {
        System.out.println("main方法...start");
        CompletableFuture<Integer> future = CompletableFuture.supplyAsync(() -> {
            System.out.println("当前线程---" + Thread.currentThread().getName());
            int num = 10 / 2;
            System.out.println("运行结果---" + num);
            return num;
        }, executor).handle((res, exception) -> {
            if (res != null) {
                return res * 2;
            }
            if (exception != null) {
                return res;
            }
            return 0;
        });
        Integer integer = 0;
        try {
            integer = future.get();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        }
        System.out.println("main方法...end" + integer);
    }

}

线程串行化

  • public CompletableFuture thenRun(Runnable action)
  • public CompletableFuture thenRunAsync(Runnable action)
  • public CompletableFuture thenRunAsync(Runnable action,Executor executor) 不能获取上一步的执行结果,无返回值
public class Test {

    public static ExecutorService executor = Executors.newFixedThreadPool(10);

    public static void main(String[] args) {
        System.out.println("main方法...start");
        CompletableFuture<Integer> future = CompletableFuture.supplyAsync(() -> {
            System.out.println("当前线程---" + Thread.currentThread().getName());
            int num = 10 / 2;
            System.out.println("运行结果---" + num);
            return num;
        }, executor);


        future.thenRunAsync(() -> {
            System.out.println("任务2启动了....");
        }, executor);

        System.out.println("main方法...end");
    }

}
  • public CompletableFuture thenAccept(Consumer<? super T> action)
  • public CompletableFuture thenAcceptAsync(Consumer<? super T> action)
  • public CompletableFuture thenAcceptAsync(Consumer<? super T> action,Executor executor)能接收上一个线程的执行结果,无返回值
public class Test {

    public static ExecutorService executor = Executors.newFixedThreadPool(10);

    public static void main(String[] args) {
        System.out.println("main方法...start");
        CompletableFuture<Integer> future = CompletableFuture.supplyAsync(() -> {
            System.out.println("当前线程---" + Thread.currentThread().getName());
            int num = 10 / 2;
            System.out.println("运行结果---" + num);
            return num;
        }, executor);


        future.thenAcceptAsync((res) -> {
            System.out.println("任务2启动了...."+res);
        }, executor);

        System.out.println("main方法...end");
    }

}
  • public CompletableFuture thenApply(Function<? super T,? extends U> fn)
  • public CompletableFuture thenApplyAsync(Function<? super T,? extends U> fn)
  • public CompletableFuture thenApplyAsync(Function<? super T,? extends U> fn, Executor executor) 能接收上一个线程的执行结果,有返回值
public class Test {

    public static ExecutorService executor = Executors.newFixedThreadPool(10);

    public static void main(String[] args) {
        System.out.println("main方法...start");
        CompletableFuture<Integer> future = CompletableFuture.supplyAsync(() -> {
            System.out.println("当前线程---" + Thread.currentThread().getName());
            int num = 10 / 2;
            System.out.println("运行结果---" + num);
            return num;
        }, executor);

        CompletableFuture<Integer> future1 = future.thenApplyAsync((res) -> {
            System.out.println("任务2启动了...." + res);
            return res * 2;
        }, executor);
        Integer integer = 0;
        try {
            integer = future1.get();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        }
        System.out.println("main方法...end" + integer);
    }

}

两任务组合-都要完成

  • public CompletableFuture runAfterBoth(CompletionStage<?> other,Runnable action)
  • public CompletableFuture runAfterBothAsync(CompletionStage<?> other,Runnable action)
  • public CompletableFuture runAfterBothAsync(CompletionStage<?> other,Runnable action,Executor executor) 组合两个future,不需要获取future的结果,只需要两个future处理完任务后,处理该任务
public class Test {

    public static ExecutorService executor = Executors.newFixedThreadPool(10);

    public static void main(String[] args) {
        System.out.println("main方法...start");
        CompletableFuture<Integer> future = CompletableFuture.supplyAsync(() -> {
            System.out.println("当前线程1---" + Thread.currentThread().getName());
            int num = 10 / 2;
            System.out.println("运行结果1---" + num);
            return num;
        }, executor);

        CompletableFuture<Integer> future1 = CompletableFuture.supplyAsync(() -> {
            System.out.println("当前线程1---" + Thread.currentThread().getName());
            int num = 10 / 5;
            System.out.println("运行结果1---" + num);
            return num;
        }, executor);

        future.runAfterBothAsync(future1,()->{
            System.out.println("任务3开始");
        },executor);


        System.out.println("main方法...end");
    }

}
  • public < U> CompletableFuture< Void> thenAcceptBoth(CompletionStage<? extends U> other,BiConsumer<? super T, ? super U> action)
  • public CompletableFuture thenAcceptBothAsync(CompletionStage<? extends U> other,BiConsumer<? super T, ? super U> action)
  • public CompletableFuture thenAcceptBothAsync(CompletionStage<? extends U> other,BiConsumer<? super T, ? super U> action,Executor executor) 组合两个future,获取两个future任务的返回结果,然后处理任务,没有返回值
public class Test {

    public static ExecutorService executor = Executors.newFixedThreadPool(10);

    public static void main(String[] args) {
        System.out.println("main方法...start");
        CompletableFuture<Integer> future = CompletableFuture.supplyAsync(() -> {
            System.out.println("当前线程1---" + Thread.currentThread().getName());
            int num = 10 / 2;
            System.out.println("运行结果1---" + num);
            return num;
        }, executor);

        CompletableFuture<Integer> future1 = CompletableFuture.supplyAsync(() -> {
            System.out.println("当前线程1---" + Thread.currentThread().getName());
            int num = 10 / 5;
            System.out.println("运行结果1---" + num);
            return num;
        }, executor);

        future.thenAcceptBothAsync(future1, (res1, res2) -> {
            System.out.println("第一个结果的返回值" + res1 + "第二个结果的返回值" + res2);
        }, executor);


        System.out.println("main方法...end");
    }

}
  • public <U,V> CompletableFuture thenCombine(CompletionStage<? extends U> other,BiFunction<? super T,? super U,? extends V> fn)
  • public <U,V> CompletableFuture thenCombineAsync(CompletionStage<? extends U> other,BiFunction<? super T,? super U,? extends V> fn)
  • public <U,V> CompletableFuture thenCombineAsync(CompletionStage<? extends U> other,BiFunction<? super T,? super U,? extends V> fn, Executor executor) 组合两个future,获取两个future的返回结果,并返回当前任务的返回值
public class Test {

    public static ExecutorService executor = Executors.newFixedThreadPool(10);

    public static void main(String[] args) {
        System.out.println("main方法...start");
        CompletableFuture<Integer> future = CompletableFuture.supplyAsync(() -> {
            System.out.println("当前线程1---" + Thread.currentThread().getName());
            int num = 10 / 2;
            System.out.println("运行结果1---" + num);
            return num;
        }, executor);

        CompletableFuture<Integer> future1 = CompletableFuture.supplyAsync(() -> {
            System.out.println("当前线程1---" + Thread.currentThread().getName());
            int num = 10 / 5;
            System.out.println("运行结果1---" + num);
            return num;
        }, executor);

        CompletableFuture<Integer> future2 = future.thenCombineAsync(future1, (res1, res2) -> {
            System.out.println("第一个结果的返回值" + res1 + "第二个结果的返回值" + res2);
            return res1 + res2;
        }, executor);

        Integer integer = 0;
        try {
            integer = future2.get();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        }
        System.out.println("main方法...end" + integer);
    }

}

两任务组合-单个完成

  • public CompletableFuture runAfterEither(CompletionStage<?> other,Runnable action)
  • public CompletableFuture runAfterEitherAsync(CompletionStage<?> other,Runnable action)
  • public CompletableFuture runAfterEitherAsync(CompletionStage<?> other,Runnable action,Executor executor) 组合两个future,两个任务有一个任务执行完,不需要获取返回结果,处理任务没有新的返回结果
public class Test {

    public static ExecutorService executor = Executors.newFixedThreadPool(10);

    public static void main(String[] args) {
        System.out.println("main方法...start");
        CompletableFuture<Integer> future = CompletableFuture.supplyAsync(() -> {
            System.out.println("当前线程1---" + Thread.currentThread().getName());
            int num = 10 / 2;
            System.out.println("运行结果1---" + num);
            return num;
        }, executor);

        CompletableFuture< Integer> future1 = CompletableFuture.supplyAsync(() -> {
            try {
                Thread.sleep(3000);
                System.out.println("当前线程1---" + Thread.currentThread().getName());
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            int num = 10 / 5;
            System.out.println("运行结果1---" + num);
            return num;
        }, executor);

        future.runAfterEitherAsync(future1, () -> {
            System.out.println("任务3开始....");
        }, executor);


        System.out.println("main方法...end");
    }

}
  • public CompletableFuture acceptEither(CompletionStage<? extends T> other, Consumer<? super T> action)
  • public CompletableFuture acceptEitherAsync(CompletionStage<? extends T> other, Consumer<? super T> action)
  • public CompletableFuture acceptEitherAsync(CompletionStage<? extends T> other, Consumer<? super T> action,Executor executor) 两个任务有一个任务执行完,获取它的返回结果,处理任务没有新的返回结果
public class Test {

    public static ExecutorService executor = Executors.newFixedThreadPool(10);

    public static void main(String[] args) {
        System.out.println("main方法...start");
        CompletableFuture<Integer> future = CompletableFuture.supplyAsync(() -> {
            System.out.println("当前线程1---" + Thread.currentThread().getName());
            int num = 10 / 2;
            System.out.println("运行结果1---" + num);
            return num;
        }, executor);

        CompletableFuture<Integer> future1 = CompletableFuture.supplyAsync(() -> {
            try {
                Thread.sleep(3000);
                System.out.println("当前线程1---" + Thread.currentThread().getName());
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            int num = 10 / 5;
            System.out.println("运行结果1---" + num);
            return num;
        }, executor);

        future.acceptEitherAsync(future1, (res) -> {
            System.out.println("任务返回结果" + res);
        }, executor);


        System.out.println("main方法...end");
    }

}
  • public CompletableFuture applyToEither(CompletionStage<? extends T> other, Function<? super T, U> fn)
  • public CompletableFuture applyToEitherAsync(CompletionStage<? extends T> other, Function<? super T, U> fn)
  • public CompletableFuture applyToEitherAsync(CompletionStage<? extends T> other, Function<? super T, U> fn,Executor executor) 两个任务有一个任务执行完,获取它的返回结果,处理任务并返回新的返回结果
public class Test {

    public static ExecutorService executor = Executors.newFixedThreadPool(10);

    public static void main(String[] args) {
        System.out.println("main方法...start");
        CompletableFuture<Integer> future = CompletableFuture.supplyAsync(() -> {
            System.out.println("当前线程1---" + Thread.currentThread().getName());
            int num = 10 / 2;
            System.out.println("运行结果1---" + num);
            return num;
        }, executor);

        CompletableFuture<Integer> future1 = CompletableFuture.supplyAsync(() -> {
            try {
                Thread.sleep(3000);
                System.out.println("当前线程1---" + Thread.currentThread().getName());
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            int num = 10 / 5;
            System.out.println("运行结果1---" + num);
            return num;
        }, executor);

        CompletableFuture<Integer> future2 = future.applyToEitherAsync(future1, (res) -> {
            System.out.println("任务返回结果" + res);
            return res * 2;
        }, executor);

        Integer integer = 0;
        try {
            integer = future2.get();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        }

        System.out.println("main方法...end" + integer);
    }

}

多任务组合

  • public static CompletableFuture allOf(CompletableFuture<?>... cfs) 等待所有结果完成
public class Test {

    public static ExecutorService executor = Executors.newFixedThreadPool(10);

    public static void main(String[] args) {
        System.out.println("main方法...start");
        CompletableFuture<Integer> future = CompletableFuture.supplyAsync(() -> {
            System.out.println("当前线程1---" + Thread.currentThread().getName());
            int num = 10 / 2;
            System.out.println("运行结果1---" + num);
            return num;
        }, executor);

        CompletableFuture<Integer> future1 = CompletableFuture.supplyAsync(() -> {
            try {
                Thread.sleep(3000);
                System.out.println("当前线程1---" + Thread.currentThread().getName());
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            int num = 10 / 5;
            System.out.println("运行结果1---" + num);
            return num;
        }, executor);


        CompletableFuture<Void> allOf = CompletableFuture.allOf(future, future1);
        try {
//            allOf.get();
            System.out.println("第一个返回结果" + future.get() + "第二个返回结果" + future1.get());
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        }

    }

}
  • public static CompletableFuture anyOf(CompletableFuture<?>... cfs) 一个任务完成即可
    public class Test {
    
        public static ExecutorService executor = Executors.newFixedThreadPool(10);
    
        public static void main(String[] args) {
            System.out.println("main方法...start");
            CompletableFuture<Integer> future = CompletableFuture.supplyAsync(() -> {
                System.out.println("当前线程1---" + Thread.currentThread().getName());
                int num = 10 / 2;
                System.out.println("运行结果1---" + num);
                return num;
            }, executor);
    
            CompletableFuture<Integer> future1 = CompletableFuture.supplyAsync(() -> {
                try {
                    Thread.sleep(3000);
                    System.out.println("当前线程2---" + Thread.currentThread().getName());
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                int num = 10 / 5;
                System.out.println("运行结果2---" + num);
                return num;
            }, executor);
    
    
            CompletableFuture<Object> anyOf = CompletableFuture.anyOf(future, future1);
            try {
                anyOf.get();
                System.out.println("返回结果..."); //此处打印先于  当前线程2---
            } catch (InterruptedException e) {
                e.printStackTrace();
            } catch (ExecutionException e) {
                e.printStackTrace();
            }
    
        }
    
    }