Bootstrap

JUC高并发编程学习2

===================== Callable ===============================

创建线程多种方式

  1. 继承Thread类
  2. 实现Runnable接口
  3. 实现Callable接口
  4. 线程池方式

Runnable接口和Callable接口区别

  1. 是否有返回值
  2. 是否抛出异常
  3. 实现名称不同,一个是run 一个是call
@FunctionalInterface
public interface Runnable {
    /**
     * When an object implementing interface <code>Runnable</code> is used
     * to create a thread, starting the thread causes the object's
     * <code>run</code> method to be called in that separately executing
     * thread.
     * <p>
     * The general contract of the method <code>run</code> is that it may
     * take any action whatsoever.
     *
     * @see     java.lang.Thread#run()
     */
    public abstract void run();
}
@FunctionalInterface
public interface Callable<V> {
    /**
     * Computes a result, or throws an exception if unable to do so.
     *
     * @return computed result
     * @throws Exception if unable to compute a result
     */
    V call() throws Exception;
}

Callable创建线程

Runnable接口有实现类FutureTask
FutureTask构造可以传递Callable

package com.example.juclearn.callable;

import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;

class MyThread1 implements Runnable {

    @Override
    public void run() {
        System.out.println("实现Runnable接口创建线程。。。");
    }
}

class MyThread2 implements Callable {

    @Override
    public Integer call() throws Exception {
        return 200;
    }
}
public class CallableDemo {

    public static void main(String[] args) {
        new Thread(new MyThread1(),"A").start();
//      类实现
        FutureTask futureTask1 = new FutureTask<>(new MyThread2());
//        lambda实现
        FutureTask<Integer> futureTask2 = new FutureTask<>(() -> {
            return 1024;
        });

        new Thread(futureTask1,"B").start();
//        任务是否完成 完成之后才会得到结果
        while (!futureTask1.isDone()) {
            System.out.println("wait..");
        }
        try {
            System.out.println(futureTask1.get());
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        }

    }
}

======================= JUC强大的辅助类 ==== ====================

减少计数CountDownLatch

实现功能,教室中6个人都离开后班长锁门

package com.example.juclearn.juchelpclass;

import java.util.concurrent.CountDownLatch;

/**
 * 教室中的6个人都离开时,班长锁门
 */

public class CountDownLatchDemo {
    public static void main(String[] args) {
//        共有6个人,创建对象的参数的阈值为6
        CountDownLatch countDownLatch = new CountDownLatch(6);
//        6个人创建6个线程
        for (int i = 0; i < 6; i++) {
            new Thread(()->{
                System.out.println(Thread.currentThread().getName() + " 离开了教室。。。");
//                每离开一个人 数量减一
                countDownLatch.countDown();
            },String.valueOf(i+1)).start();
        }
        try {
//            当数值没有达到0时,一直等待
            countDownLatch.await();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("班长锁门。。。。");
    }
}

循环栅栏CyclicBarrier

实现功能:集齐7个龙珠可以召唤神龙

package com.example.juclearn.juchelpclass;

import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier;

/**
 * 集齐7个龙珠可以召唤神龙
 */
public class CyclicBarrierDemo {
//    设置阈值
    public static final int NUMBER = 7;
    public static void main(String[] args) {
        CyclicBarrier cyclicBarrier =
                new CyclicBarrier(NUMBER,()->{
                    System.out.println("----集齐7颗龙珠 召唤神龙-----");
                });
        for (int i = 0; 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+1)).start();
        }

    }
}

信号灯Semaphore

实现功能:6辆车 3个停车位

package com.example.juclearn.juchelpclass;

import java.util.Random;
import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeUnit;

/**
 * 6辆车 3个车位
 */
public class SemaphoreDemo {
    public static void main(String[] args) {
//        参数为许可数量 这里的停车位数量
        Semaphore semaphore = new Semaphore(3);
//        设置6辆车
        for (int i = 0; i < 6; i++) {
            new Thread(() -> {
                try {
//                    抢占
                    semaphore.acquire();
                    System.out.println(Thread.currentThread().getName() + " 抢到了车位。。。");
//                  停留时间
                    TimeUnit.SECONDS.sleep(new Random().nextInt(5));
                    System.out.println(Thread.currentThread().getName() + "---离开了车位---");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } finally {
//                    释放
                    semaphore.release();
                }
            }, String.valueOf(i + 1)).start();

        }
    }
}

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

  • 无锁情况:多线程抢夺资源,较乱
  • 添加锁:synchronized和lock,独占锁,每次只能一个线程操作
  • 读写锁:ReentrantReadWriteLock,读读时可以共享,提升性能。但是会造成锁饥饿一直读没有写操作;读时不能写,只有写操作时可以读。

