JUC并发编程学习笔记(一)

1. 什么是JUC

java.util.concurrent
java.util.concurrent.locks
业务:普通的线程代码Thread
Runnable:没有返回值,效率相较于Callable较低.

2. 线程与进程

进程:一个程序的集合;一个进程可以包含至少一个或多个线程;
Java默认有几个线程?2个!一个main线程,一个GC线程。
线程:

Java真的可以开启线程吗?

答:不可以。
原因是Java没有权限开启一个线程,Java是通过native本地C++的方法调用来开启一个线程

public synchronized void start() {
        if (threadStatus != 0)
            throw new IllegalThreadStateException();


        group.add(this);

        boolean started = false;
        try {
            start0();
            started = true;
        } finally {
            try {
                if (!started) {
                    group.threadStartFailed(this);
                }
            } catch (Throwable ignore) {

            }
        }
    }
    //本地方法,调用C++
    private native void start0();

并发、并行

并发(多线程操作同一个资源)
CPU单核,通过快速交换,模拟出来多个线程。
并行,多核CPU多个线程同时执行,线程池

public class Test01 {
    public static void main(String[] args){
        // 获取CPU的核心数,
        //cpu密集型,io密集型
        System.out.println(Runtime.getRuntime().availableProcessors());
    }
}

并发编程的本质:充分利用CPU资源

多线程

线程有几个状态

 public enum State {
        //线程创建
        NEW,
        // 运行
        RUNNABLE,
        //阻塞
        BLOCKED,
        //等待
        WAITING,
        //超时等待
        TIMED_WAITING,
        //终止
        TERMINATED;
    }

wait和sleep的区别

  1. 来自不同的类: wait()来自Object类。sleep()来自Thread类。
  2. 关于锁的释放:wait()会释放锁,而sleep()不会释放锁。<记忆:因为睡着了不会释放锁>
  3. 使用的范围是不同的:wait必须在同步代码块中使用;sleep可以在任意地方使用。
  4. 是否需要捕获异常:wait()不需要捕获异常,而sleep需要捕获异常

3. Lock锁(!important)

传统的synchronized,解决并发中可能存在的问题

本质:队列,锁
锁:锁对象,锁class

Lock接口

lock()加锁;unlock()解锁;
实现类:

  1. ReentrantLock 可重入锁(常用)
  2. ReadLock 读锁
  3. WriteLock 写锁
    在这里插入图片描述
    公平锁:十分公平,先来后到
    非公平锁:不公平的,可以插队(默认)

使用Lock锁的三步骤

  1. Lock lock = new ReentrantLock();
  2. lock.lock(); //加锁
  3. 在finally中使用lock.unlock()解锁

synchronized与lock的区别

  1. Synchronized 是Java内置的关键字,Lock 是一个Java类
  2. Synchronized 无法判断获取锁的状态,Lock可以判断是否获取到了锁
  3. Synchronized 会自动释放锁,而lock锁必须手动释放锁,若不释放锁则会发生死锁
  4. Synchronized 线程1(获得锁)、线程2(等待);若线程1发生阻塞,则线程2将一直等待。而lock锁则不一定会一直等待。
  5. Synchronized:可重入锁,不可中断,非公平锁。Lock:可重入锁,可以判断锁,非公平(可以自己设置)
  6. Synchronized:适合锁少量的代码同步问题,Lock适合锁大量的代码同步问题。

锁是什么?如何判断锁的是谁?

4. 生产者消费者问题

Synchronized写法

package com.huathy.demo02_productConsumer;

/**
 * @ClassPath: com.huathy.demo02_productConsumer.A
 * @Author: Huathy
 * @Description:
 * @Date: 2021-01-07 20:13
 */

/**
 * 线程之间的通信问题:生产者与消费者问题
 * 线程交替执行,A   B  操作同一个变量  num=0
 * A   num ++
 * B   num --
 */