降级锁

package com.example.juclearn.readwritelock;

import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;

/**
 * 锁降级顺序
 */
public class LockDecr {
    public static void main(String[] args) {
        ReadWriteLock readWriteLock = new ReentrantReadWriteLock();
//        获取写锁
        readWriteLock.writeLock().lock();
//        获取读锁
        readWriteLock.readLock().lock();
//        释放写锁
        readWriteLock.writeLock().unlock();
//        释放读锁
        readWriteLock.readLock().unlock();
    }
}

=========================== 阻塞队列 ===========================

介绍

  • 当队列为空,从队列中获取元素的操作将会被阻塞
  • 当队列已满,向队列中添加元素的操作将会被阻塞
  • 试图从空的队列中获取元素的线程将会被阻塞,直到其他线程往空的队列插入新的元素
  • 试图向已满的队列中添加元素将会被阻塞,直到其他线程从队列中移除一个或者多个元素,是队列变得空闲
    在这里插入图片描述

分类

在这里插入图片描述

  • ArrayBlockingQueue:数组结构组成的有界阻塞队列,用于生产者消费者模式
  • LinkedBlockingQueue:链表结构组成的有界阻塞队列,用于生产者消费者模式
  • DelayQueue:使用优先级队列实现的延迟无界阻塞队列
  • PriorityBlockingQueue:支持优先级排序的无阻塞队列
  • SynchronousQueue
  • LinkedTransferQueue
  • LinkedBlockingDeque

在这里插入图片描述

package com.example.juclearn.blockingqueue;

import java.util.concurrent.ArrayBlockingQueue;

public class ArrayBlockingQueueDemo {
    public static void main(String[] args) {
        ArrayBlockingQueue<String> arrayBlockingQueue = new ArrayBlockingQueue<String>(3);

        arrayBlockingQueue.add("1");
        arrayBlockingQueue.add("2");
        arrayBlockingQueue.add("3");
//       add方法添加元素多余容量时抛出异常 Exception in thread "main" java.lang.IllegalStateException: Queue full
//        arrayBlockingQueue.add("4");

        arrayBlockingQueue.remove();
        arrayBlockingQueue.remove();
        arrayBlockingQueue.remove();
//        remove方法移除元素多余队列中元素个数时会抛出异常 Exception in thread "main" java.util.NoSuchElementException
//        arrayBlockingQueue.remove();
//        返回队列首元素 如果没有元素则抛出异常 Exception in thread "main" java.util.NoSuchElementException
        System.out.println(arrayBlockingQueue.element());
    }
}

package com.example.juclearn.blockingqueue;

import java.util.concurrent.ArrayBlockingQueue;

public class ArrayBlockingQueueDemo1 {
    public static void main(String[] args) {
        ArrayBlockingQueue<String> arrayBlockingQueue = new ArrayBlockingQueue<String>(3);
//      插入成功返回true
        System.out.println(arrayBlockingQueue.offer("1"));
        System.out.println(arrayBlockingQueue.offer("2"));
        System.out.println(arrayBlockingQueue.offer("3"));
//       插入失败返回false 没有抛出异常
        System.out.println(arrayBlockingQueue.offer("4"));

        //移除成功返回移除的值
        System.out.println(arrayBlockingQueue.poll());
        System.out.println(arrayBlockingQueue.poll());
        System.out.println(arrayBlockingQueue.poll());
//        队列为空时移除的值为null
        System.out.println(arrayBlockingQueue.poll());

//        返回队列首元素 如果没有元素则返回null
        System.out.println(arrayBlockingQueue.peek());
    }
}

package com.example.juclearn.blockingqueue;

import java.util.concurrent.ArrayBlockingQueue;

public class ArrayBlockingQueueDemo2 {
    public static void main(String[] args) throws InterruptedException {
        ArrayBlockingQueue<String> arrayBlockingQueue = new ArrayBlockingQueue<String>(3);
//      插入成功返回true
        arrayBlockingQueue.put("1");
        arrayBlockingQueue.put("2");
        arrayBlockingQueue.put("3");
        //       无法插入处于阻塞状态
//        arrayBlockingQueue.put("4");


        //移除成功返回移除的值
        System.out.println(arrayBlockingQueue.take());
        System.out.println(arrayBlockingQueue.take());
        System.out.println(arrayBlockingQueue.take());
//        队列为空时移除的处于阻塞状态
        System.out.println(arrayBlockingQueue.take());


    }
}

package com.example.juclearn.blockingqueue;

import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.TimeUnit;

public class ArrayBlockingQueueDemo3 {
    public static void main(String[] args) throws InterruptedException {
        ArrayBlockingQueue<String> arrayBlockingQueue = new ArrayBlockingQueue<String>(3);
//      插入成功返回true
        System.out.println(arrayBlockingQueue.offer("1",3, TimeUnit.SECONDS));
        System.out.println(arrayBlockingQueue.offer("2",3, TimeUnit.SECONDS));
        System.out.println(arrayBlockingQueue.offer("3",3, TimeUnit.SECONDS));
//       插入失败返回false 阻塞时间设置为3s
        System.out.println(arrayBlockingQueue.offer("4",3, TimeUnit.SECONDS));

        //移除成功返回移除的值
        System.out.println(arrayBlockingQueue.poll(3, TimeUnit.SECONDS));
        System.out.println(arrayBlockingQueue.poll(3, TimeUnit.SECONDS));
        System.out.println(arrayBlockingQueue.poll(3, TimeUnit.SECONDS));
//        队列为空时移除的值为null,阻塞时间设置为3s
        System.out.println(arrayBlockingQueue.poll(3, TimeUnit.SECONDS));


    }
}

=========================== 线程池 ===============================

分类

  1. 一池N线程
  2. 一池一线程
  3. 一池可扩容线程

一池一线程 无论多少个人办理业务,都只有一个线程处理

package com.example.juclearn.threadpool;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class ThreadPoolDemo {
    public static void main(String[] args) {
//        一池一线程
        ExecutorService threadPool1 = Executors.newSingleThreadExecutor();//相当于银行只有一个窗口
//        一池N线程 参数为N的数量
        ExecutorService threadPool2 = Executors.newFixedThreadPool(5);//相当于银行有五个窗口
//        一池可扩容线程
        ExecutorService threadPool3 = Executors.newCachedThreadPool();//相当于银行随机开放窗口

        try{
            for (int i = 0; i < 10; i++) {
                threadPool1.execute(()->{
                    System.out.println(Thread.currentThread().getName()+"办理业务");
                });
            }
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            threadPool1.shutdown();
        }


    }
}

在这里插入图片描述
最多有5个线程处理业务

package com.example.juclearn.threadpool;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class ThreadPoolDemo {
    public static void main(String[] args) {
//        一池一线程
        ExecutorService threadPool1 = Executors.newSingleThreadExecutor();//相当于银行只有一个窗口
//        一池N线程 参数为N的数量
        ExecutorService threadPool2 = Executors.newFixedThreadPool(5);//相当于银行有五个窗口
//        一池可扩容线程
        ExecutorService threadPool3 = Executors.newCachedThreadPool();//相当于银行随机开放窗口

        try{
            for (int i = 0; i < 10; i++) {
                threadPool2.execute(()->{
                    System.out.println(Thread.currentThread().getName()+"办理业务");
                });
            }
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            threadPool2.shutdown();
        }


    }
}

在这里插入图片描述

package com.example.juclearn.threadpool;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class ThreadPoolDemo {
    public static void main(String[] args) {
//        一池一线程
        ExecutorService threadPool1 = Executors.newSingleThreadExecutor();//相当于银行只有一个窗口
//        一池N线程 参数为N的数量
        ExecutorService threadPool2 = Executors.newFixedThreadPool(5);//相当于银行有五个窗口
//        一池可扩容线程
        ExecutorService threadPool3 = Executors.newCachedThreadPool();//相当于银行随机开放窗口

        try{
            for (int i = 0; i < 10; i++) {
                threadPool3.execute(()->{
                    System.out.println(Thread.currentThread().getName()+"办理业务");
                });
            }
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            threadPool3.shutdown();
        }
    }
}

在这里插入图片描述

参数解释

    public ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue<Runnable> workQueue) {
        this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
             Executors.defaultThreadFactory(), defaultHandler);
    }