public class A {
    public static void main(String[] args) {
        Data data = new Data();

        new Thread( ()->{
            try {
                for (int i = 0; i < 20; i++) {
                    data.increment();
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        },"A" ).start();

        new Thread( ()->{
            try {
                for (int i = 0; i < 20; i++) {
                    data.decrement();
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        },"B" ).start();

    }
}

/**
 * 数字:资源类
 * 判断等待,业务,通知
 */
class Data{

    private int num = 0;

    public synchronized void increment() throws InterruptedException {
        //这里写if判断会存在什么问题?虚假唤醒
        if( num != 0 ){
            //等待
            this.wait();
        }
        num ++;
        System.out.println(Thread.currentThread().getName() + " ==> " +num);
        //通知其他线程
        this.notifyAll();
    }

    public synchronized void decrement() throws InterruptedException {
        if( num == 0 ){
            //等待
            this.wait();
        }
        num --;
        System.out.println(Thread.currentThread().getName() + " ==> " +num);
        //通知其他线程
        this.notifyAll();
    }
}

虚假唤醒

当一个条件满足时,很多线程都被唤醒,但只有其中部分是有用的唤醒。
问题:存在A B C D四个线程,虚假唤醒!
在这里插入图片描述

解决方法:将上面代码中的if判断改为while判断

public class A {
    public static void main(String[] args) {
        Data data = new Data();

        new Thread( ()->{
            try {
                for (int i = 0; i < 20; i++) {
                    data.increment();
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        },"A" ).start();

        new Thread( ()->{
            try {
                for (int i = 0; i < 20; i++) {
                    data.decrement();
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        },"B" ).start();

    }
}

/**
 * 数字:资源类
 * 判断等待,业务,通知
 */
class Data{

    private int num = 0;

    public synchronized void increment() throws InterruptedException {
        //这里写if判断会存在什么问题?虚假唤醒
        while ( num != 0 ){
            //等待
            this.wait();
        }
        num ++;
        System.out.println(Thread.currentThread().getName() + " ==> " +num);
        //通知其他线程
        this.notifyAll();
    }

    public synchronized void decrement() throws InterruptedException {
        while ( num == 0 ){
            //等待
            this.wait();
        }
        num --;
        System.out.println(Thread.currentThread().getName() + " ==> " +num);
        //通知其他线程
        this.notifyAll();
    }
}

JUC写法的生产者与消费者问题

通过Lock 找到 Condition
在这里插入图片描述
condition.await(); //等待
condition.signalAll(); //唤醒全部

package com.huathy.demo02_productConsumer;

import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

/**
 * @ClassPath: com.huathy.demo02_productConsumer.B
 * @Author: Huathy
 * @Description:
 * @Date: 2021-01-12 09:21
 */
public class B {
    public static void main(String[] args) {
        Data2 data = new Data2();

        new Thread( ()->{
            try {
                for (int i = 0; i < 20; i++) {
                    data.increment();
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        },"A" ).start();

        new Thread( ()->{
            try {
                for (int i = 0; i < 20; i++) {
                    data.decrement();
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        },"B" ).start();

        new Thread( ()->{
            try {
                for (int i = 0; i < 20; i++) {
                    data.increment();
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        },"C" ).start();

        new Thread( ()->{
            try {
                for (int i = 0; i < 20; i++) {
                    data.decrement();
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        },"D" ).start();
    }
}

/**
 * 数字:资源类
 * 判断等待,业务,通知
 */
class Data2 {

    private int num = 0;

    Lock lock = new ReentrantLock();
    Condition condition = lock.newCondition();
//    condition.await();        //等待
//    condition.signalAll();    //唤醒全部

    public void increment() throws InterruptedException {
        lock.lock();
        try {
            while (num != 0) {
                //等待
                condition.await();
            }
            num++;
            System.out.println(Thread.currentThread().getName() + " ==> " + num);
            //通知其他线程
            condition.signalAll();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }

    public void decrement() throws InterruptedException {
       lock.lock();
       try {
           while (num == 0) {
               //等待
               condition.await();
           }
           num--;
           System.out.println(Thread.currentThread().getName() + " ==> " + num);
           //通知其他线程
           condition.signalAll();
       }catch (Exception e){
           e.printStackTrace();
       }finally {
           lock.unlock();
       }
    }

}

问题:现在的执行是随机的,如何做到精准的通知与唤醒线程?

Condition精准通知唤醒

使用多个监视器来实现精准的唤醒

package com.huathy.demo02_productConsumer;

import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

/**
 * @ClassPath: com.huathy.demo02_productConsumer.C
 * @Author: Huathy
 * @Description:
 * @Date: 2021-01-12 09:48
 */
public class C {
    public static void main(String[] args) {
        Data3 data = new Data3();

        new Thread( ()->{
            for (int i = 0; i < 10; i++) {
                data.printA();
            }
        } , "A" ).start();

        new Thread( ()->{
            for (int i = 0; i < 10; i++) {
                data.printB();
            }
        } , "B" ).start();

        new Thread( ()->{
            for (int i = 0; i < 10; i++) {
                data.printC();
            }
        } , "C" ).start();
    }

}

class Data3{
    Lock lock = new ReentrantLock();
    Condition condition1 = lock.newCondition();
    Condition condition2 = lock.newCondition();
    Condition condition3 = lock.newCondition();
    int num = 1;

    public void printA(){
        lock.lock();
        try {
            while (num != 1){
                condition1.await();
            }
            System.out.println(Thread.currentThread().getName() + "==> AAA");
            num = 2;
            condition2.signal();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }

    public void printB(){
        lock.lock();
        try {
            while (num != 2){
                condition2.await();
            }
            System.out.println(Thread.currentThread().getName() + "==> BBB");
            num = 3 ;
            condition3.signal();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }

    public void printC(){
        lock.lock();
        try {
            while (num != 3){
                condition3.await();
            }
            System.out.println(Thread.currentThread().getName() + "==> CCC");
            num = 1;
            condition1.signal();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }
}

5. 8锁现象

(为了理解什么是锁。称之为8锁现象)
如何判断锁?锁的是谁?

  1. Synchronized锁的对象是方法的调用者。
  1. 加了static的静态同步方法,锁的就是Class类模板。

6. 集合类不安全

List不安全

安全:CopyOnWriteArrayList

package com.huathy.unsafe;

import java.util.*;
import java.util.concurrent.CopyOnWriteArrayList;

/**
 * @ClassPath: com.huathy.unsafe.Test01
 * @Author: Huathy
 * @Description:
 * @Date: 2021-01-12 14:18
 */

// java.util.ConcurrentModificationException 并发修改异常!
public class Test01 {
    public static void main(String[] args) {
//        List<String> list = Arrays.asList("1","2","3","4");
//        list.forEach(System.out::println);

        /**
         * 在并发下ArrayList是不安全的
         * 解决方案:
         * 1. List<String> list1 = new Vector<>();
         * 2. List<String> list1 = Collections.synchronizedList(new ArrayList<>());
         * 3. List<String> list1 = new CopyOnWriteArrayList<>();
         * //建议使用这种JUC的写法
         * CopyOnWriteArrayList 比 Vector的效率要高
         * 因为Vector使用的是synchronized锁,而CopyOnWriteArrayList使用的是Lock锁
         */
        /**
         * CopyOnWrite  写入时复制,COW   是计算机程序设计领域的优化策略
         * 多个线程调用时,list在读取时是唯一的,在写入时可能会存在覆盖操作。
         * 在写入时,为避免覆盖造成数据问题。先进行覆盖。
         * 读写分离思想
         */

//        List<String> list1 = new ArrayList<>();
//        List<String> list1 = new Vector<>();
//        List<String> list1 = Collections.synchronizedList(new ArrayList<>());
        List<String> list1 = new CopyOnWriteArrayList<>();

        for (int i = 0; i < 10; i++) {
            new Thread( ()->{
                list1.add(UUID.randomUUID().toString().substring(0,5));
                System.out.println(list1);
            },String.valueOf(i) ).start();
        }
    }
}

Set不安全

package com.huathy.unsafe;

import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.CopyOnWriteArraySet;

/**
 * @ClassPath: com.huathy.unsafe.SetTest
 * @Author: Huathy
 * @Description:
 * @Date: 2021-01-12 14:55
 */

//java.util.ConcurrentModificationException 并发修改异常
public class SetTest {
    public static void main(String[] args) {

//        Set<String> set = new HashSet<>();
//        Set<String> set = Collections.synchronizedSet(new HashSet<>());
        Set<String> set = new CopyOnWriteArraySet<>();

        for (int i = 0; i < 20; i++) {
            new Thread( ()->{
                set.add( UUID.randomUUID().toString().substring(0,5) );
                System.out.println(set);
            } , String.valueOf(i) ).start();
        }
    }
}

HashSet底层原理

public HashSet() {
    map = new HashMap<>();
}

//add set本质就是map的key,因为map的key是不能重复的
public boolean add(E e) {
    return map.put(e, PRESENT)==null;
}

//常量
private static final Object PRESENT = new Object();

Map不安全

HashMap不安全
ConcurrentHashMap
在这里插入图片描述

package com.huathy.unsafe;

import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;

/**
 * @ClassPath: com.huathy.unsafe.MapTest
 * @Author: Huathy
 * @Description:
 * @Date: 2021-01-13 11:53
 */

/**
 * ConcurrentModificationException
 */
public class MapTest {
    public static void main(String[] args) {
        // map是这样用的嘛? 不是
        // 默认等价于什么?
//        Map<String , Object> map = new HashMap<>();
        //加载因子,初始容量

        //解决方案
        Map<String, Object> map = new ConcurrentHashMap<>();
        for (int i = 0; i < 30; i++) {
            new Thread( ()->{
                map.put(Thread.currentThread().getName(), UUID.randomUUID().toString().substring(0,5));
                System.out.println(map);
            } , String.valueOf(i) ).start();
        }
    }
}

7. Callable

类似于Runnable,都是为其实例可能由另一线程执行的类设计的。而Runnable不返回结果,也不能抛出被检查的异常。

  1. 可以有返回值
  2. 可以抛出异常
  3. 方法不同call()
package com.huathy.callable;

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

/**
 * @ClassPath: com.huathy.callable.CallableTest
 * @Author: Huathy
 * @Description:
 * @Date: 2021-01-13 14:09
 */
public class CallableTest {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
//        new Thread(new Runnable() {}).start();
//        new Thread(new FutureTask<>()).start();
//        new Thread(new FutureTask<>(Callable)).start();

        FutureTask futureTask = new FutureTask(new MyThread()); //适配类
        new Thread(futureTask,"A").start();
        new Thread(futureTask,"B").start();

        //获取Callable的返回结果
        //这个get方法可能会产生阻塞,所以放到最后。或者使用异步通信来处理
        Integer res = (Integer) futureTask.get();
        System.out.println(res);
    }
}

class MyThread implements Callable<Integer>{
    @Override
    public Integer call() throws Exception {
        System.out.println("call");
        return 1234;
    }
}

细节:1. 有缓存 2.结果可能需要等待,会阻塞。

8. 常用的辅助类

CountDownLatch

减法计数器

package com.huathy.auxiliary;

import java.util.concurrent.CountDownLatch;

/**
 * @ClassPath: com.huathy.auxiliary.CountDownLatch
 * @Author: Huathy
 * @Description:
 * @Date: 2021-01-13 16:50
 */
public class CountDownLatchTest {
    public static void main(String[] args) throws InterruptedException {
        //总数是6,在有必须要执行任务的时候使用
        CountDownLatch countDownLatch = new CountDownLatch(6);

        for (int i = 0; i < 6; i++) {
            new Thread( ()->{
                System.out.println(Thread.currentThread().getName() + " go out");
                countDownLatch.countDown();     //数量-1
            } , String.valueOf(i) ).start();
        }

        //等待计数器归零后继续向下执行
        countDownLatch.await();
        System.out.println("go end");
    }
}

原理:1. countDownLatch.countDown(); //数量-1
2. countDownLatch.await();
每次有线程调用countDown()数量-1,假设计数器数量变为0,countDownLatch.await()就会被唤醒,继续执行。

CyclicBarrier

加法计数器

package com.huathy.auxiliary;

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

/**
 * @ClassPath: com.huathy.auxiliary.CyclicBarrierTest
 * @Author: Huathy
 * @Description:
 * @Date: 2021-01-13 17:22
 */
public class CyclicBarrierTest {
    public static void main(String[] args) {

        CyclicBarrier cyclicBarrier = new CyclicBarrier(7, ()->{
            System.out.println("召唤神龙!");
        } );

        for (int i = 0; i < 7; i++) {
            final int temp = i;
            //lambda能拿到上面的i嘛? 不能。
            new Thread( ()->{
                System.out.println(Thread.currentThread().getName() + " 收集第 " + temp + " 龙珠");
                try {
                    cyclicBarrier.await();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } catch (BrokenBarrierException e) {
                    e.printStackTrace();
                }
            } ).start();
        }

    }
}

Semaphore

计数信号量。

package com.huathy.auxiliary;

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

/**
 * @ClassPath: com.huathy.auxiliary.SemaphoreTest
 * @Author: Huathy
 * @Description:
 * @Date: 2021-01-13 17:34
 */
public class SemaphoreTest {
    public static void main(String[] args) {
        //初始化:3个信号量。 使用场景:限流
        Semaphore semaphore = new Semaphore(3);

        for (int i = 0; i < 6; i++) {
            new Thread( ()->{
                try {
                    semaphore.acquire();    //获取
                    System.out.println(Thread.currentThread().getName() + " 获得信号量");
                    TimeUnit.SECONDS.sleep(3);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } finally {
                    System.out.println(Thread.currentThread().getName() + " 释放信号量");
                    semaphore.release();    //释放
                }
            },String.valueOf(i) ).start();
        }
    }
}

原理:

  1. semaphore.acquire(); //获取,假设已经满了,等待,等待被释放为止。
  2. semaphore.release(); //释放,会将当前的信号量释放,并+1.然后唤醒等待的线程。

使用场景:多个共享资源的互斥使用!并发限流,控制最大线程数量。

9. ReadWriteLock 读写锁

package com.huathy.rw;

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

/**
 * @ClassPath: com.huathy.rw.ReadWriteLockTest
 * @Author: Huathy
 * @Description:
 * @Date: 2021-01-13 17:48
 */

/**
 * 共享锁:多个线程可以同时占有
 * 排他锁:一次只能被一个线程占有
 * readWriteLock
 * 读-读  共享锁
 * 读-写  排他锁
 * 写-写  排他锁
 */
public class ReadWriteLockTest {
    public static void main(String[] args) {
        MyCatchLock myCatch = new MyCatchLock();

        //写入
        for (int i = 0; i < 5; i++) {
            final int temp = i;
            new Thread( ()->{
                myCatch.put(temp+"",temp+"");
            } , String.valueOf(i) ).start();
        }

        //读取
        for (int i = 0; i < 5; i++) {
            final int temp = i;
            new Thread( ()->{
                myCatch.get(temp+"");
            } , String.valueOf(i) ).start();
        }
    }
}

/**
 * 自定义缓存
 * 加锁的
 */
class MyCatchLock{

    private volatile Map<Object,Object> map = new HashMap<>();
    //读写锁:更加细粒度的控制
    private ReadWriteLock readWriteLock = new ReentrantReadWriteLock();

    //存  写  只希望一个线程操作
    public void put(String key,Object val){
        readWriteLock.writeLock().lock();
        try {
            System.out.println(Thread.currentThread().getName() + " 写入 " + key);
            map.put(key,val);
            System.out.println(Thread.currentThread().getName() + " 写入完毕");
        } finally {
            readWriteLock.writeLock().unlock();
        }
    }

    //取  读
    public Object get(String key){
        System.out.println(Thread.currentThread().getName() + " 读取 " + key);
        return map.get(key);
    }

}

/**
 * 自定义缓存
 * 没有加锁
 */
class MyCatch{

    private volatile Map<Object,Object> map = new HashMap<>();

    //存  写
    public void put(String key,Object val){
        System.out.println(Thread.currentThread().getName() + " 写入 " + key);
        map.put(key,val);
        System.out.println(Thread.currentThread().getName() + " 写入完毕");
    }

    //取  读
    public Object get(String key){
        System.out.println(Thread.currentThread().getName() + " 读取 " + key);
        return map.get(key);
    }

}

10. 阻塞队列

Queue 队列
Deque 双端队列
FIFO先进先出
写入:等待,阻塞
读取:if队列空,需要等待生产

阻塞队列:BlockingQueue

实现类

  1. SynchronousQueue:同步队列
  2. ArrayBlockingQueue
  3. LinkedBlockingQueue

在这里插入图片描述

使用队列

BlockingQueue的四组API

  1. 抛出异常
  2. 不抛异常
  3. 阻塞等待
  4. 超时等待
方式抛出异常不抛异常,有返回值阻塞等待超时等待
添加add()offer()put()offer()
移除remove()poll()take()poll()
判断队列首element()peek()--
package com.huathy.bq;

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

/**
 * @ClassPath: com.huathy.bq.Test01
 * @Author: Huathy
 * @Description:
 * @Date: 2021-01-13 19:46
 */
public class Test01 {
    public static void main(String[] args) throws InterruptedException {
//        test1();
//        test2();
//        test3();
        test4();
    }

    /**
     * 抛出异常
     */
    public static void test1(){
        ArrayBlockingQueue arrayBlockingQueue = new ArrayBlockingQueue<>(3);

        System.out.println(arrayBlockingQueue.add("a"));
        System.out.println(arrayBlockingQueue.add("b"));
        System.out.println(arrayBlockingQueue.add("c"));

        //已满再加:java.lang.IllegalStateException: Queue full
//        System.out.println(arrayBlockingQueue.add("d"));
        System.out.println("========================");

        System.out.println(arrayBlockingQueue.element());
        System.out.println(arrayBlockingQueue.remove());
        System.out.println(arrayBlockingQueue.remove());
        System.out.println(arrayBlockingQueue.remove());

        //已经没有元素:java.util.NoSuchElementException
//        System.out.println(arrayBlockingQueue.element());

        //没有再取:java.util.NoSuchElementException
//        System.out.println(arrayBlockingQueue.remove());

    }

    /**
     * 不抛异常,有返回值
     */
    public static void test2(){
        ArrayBlockingQueue arrayBlockingQueue = new ArrayBlockingQueue<>(3);

        System.out.println(arrayBlockingQueue.offer("a"));
        System.out.println(arrayBlockingQueue.offer("b"));
        System.out.println(arrayBlockingQueue.offer("c"));
        System.out.println(arrayBlockingQueue.offer("d"));

        System.out.println("======================");

        System.out.println("队首:" + arrayBlockingQueue.peek());
        System.out.println(arrayBlockingQueue.poll());
        System.out.println(arrayBlockingQueue.poll());
        System.out.println(arrayBlockingQueue.poll());
        System.out.println(arrayBlockingQueue.poll());
        System.out.println("队首:" + arrayBlockingQueue.peek());

    }

    /**
     * 阻塞等待
     * @throws InterruptedException
     */
    public static void test3() throws InterruptedException {
        ArrayBlockingQueue arrayBlockingQueue = new ArrayBlockingQueue(3);
        arrayBlockingQueue.put("a");
        arrayBlockingQueue.put("b");
        arrayBlockingQueue.put("c");

        //队列没有位置,则一直等待
//        arrayBlockingQueue.put("d");
        System.out.println(arrayBlockingQueue.take());
        System.out.println(arrayBlockingQueue.take());
        System.out.println(arrayBlockingQueue.take());

        //队列中没有元素,则一直等待
//        System.out.println(arrayBlockingQueue.take());
    }

    /**
     * 超时等待
     * @throws InterruptedException
     */
    public static void test4() throws InterruptedException {
        ArrayBlockingQueue arrayBlockingQueue = new ArrayBlockingQueue(3);

        arrayBlockingQueue.offer("a");
        arrayBlockingQueue.offer("b");
        arrayBlockingQueue.offer("c");

        //超时等待3s退出
        arrayBlockingQueue.offer("d",3, TimeUnit.SECONDS);

        System.out.println("================");

        System.out.println(arrayBlockingQueue.poll());
        System.out.println(arrayBlockingQueue.poll());
        System.out.println(arrayBlockingQueue.poll());

        System.out.println(arrayBlockingQueue.poll(3,TimeUnit.SECONDS));
    }
}

同步队列 SynchronousQueue

package com.huathy.bq;

/**
 * @ClassPath: com.huathy.bq.SynchronizedQueueDemo
 * @Author: Huathy
 * @Description:
 * @Date: 2021-01-14 09:41
 */

import java.util.concurrent.BlockingQueue;
import java.util.concurrent.SynchronousQueue;
import java.util.concurrent.TimeUnit;

/**
 * 同步队列
 * 和其他的BlockingQueue不一样,Synchronous不存储元素
 * put了一个元素,必须先从里面take出来,否则不能put。
 */
public class SynchronousQueueDemo {
    public static void main(String[] args) {
        BlockingQueue<String> synchronousQueue = new SynchronousQueue<>();

        new Thread( ()->{
            try {
                System.out.println(Thread.currentThread().getName() + " put 1");
                synchronousQueue.put("1");
                System.out.println(Thread.currentThread().getName() + " put 2");
                synchronousQueue.put("2");
                System.out.println(Thread.currentThread().getName() + " put 3");
                synchronousQueue.put("3");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        } ,"T1" ).start();

        new Thread( ()->{
            try {
                TimeUnit.SECONDS.sleep(3);
                System.out.println(Thread.currentThread().getName() + " " + synchronousQueue.take());
                TimeUnit.SECONDS.sleep(3);
                System.out.println(Thread.currentThread().getName() + " " + synchronousQueue.take());
                TimeUnit.SECONDS.sleep(3);
                System.out.println(Thread.currentThread().getName() + " " + synchronousQueue.take());
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        } ,"T2" ).start();
    }
}

11. 线程池 !

JUC线程池有:3大方法、7个参数、4种拒绝策略。

池化技术

程序的运行,会占用系统内资源。池化技术可以优化资源的使用。线程池,连接池,内存池,对象池。

线程池的好处:线程复用、最大并发数可控、管理线程

  1. 降低资源开销
  2. 提高相应速度
  3. 方便管理
    在这里插入图片描述

Executors

package com.huathy.poll;

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

/**
 * @ClassPath: com.huathy.poll.Demo01
 * @Author: Huathy
 * @Description:
 * @Date: 2021-01-14 15:29
 */
public class Demo01 {
    public static void main(String[] args) {
//        ExecutorService threadPool = Executors.newSingleThreadExecutor();//创建单个线程
//        ExecutorService threadPool = Executors.newFixedThreadPool(5);    //创建固定大小的线程池
        ExecutorService threadPool = Executors.newCachedThreadPool();        //创建可伸缩的线程池

        try {
            for (int i = 0; i < 50; i++) {
                threadPool.execute( ()->{
                    System.out.println(Thread.currentThread().getName() + " ok");
                } );
            }
        } finally {
            threadPool.shutdown();
        }

    }
}

7大参数

源码

public static ExecutorService newSingleThreadExecutor() {
    return new FinalizableDelegatedExecutorService
        (new ThreadPoolExecutor(1, 1,
                                0L, TimeUnit.MILLISECONDS,
                                new LinkedBlockingQueue<Runnable>()));
}
public static ExecutorService newFixedThreadPool(int nThreads) {
    return new ThreadPoolExecutor(nThreads, nThreads,
                                  0L, TimeUnit.MILLISECONDS,
                                  new LinkedBlockingQueue<Runnable>());
}
public static ExecutorService newCachedThreadPool() {
    return new ThreadPoolExecutor(0, Integer.MAX_VALUE, //21亿 OOM
                                  60L, TimeUnit.SECONDS,
                                  new SynchronousQueue<Runnable>());
}
本质是:new ThreadPoolExecutor
public ThreadPoolExecutor(int corePoolSize,         //核心线程数
                            int maximumPoolSize,    //最大核心线程池大小
                            long keepAliveTime,     //超时释放时间
                            TimeUnit unit,          //时间单位
                            BlockingQueue<Runnable> workQueue,  //阻塞队列
                            ThreadFactory threadFactory,        //线程工厂,创建线程的,一般不动
                            RejectedExecutionHandler handler    //拒绝策略
                            ) {
    if (corePoolSize < 0 ||
        maximumPoolSize <= 0 ||
        maximumPoolSize < corePoolSize ||
        keepAliveTime < 0)
        throw new IllegalArgumentException();
    if (workQueue == null || threadFactory == null || handler == null)
        throw new NullPointerException();
    this.acc = System.getSecurityManager() == null ?
            null :
            AccessController.getContext();
    this.corePoolSize = corePoolSize;
    this.maximumPoolSize = maximumPoolSize;
    this.workQueue = workQueue;
    this.keepAliveTime = unit.toNanos(keepAliveTime);
    this.threadFactory = threadFactory;
    this.handler = handler;
}

四种拒绝策略

在这里插入图片描述

ThreadPoolExecutor.AbortPolicy() //终止策略:拒绝服务,抛出异常
ThreadPoolExecutor.CallerRunsPolicy() //调用者运行策略:不抛异常
ThreadPoolExecutor.DiscardPolicy() //丢弃策略:不抛异常
ThreadPoolExecutor.DiscardOldestPolicy() //放弃最早的策略:尝试获取资源,不抛异常

自定义线程池

package com.huathy.poll;

import java.util.concurrent.*;

/**
 * @ClassPath: com.huathy.poll.Demo01
 * @Author: Huathy
 * @Description:
 * @Date: 2021-01-14 15:29
 */

/**
 * 四种拒绝策略:
 * new ThreadPoolExecutor.AbortPolicy()         //终止策略:拒绝服务,抛出异常
 * new ThreadPoolExecutor.CallerRunsPolicy()   //调用者运行策略:不抛异常
 * new ThreadPoolExecutor.DiscardPolicy()      //丢弃策略:不抛异常
 * new ThreadPoolExecutor.DiscardOldestPolicy()      //放弃最早的策略:尝试获取资源,不抛异常
 */
public class Demo02 {
    public static void main(String[] args) {
        ExecutorService executorService = new ThreadPoolExecutor(
                2,          //核心线程数
                5,      //最大核心线程池大小
                3,         //超时释放时间
                TimeUnit.SECONDS,       //时间单位
                new LinkedBlockingQueue<>(3),   //阻塞队列
                Executors.defaultThreadFactory(),       //线程工厂
//                new ThreadPoolExecutor.AbortPolicy()    //终止策略:拒绝服务,抛出异常
//                new ThreadPoolExecutor.CallerRunsPolicy()   //调用者运行策略:不抛异常
//                new ThreadPoolExecutor.DiscardPolicy()      //丢弃策略:不抛异常
                new ThreadPoolExecutor.DiscardOldestPolicy()      //放弃最早的策略:
        );

        try {
            //最大承载:Queue + max
            //如果超出承载,则抛出java.util.concurrent.RejectedExecutionException
            for (int i = 1; i <= 100; i++) {
                executorService.execute( ()->{
                    System.out.println(Thread.currentThread().getName() + " ok");
                } );
            }
        } finally {
            executorService.shutdown();
        }

    }
}

小结 & 扩展

最大线程数量到底如何定义:

  1. CPU密集型:几核心就定义多少。这样可以保持CPU效率最高。
  2. IO密集型:一个程序有n个大型任务,io消耗资源。则最大数可以定义比n大。可以是2倍。

12. 四大函数式接口 !

lambda表达式、链式编程、函数式接口、Stream流式计算。
FunctionInterface 函数式接口:只有一个方法的接口。
简化编程模型,在新版底层中大量使用。foreach(消费者类的函数式接口)

  1. Consumer:
  2. Function:函数式接口。参数:一个接收类型,一个返回类型。
  3. Predicate:断定型接口。参数:一个接收类型,返回Boolean值。
  4. Supplier:

代码示例:

  1. Function:函数式接口
package com.huathy.function;

import java.util.function.Function;

/**
 * @ClassPath: com.huathy.function.FunctionDemo
 * @Author: Huathy
 * @Description:
 * @Date: 2021-01-14 20:13
 */

/**
 * 函数式接口
 * 参数:一个输入类型,一个返回类型。
 */
public class FunctionDemo {
    public static void main(String[] args) {
//        Function function = new Function<Integer, String>() {
//            @Override
//            public String apply(Integer integer) {
//                return (++ integer).toString();
//            }
//        };

        Function<Integer,String> function = (i)->{return (++ i).toString();};

        System.out.println(function.apply(1));
    }
}
  1. PredicateDemo:断定型接口
package com.huathy.function;

import java.util.function.Predicate;

/**
 * @ClassPath: com.huathy.function.PredicateDemo
 * @Author: Huathy
 * @Description:
 * @Date: 2021-01-14 20:20
 */

/**
 * 断定型接口。
 * 参数:一个接收类型。返回Boolean值
 */
public class PredicateDemo {
    public static void main(String[] args) {
//        Predicate<Integer> predicate = new Predicate<Integer>(){
//            @Override
//            public boolean test(Integer i) {
//                return i > 0;
//            }
//        };

        Predicate<Integer> predicate = (i) -> { return i>0; };

        System.out.println(predicate.test(2));
    }
}
  1. Consumer消费型接口
package com.huathy.function;

import java.util.function.Consumer;

/**
 * @ClassPath: com.huathy.function.ConsumerDemo
 * @Author: Huathy
 * @Description:
 * @Date: 2021-01-14 20:34
 */

/**
 * 消费型接口
 */
public class ConsumerDemo {
    public static void main(String[] args) {
//        Consumer<Object> consumer = new Consumer<Object>(){
//            @Override
//            public void accept(Object o) {
//                System.out.println(o);
//            }
//        };

        Consumer<Object> consumer = (o) -> {
            System.out.println(o);
        };

        consumer.accept(123);
    }
}
  1. Supplier供给型接口
package com.huathy.function;

import java.util.function.Supplier;

/**
 * @ClassPath: com.huathy.function.SupplierDemo
 * @Author: Huathy
 * @Description:
 * @Date: 2021-01-14 20:38
 */

/**
 * 供给型接口
 */
public class SupplierDemo {
    public static void main(String[] args) {
//        Supplier<Integer> supplier = new Supplier<Integer>() {
//            @Override
//            public Integer get() {
//                return 1024;
//            }
//        };

        Supplier<Integer> supplier = ()->{ return 1024; };

        System.out.println(supplier.get());
    }
}

13. Stream流式计算

什么是Stream流式计算。

package com.huathy.stream;

/**
 * @ClassPath: com.huathy.stream.Test
 * @Author: Huathy
 * @Description:
 * @Date: 2021-01-14 20:48
 */

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

import java.util.Arrays;
import java.util.List;

/**
 * 题目要求:一分钟,一行代码,从5个用户中筛选
 * 1. ID是偶数
 * 2. 年龄>25
 * 3. 用户名转为大写
 * 4. 用户名字母倒序
 * 5. 只输出一个用户
 */
public class Test {
    public static void main(String[] args) {
        User u1 = new User(1,"a",10);
        User u2 = new User(2,"b",20);
        User u3 = new User(3,"c",30);
        User u4 = new User(4,"d",40);
        User u5 = new User(6,"e",50);

        //集合用来存储
        List<User> list = Arrays.asList(u1, u2, u3, u4, u5);

        //用stream流来计算
        list.stream().
                //过滤:id为偶数,且年龄>25
                filter( u -> { return u.getId()%2==0 && u.getAge()>25; })
                //转换:姓名转为大写
                .map( u -> { return u.getName().toUpperCase(); } )
                //排序    倒序
                .sorted( (us1,us2) -> { return us2.compareTo(us1); } )
                //分页:只输出一个
                .limit(1)
                .forEach(System.out::println);

    }
}

@Data
@NoArgsConstructor
@AllArgsConstructor
class User {
    private int id;
    private String name;
    private int age;
}

14. ForkJoin 分支合并

什么是ForkJoin。并发执行任务!提高效率。大数据量。
大数据:Map Reduce(将大任务拆分成小任务)
特点:工作窃取。线程B执行完任务后,将A线程没有执行的任务拿来执行。可以提高工作效率。

这个里面维护的都是双端队列。

ForkJoin使用步骤:

  1. ForkJoinPool 通过它来执行
  2. ForkJoinTask:实现类(RecursiveAction递归事件,没有返回值;RecursiveTask递归任务,有返回值)

15. 异步回调

Future 设计初衷:对将来的某个事件进行建模。
实现类:CompletableFuture

package com.huathy.future;

/**
 * @ClassPath: com.huathy.future.Demo001
 * @Author: Huathy
 * @Description:
 * @Date: 2021-01-15 23:32
 */

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

/**
 * 异步调用:CompletableFuture
 * 类比Ajax发起请求
 * 1. 异步执行
 * 2. 成功回调
 * 3. 失败回调
 */
public class Demo01 {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
//        noReturnTest();
        returnTest1();
    }

    public static void noReturnTest() throws ExecutionException, InterruptedException {
        //没有返回值的runAsync异步回调。
        CompletableFuture<Void> completableFuture = CompletableFuture.runAsync(() -> {
            try {
                TimeUnit.SECONDS.sleep(2);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName() + " runAsync => Void");
        });

        System.out.println("===========");

        //阻塞获取执行结果
        completableFuture.get();

    }

    public static void returnTest1() throws ExecutionException, InterruptedException {
        //有返回值的supplyAsync异步回调。
        CompletableFuture<Integer> completableFuture = CompletableFuture.supplyAsync( ()-> {
            System.out.println(Thread.currentThread().getName() + " supplyAsync => Integer");
            int i = 10 / 0;     //测试exception
            return 1024;
        } );

        CompletableFuture<Integer> exceptionally = completableFuture.whenComplete((t, u) -> {
            System.out.println("t=>" + t);
            System.out.println("u=>" + u);
        }).exceptionally((e) -> {
            System.out.println(e.getMessage());
            return -1;
        });

        //阻塞获取执行结果
        System.out.println( exceptionally.get() );
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Huathy-雨落江南,浮生若梦

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值