在这里插入图片描述

底层工作流程

在这里插入图片描述

拒绝策略

在这里插入图片描述

自定义线程池

线程池不允许使用Excutors去创建,而是要通过ThreadPoolExecutor的方式创建,这样的处理方式让写的同学更加明确线程池的运行规则,规避资源耗尽的风险。
Executors返回线程池对象的弊端如下:

  • FixedThreadPool 和SingleThreadPool允许的请求队列长度是Integer.MAX_VALUE,可能堆积大量请求,导致OOM
  • CachedThreadPool允许创建线程的数量是Integer.MAX_VALUE,导致OOM
package com.example.juclearn.threadpool;

import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

public class UserDefineThreadPool {
    public static void main(String[] args) {
        ThreadPoolExecutor threadPool = new ThreadPoolExecutor(
                2,
                5,
                2,
                TimeUnit.SECONDS,
                new ArrayBlockingQueue<>(3),
                Executors.defaultThreadFactory(),
                new ThreadPoolExecutor.AbortPolicy()
        );

        try{
            for (int i = 0; i < 10; i++) {
                threadPool.execute(()->{
                    System.out.println(Thread.currentThread().getName()+"办理业务");
                });
            }
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            threadPool.shutdown();
        }
    }
}

每次执行结果都不同。
在这里插入图片描述

在这里插入图片描述

========================= ForkJoin 框架 ======================

简介

Fork/Join 可以将一个大的任务拆分成多个子任务进行并行处理,最后将子任务结果合并成最后的计算结果,并进行输出。
Fork:把一个复杂的任务进行拆分,大事化小
Join:将分拆任务的结果进行合并

实例

实现1+2+3+4+…+100,我们将差值不超过10的部分进行相加,最后合并

package com.example.juclearn.forkjoin;

import java.util.concurrent.ExecutionException;
import java.util.concurrent.ForkJoinPool;
import java.util.concurrent.ForkJoinTask;
import java.util.concurrent.RecursiveTask;

class MyTask extends RecursiveTask<Integer> {

//    计算差值不超过10的和
    private static final int CHA = 10;
//    拆分开始值
    private int start;
//    拆分结束值
    private int end;
//    得到的结果
    private int result;

    public MyTask(int start, int end) {
        this.start = start;
        this.end = end;
    }

    /**
     * 拆分合并过程
     * @return
     */
    @Override
    protected Integer compute() {
        MyTask myTask = new MyTask(start,end);
//        差值是否小于10
        if (end - start <=10) {
//            相加
            for (int i = start; i <= end ; i++) {
                result += i;
            }
        }else {
//            计算中间值
            int middle = (start + end)/2;
//            任务拆分 左边
            MyTask myTask1 = new MyTask(start, middle);
//            任务拆分 右边
            MyTask myTask2 = new MyTask(middle + 1, end);
//            调用拆分方法
            myTask1.fork();
            myTask2.fork();
//            拆分后的任务合并
            result = myTask1.join() + myTask2.join();
        }
        return result;
    }
}
public class ForkJoinDemo {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        MyTask myTask = new MyTask(0, 100);
        ForkJoinPool forkJoinPool = new ForkJoinPool();
        ForkJoinTask<Integer> submit = forkJoinPool.submit(myTask);
        Integer result = submit.get();

        System.out.println(result);
        forkJoinPool.shutdown();
    }
}

========================= JUC异步回调 ============================

异步:当我去教室找人,发现不在时,转告其他同学如果他回来的话告诉我,我继续忙我其他的事情

package com.example.juclearn.completablefuture;

import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;

public class CompletableFutureDemo {
    public static void main(String[] args) {
//        异步回调 没有返回值
        CompletableFuture<Void> completableFuture1 = CompletableFuture.runAsync(() -> {
            System.out.println(Thread.currentThread().getName() + " completableFuture1");
        });
        try {
            completableFuture1.get();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        }
//        异步回调 有返回值
        CompletableFuture<Integer> completableFuture2 = CompletableFuture.supplyAsync(() -> {
            System.out.println(Thread.currentThread().getName() + " completableFuture2");
            return 1024;
        });
/**
 * 当完成时输出内容
 */
        completableFuture2.whenComplete((t,u)->{
//            方法返回值
            System.out.println("t=" + t);
//            异常情况,没有返回null
            System.out.println("u=" + u);

        });
    }
}

;