Java——JUC并发编程

文章目录

  • 1、 什么是JUC
  • 2、线程和进程
  • 3、Lock锁
    • 3.2、Synchronized和Lock区别
  • 4、生产者和消费者的问题
    • 4.1、 Synchronized版
      • 4.1.2、问题纯在(A,B,CD)4个线程!虚假唤醒
    • 4.2、JUC版
    • 4.2.1、顺序执行
  • 5、八锁现象
    • 5.1、小结
  • 6、集合类不安全
    • 6.1、List不安全
    • 6.2、Set不安全
      • 6.2.1、hashSet底层是什么
    • 6.3、Map不安全
      • 6.3.1、ConcurrentHashMap的原理
  • 7、Callable
  • 8、常用的辅助类
    • 8.1、CountDownLatch(减法计数器)
      • 8.1.1、使用方法
    • 8.2、CyclicBarrier(加法计数器)
    • 8.3、Semaphore
      • 8.3.1、使用方法
  • 9、ReadWriteLock(读写锁)
  • 10、阻塞队列
    • 10.1、抛出异常(add&&remove )
    • 10.2、不抛出异常(offer&&poll)
    • 10.3、阻塞(put&&take)
    • 10.4、超时等待(不抛出异常(offer&&poll))
    • 10.5、同步队列(SynchronizedQueue)
  • 11、线程池
    • 11.1、源码分析
    • 11.2、自定义线程池
      • 11.2.1、四种拒绝策略
      • 11.2.2、CPU密集型和IO密集型
  • 12、四大函数式接口(必须掌握)
    • 12.1、函数型接口和断定型接口
      • 12.1.1、Function函数式接口
      • 12.1.2、Predicate断定型接口
    • 12.2、消费型接口和供给型接口
      • 12.2.1、Consumer消费型接口
      • 12.2.2、Supplier供给型接口
  • 13、Stream流式计算
  • 14、ForkJoin
  • 15、异步回调
  • 16、JMM
  • 17、Volatile
  • 18、单例模式
    • 18.1、饿汉式
    • 18.2、DCL懒汉式
    • 18.3、静态内部类
    • 18.4、枚举
  • 19、理解CAS
    • 19.1、ABA问题(狸猫换太子)
  • 20、原子应用
  • 21、可重入锁
    • 21.1、Synchronized
    • 21.2、Lock
  • 22、自旋锁
  • 24、死锁

1、 什么是JUC

  1. 源码+官方文档
  2. java.util工具包、包分类
  • Runable 没有返回值,效率相比Callable相对较低!
    Java——JUC并发编程_第1张图片
    Java——JUC并发编程_第2张图片

2、线程和进程

  1. 进程:一个程序,程序的集合;一个进程
    Java——JUC并发编程_第3张图片
  2. Java无法直接操作硬件,所以它并不能直接开启线程,要调用本地方法(底层的C++)
  • 并发编程:并发、并行
  • 并发(多个线程操作同一个资源)
    CPU一核,模拟出来多条线程。(天下武功,唯快不破。快速交替)
    -并行(多个人一起行走)
    CPU多核,多个线程可以同时执行:
package Demo01;

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

  • 并发编程的本质:充分利用CPU的资源
  1. 线程的状态
public enum State {
       //新生
        NEW,

        //运行
        RUNNABLE,

       //阻塞
        BLOCKED,
		
		//等待,死死的等
        WAITING,

        //超时等待
        TIMED_WAITING,

        //终止
        TERMINATED;
    }

  1. wait/sleep区别
    1. 来自不同的类
      1. wait——>Object
      2. sleep——>Thread
  2. 关于锁的释放
    wait会释放锁,sleep不会释放
  3. 使用的范围不同
    1. wait
      必须在同步代码块中
    2. sleep
      可以在任何地方睡

3、Lock锁

  1. 传统Synchronized
package Demo01;
//基本的买票例子
/*
    真正的多线程开发,降低耦合性
    线程就是单独的资源类,没有任何附属的操作
    1. 属性、方法
 */
public class SaleTicketDemo01 {
    public static void main( String[] args ) {
        //并发:多线程操作同一个资源类
        Ticket ticket = new Ticket();
        //函数式接口
        new Thread(()->{
            for (int i = 0; i < 50; i++) {
                ticket.sale();
            }

        },"A").start();
        new Thread(()->{
            for (int i = 0; i < 50; i++) {
                ticket.sale();
            }
        },"B").start();
        new Thread(()->{
            for (int i = 0; i < 50; i++) {
                ticket.sale();
            }
        },"C").start();
    }
}
//资源类   oop
class Ticket{
    //属性、方法
    private int number = 50;
    //买票的方式
    //synchronized 本质:队列,锁
    public synchronized void sale(){
        if(number>0){
            System.out.println(Thread.currentThread().getName()+"卖出了"+(number--)+"票,剩余:"+number);
        }
    }
}
  1. Lock接口
    在这里插入图片描述
    Java——JUC并发编程_第4张图片
    Java——JUC并发编程_第5张图片
  • 公平锁:十分公平:可以先来后到
  • 非公平锁:十分不公平:可以插队(默认)
package Demo01;

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

//基本的买票例子
/*
    真正的多线程开发,降低耦合性
    线程就是单独的资源类,没有任何附属的操作
    1. 属性、方法
 */
public class SaleTicketDemo02 {
    public static void main( String[] args ) {
        //并发:多线程操作同一个资源类
        Ticket2 ticket = new Ticket2();
        //函数式接口
        new Thread(()->{
            for (int i = 0; i < 50; i++) ticket.sale(); },"A").start();
        new Thread(()->{
            for (int i = 0; i < 50; i++) ticket.sale(); },"B").start();
        new Thread(()->{ for (int i = 0; i < 50; i++) ticket.sale();},"C").start();
    }
}
/*
    Lock 三部曲
        1. new ReentrantLock
        2. Lock.lock();//加锁
        3. Lock.unlock();//解锁
 */

//资源类   oop
class Ticket2{
    //属性、方法
    private int number = 50;
    //买票的方式
    Lock lock = new ReentrantLock();
    public void sale(){
        lock.lock();    //加锁
        try {
            if(number>0){
                System.out.println(Thread.currentThread().getName()+"卖出了"+(number--)+"票,剩余:"+number);
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }
}

3.2、Synchronized和Lock区别

  1. Synchronized 内置的Java关键字,Lock是一个Java类
  2. Synchronized 无法判断获取锁的状态,Lock可以判断是否获取到了锁
  3. Synchronized 会自动释放锁,Lock必须要手动释放锁!如果不释放锁(死锁)
  4. Synchronized 线程1(获得锁)、线程2(等待),Lock锁不一定等待下去
  5. Synchronized 可重入锁,不可以中断,非公平;Lock,可重入锁,可以判断锁,非公平(可以设置)
  6. Synchronized 适合锁少量代码同步问题,Lock适合锁大量的同步代码

4、生产者和消费者的问题

4.1、 Synchronized版

package Demo01;
/*
    线程之间的通信问题:生产者和消费者问题!等待唤醒,通知唤醒
    线程交替执行 A B 操作同一个变量 num = 0
    A num+1
    B nm+1
 */
public class A {
    public static void main( String[] args ) {
        Data data = new Data();
        new Thread(()->{
            for (int i = 0; i < 100; i++) {
                try {
                    data.increment();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        },"A").start();
        new Thread(()->{
            for (int i = 0; i < 100; i++) {
                try {
                    data.decrement();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        },"B").start();

    }
}
//资源类
//等待,业务,通知
class Data{
    private int number = 0;
    //+1
    public synchronized void increment() throws InterruptedException {
        if(number!=0){
            //等待
            this.wait();
        }
        number++;
        System.out.println(Thread.currentThread().getName() + "-----" + number);
        ///通知其它线程,我+1完毕了
        this.notifyAll();
    }
    //-1
    public synchronized void decrement() throws InterruptedException {
        if(number==0){
            //等待
            this.wait();
        }
        number--;
        System.out.println(Thread.currentThread().getName() + "-----" + number);
        //通知其它线程,我-1完毕了
        this.notifyAll();
    }

}

4.1.2、问题纯在(A,B,CD)4个线程!虚假唤醒

Java——JUC并发编程_第6张图片
if改为while

package Demo01;
/*
    线程之间的通信问题:生产者和消费者问题!等待唤醒,通知唤醒
    线程交替执行 A B 操作同一个变量 num = 0
    A num+1
    B nm+1
 */
public class A {
    public static void main( String[] args ) {
        Data data = new Data();
        new Thread(()->{
            for (int i = 0; i < 100; i++) {
                try {
                    data.increment();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        },"A").start();
        new Thread(()->{
            for (int i = 0; i < 100; i++) {
                try {
                    data.decrement();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        },"B").start();

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

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

    }
}
//资源类
//等待,业务,通知
class Data{
    private int number = 0;
    //+1
    public synchronized void increment() throws InterruptedException {
        while(number!=0){
            //等待
            this.wait();
        }
        number++;
        System.out.println(Thread.currentThread().getName() + "-----" + number);
        ///通知其它线程,我+1完毕了
        this.notifyAll();
    }
    //-1
    public synchronized void decrement() throws InterruptedException {
        while(number==0){
            //等待
            this.wait();
        }
        number--;
        System.out.println(Thread.currentThread().getName() + "-----" + number);
        //通知其它线程,我-1完毕了
        this.notifyAll();
    }

}

4.2、JUC版

package Demo01;

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

/*
    线程之间的通信问题:生产者和消费者问题!等待唤醒,通知唤醒
    线程交替执行 A B 操作同一个变量 num = 0
    A num+1
    B nm+1
 */
public class B {
    public static void main( String[] args ) {
        Data data = new Data();
        new Thread(()->{
            for (int i = 0; i < 100; i++) {
                try {
                    data.increment();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        },"A").start();
        new Thread(()->{
            for (int i = 0; i < 100; i++) {
                try {
                    data.decrement();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        },"B").start();

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

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

    }
}
//资源类
//等待,业务,通知
class Data2 {
    private int number = 0;

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

    //+1
    public void increment( ) throws InterruptedException {
        lock.lock();
        try {
            while (number != 0) {
                //等待
                condition.await();
            }
            number++;
            System.out.println(Thread.currentThread().getName() + "-----" + number);
            ///通知其它线程,我+1完毕了
            condition.signalAll();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }
    //-1
    public synchronized void decrement () throws InterruptedException {
        lock.lock();

        try {
            while (number == 0) {
                //等待
                condition.await();
            }
            number--;
            System.out.println(Thread.currentThread().getName() + "-----" + number);
            //通知其它线程,我-1完毕了
            condition.signalAll();
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                lock.unlock();
        }
    }
}
  • 任何一个新的技术,绝对不是仅仅只是覆盖了原来的技术。(优势和补充)
    Java——JUC并发编程_第7张图片

4.2.1、顺序执行

  • 代码演示
package Demo01;

import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
/*
    A执行完调用B
    B执行完调用C
    C执行完调用A
 */
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
    private Lock lock =new ReentrantLock();
    private Condition condition1 = lock.newCondition();
    private Condition condition2 = lock.newCondition();
    private Condition condition3 = lock.newCondition();
    private int num = 1;        //1==A 2==B 3==C
    public void printA(){
        //业务:判断——》执行——》通知
         lock.lock();
        try {
            while (num!=1){
                //等待
                condition1.await();
            }
            System.out.println(Thread.currentThread().getName() + "——>AAAAAAAAAAAAA");
            //唤醒:唤醒指定的线程    B
            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() + "——》BBBBBBBBBBBBB");
            //唤醒:指定的线程  C
            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() + "——》CCCCCCCCCCCCCCCCCC");
            //唤醒指定的线程
            num = 1;
            condition1.signal();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }
}
  • 运行结果
    Java——JUC并发编程_第8张图片

5、八锁现象

  • 如何判断锁的是谁!永远的知道什么锁,锁到底锁的是谁!
package Demo01;

import java.util.concurrent.TimeUnit;

/*
    八锁:就是关于锁的八个问题
        1.标准情况下,两个线程是先打印发短信还是打电话? 1.发短信 2.打电话
        2.sendSms延迟4秒,两个线程先打印 发短信还是打电话? 1.发短信 2.打电话

 */
public class Test01 {
    public static void main( String[] args ) {
        Phone phone = new Phone();
        //锁的存在
        new Thread(()->{
            phone.sendSms();
        },"A").start();
        //捕获
        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        new Thread(()->{
            phone.call();
        },"B").start();
    }
}
class Phone{
    // Synchronized 锁的对象是方法的调用者!
    // 两个方法用的是同一个锁,谁先拿到谁执行
    public synchronized void sendSms(){
        try {
            TimeUnit.SECONDS.sleep(4);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("发短信");
    }
    public synchronized void call(){
        System.out.println("打电话");
    }
}

package Demo01;

import java.util.concurrent.TimeUnit;

/*
    八锁:就是关于锁的八个问题
        1.标准情况下,两个线程是先打印发短信还是打电话? 1.发短信 2.打电话
        2.sendSms延迟4秒,两个线程先打印 发短信还是打电话? 1.发短信 2.打电话
        3.增加了一个普通方法发短信还是hello?  1.hello 2.发短信
        4.两个对象,两个同步方法,发短信还是打电话? 1.打电话 2.发短信

 */
public class Test02 {
    public static void main( String[] args ) {
        //两个对象,两个调用者,两把锁
        Phone2 phone = new Phone2();
        Phone2 phone2 = new Phone2();
        //锁的存在
        new Thread(()->{
            phone.sendSms();
        },"A").start();
        //捕获
        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        new Thread(()->{
            phone2.call();
        },"B").start();
    }
}
class Phone2{
    // Synchronized 锁的对象是方法的调用者!
    // 两个方法用的是同一个锁,谁先拿到谁执行
    public synchronized void sendSms(){
        try {
            TimeUnit.SECONDS.sleep(4);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("发短信");
    }
    public synchronized void call(){
        System.out.println("打电话");
    }
    //这里没有锁!不是同步方法,不受锁的影响
    public void  hello(){
        System.out.println("hello");
    }
}

package Demo01;

import java.util.concurrent.TimeUnit;

/*
    八锁:就是关于锁的八个问题
        1.标准情况下,两个线程是先打印发短信还是打电话? 1.发短信 2.打电话
        2.sendSms延迟4秒,两个线程先打印 发短信还是打电话? 1.发短信 2.打电话
        1.标准情况下,两个线程是先打印发短信还是打电话? 1.发短信 2.打电话
        2.sendSms延迟4秒,两个线程先打印 发短信还是打电话? 1.发短信 2.打电话
        3.增加了一个普通方法发短信还是hello?  1.hello 2.发短信
        4.两个对象,两个同步方法,发短信还是打电话? 1.打电话 2.发短信
        5.增加两个静态的同步方法,只有一个对象,发短信还是打电话? 1.发短信 2.打电话
        6.两个对象!增加两个静态的同步方法,只有一个对象,发短信还是打电话? 1.发短信 2.打电话

 */
public class Test03 {
    public static void main( String[] args ) {
        //两个对象的Class类模板只有一个;static,锁的是Class
        Phone3 phone = new Phone3();
        Phone3 phone2 = new Phone3();
        //锁的存在
        new Thread(()->{
            phone.sendSms();
        },"A").start();
        //捕获
        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        new Thread(()->{
            phone2.call();
        },"B").start();
    }
}
//Phone3唯一的一个Class对象
class Phone3{
    // Synchronized 锁的对象是方法的调用者!
    // 两个方法用的是同一个锁,谁先拿到谁执行
    //static 静态方法;类一加载就有了!Class模板
    public static synchronized void sendSms(){
        try {
            TimeUnit.SECONDS.sleep(4);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("发短信");
    }
    public static synchronized void call(){
        System.out.println("打电话");
    }
}

package Demo01;

import java.util.concurrent.TimeUnit;

/*
    八锁:就是关于锁的八个问题
        1.标准情况下,两个线程是先打印发短信还是打电话? 1.发短信 2.打电话
        2.sendSms延迟4秒,两个线程先打印 发短信还是打电话? 1.发短信 2.打电话
        1.标准情况下,两个线程是先打印发短信还是打电话? 1.发短信 2.打电话
        2.sendSms延迟4秒,两个线程先打印 发短信还是打电话? 1.发短信 2.打电话
        3.增加了一个普通方法发短信还是hello?  1.hello 2.发短信
        4.两个对象,两个同步方法,发短信还是打电话? 1.打电话 2.发短信
        5.增加两个静态的同步方法,只有一个对象,发短信还是打电话? 1.发短信 2.打电话
        6.两个对象!增加两个静态的同步方法,只有一个对象,发短信还是打电话? 1.发短信 2.打电话
        7.一个静态同步方法,一个普通同步方法,一个对象,先打印 发短信还是打电话?
        8.一个静态同步方法,一个普通同步方法,两个个对象,先打印 发短信还是打电话?
 */
public class Test04 {
    public static void main( String[] args ) {
        Phone4 phone = new Phone4();
        Phone4 phone2 = new Phone4();
        //锁的存在
        new Thread(()->{
            phone.sendSms();
        },"A").start();
        //捕获
        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        new Thread(()->{
            phone2.call();
        },"B").start();
    }
}

class Phone4{
    // 静态同步方法,锁的是Class类模板
    public static synchronized void sendSms(){
        try {
            TimeUnit.SECONDS.sleep(4);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("发短信");
    }
    //普通的同步方法
    public synchronized void call(){
        System.out.println("打电话");
    }
}

5.1、小结

  1. new this 具体的对象
  2. static Class 唯一的一个模板

6、集合类不安全

6.1、List不安全

package unsafe;


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

//java.lang.UnsupportedOperationException 并发修改异常!
public class ListTest {
    public static void main( String[] args ) {
        /*
            并发下Arra不安全的
            解决方案:
                1. List list = new Vector<>();
                2.List list = Collections.synchronizedList(new ArrayList<>());
                3.List list = new CopyOnWriteArrayList<>();
         */
//        List list = new ArrayList();
//        List list = new Vector<>();
        
//        List list = Collections.synchronizedList(new ArrayList<>());
        //CopyOnWrite 写入时复制 cow 计算机程序设计领域的一种优化策略;
        //多个线程调用的时候,list,读取的时候,固定的,写入(覆盖)
        //写入的时候避免覆盖,造成数据问题
        //读写分离 
        //CopyOnWrite
        List list = new CopyOnWriteArrayList<>();
        for (int i = 1; i < 11; i++) {
            new Thread(()->{
                list.add(UUID.randomUUID().toString().substring(0,5));
                System.out.println(list);
            },String.valueOf(i)).start();

        }
    }
}

6.2、Set不安全

package unsafe;

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

/*
    同理可证:java.util.ConcurrentModificationException
        1.Set set = Collections.synchronizedSet(new HashSet<>());
        2.Set set = new CopyOnWriteArraySet<>();
 */
public class SetTest {
    public static void main( String[] args ) {
//        Set set = new HashSet<>();
        Set<String> set = new CopyOnWriteArraySet<>();
        for (int i = 0; i < 30; i++) {
            new Thread(()->{
                set.add(UUID.randomUUID().toString().substring(0,5));
                System.out.println(set);
            },String.valueOf(i)).start();
        }
    }
}

6.2.1、hashSet底层是什么

public HashSet() {
        map = new HashMap<>();
    }
    //ass set本质就是map 可以是无法重复的
public boolean add(E e) {
        return map.put(e, PRESENT)==null;
    }
private static final Object PRESENT = new Object();//不变的值

6.3、Map不安全

package unsafe;

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

//java.util.ConcurrentModificationException
public class MapTest {
    public static void main( String[] args ) {
        //map是这样用的吗?    不是,大公司不用HashMap
        //默认等价于什么?      new HashMap<>(16,0.75);
        //Map map = new HashMap<>();
        Map<String, String> 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();
        }
    }
}

6.3.1、ConcurrentHashMap的原理

支持检索的完全并发性和更新的高预期并发性的哈希表。 这个类服从相同功能规范如Hashtable ,并且包括对应于每个方法的方法版本Hashtable 。 不过,尽管所有操作都是线程安全的,检索操作并不意味着锁定,并没有为防止所有访问的方式锁定整个表的任何支持。 这个类可以在依赖于线程安全性的程序中与Hashtable完全互Hashtable ,但不依赖于其同步细节。
检索操作(包括get )通常不阻止,因此可能与更新操作重叠(包括put和remove )。 检索反映了最近完成的更新操作的结果。 (更正式地,对于给定密钥的更新操作熊之前发生与任何(非空关系)检索该键报告经更新的值。)对于聚合操作,比如putAll和clear ,并发检索可能反映插入或移除只有一些条目。 类似地,迭代器,分割器和枚举返回在反映迭代器/枚举创建过程中或之后反映哈希表状态的元素。 他们不抛出ConcurrentModificationException 。 然而,迭代器被设计为一次只能由一个线程使用。 请记住,骨料状态方法的结果,包括size , isEmpty和containsValue通常是有用的,只有当一个地图没有发生在其他线程并发更新。 否则,这些方法的结果反映了可能足以用于监视或估计目的的瞬态状态,但不适用于程序控制。

当存在太多的冲突(即,具有不同的哈希码但是以表的大小为模数落入相同的时隙的密钥)时,该表被动态扩展,并且每个映射保持大致两个bin的预期平均效果(对应于0.75负载因素阈值调整大小)。 由于映射被添加和删除,这个平均值可能会有很大差异,但是总的来说,这为哈希表保留了普遍接受的时间/空间权衡。 然而,调整这个或任何其他类型的散列表可能是相对较慢的操作。 如果可能,最好提供一个尺寸估计作为可选的initialCapacity构造函数参数。 附加的可选的loadFactor构造函数参数提供了另外的手段,通过指定在计算给定数量的元素时要分配的空间量时使用的表密度来定制初始表容量。 此外,为了与此类的先前版本兼容,构造函数可以可选地指定预期的concurrencyLevel作为内部大小调整的附加提示。 请注意,使用完全相同的许多键hashCode()是降低任何哈希表的hashCode()的一种可靠的方法。 为了改善影响,当按键为Comparable时,该类可以使用键之间的比较顺序来帮助打破关系。

甲Set投影一个的ConcurrentHashMap可以(使用被创建newKeySet()或newKeySet(int) ),或观察(使用keySet(Object)时仅键是感兴趣的,并且被映射的值是(可能瞬时)不使用或全部取相同的映射值。

ConcurrentHashMap可以通过使用LongAdder值并通过computeIfAbsent进行初始化,将其用作可缩放的频率映射(直方图或多集的形式)。 例如,要向ConcurrentHashMap freqs添加计数,可以使用freqs.computeIfAbsent(k -> new LongAdder()).increment();

此类及其视图和迭代器实现所有的可选方法Map个Iterator接口。

像Hashtable但不像HashMap ,这个类不允许 null用作键或值。

ConcurrentHashMaps支持一组顺序和并行批量操作,与大多数Stream方法不同,它们被设计为安全并且经常明智地应用,即使是由其他线程同时更新的映射; 例如,当计算共享注册表中的值的快照摘要时。 有三种操作,每种具有四种形式,接受键,值,条目和(键,值)参数和/或返回值的函数。 由于ConcurrentHashMap的元素不以任何特定的方式排序,并且可能会在不同的并行执行中以不同的顺序进行处理,因此提供的函数的正确性不应取决于任何排序,也不应依赖于可能瞬时变化的任何其他对象或值计算进行中; 除了每一个行动,理想情况下都是无副作用的。 对Map.Entry对象的批量操作不支持方法setValue 。

  • forEach:对每个元素执行给定的操作。 变量形式在执行操作之前对每个元素应用给定的变换。
  • search:返回在每个元素上应用给定函数的第一个可用非空结果; 当找到结果时跳过进一步的搜索。
  • reduce:累积每个元素。 提供的减少功能不能依赖于排序(更正式地,它应该是关联和交换)。 有五种变体:
    • 平原减少 (由于没有相应的返回类型,因此(key,value)函数参数没有这种方法的形式)
    • 映射的减少积累了应用于每个元素的给定函数的结果。
    • 使用给定的基础值减少到标量双,长和int。
      这些批量操作接受一个parallelismThreshold参数。 如果估计当前地图大小小于给定阈值,则方法依次进行。 使用Long.MAX_VALUE的值Long.MAX_VALUE抑制所有的并行性。 使用1的值可以通过划分为足够的子任务来完全利用用于所有并行计算的ForkJoinPool.commonPool()来实现最大并行度。 通常,您最初将选择其中一个极值,然后测量使用中间值之间的性能,从而降低开销与吞吐量之间的关系。

批量操作的并发属性遵循ConcurrentHashMap的并发属性:从get(key)返回的任何非空结果和相关的访问方法与相关的插入或更新都有一个发生之前的关系。 任何批量操作的结果都反映了这些每个元素关系的组成(但是除非以某种方式已知是静态的,它们并不一定是相对于整个地图的原子)。 相反,因为映射中的键和值从不为空,所以null作为目前缺乏任何结果的可靠原子指标。 为了保持此属性,null用作所有非标量缩减操作的隐含基础。 对于double,long和int版本,基础应该是当与任何其他值组合时返回其他值(更正式地,它应该是减少的标识元素)。 大多数常见的减少具有这些属性; 例如,使用基数0或最小值与基准MAX_VALUE计算和。

作为参数提供的搜索和转换函数应该类似地返回null以指示缺少任何结果(在这种情况下不被使用)。 在映射缩减的情况下,这也使得转换可以用作过滤器,如果不应该组合元素,返回null(或者在原始专业化的情况下,身份基础)。 在使用它们进行搜索或减少操作之前,您可以通过在“null意味着现在没有任何内容”规则下自行构建复合转换和过滤。

接受和/或返回Entry参数的方法维护键值关联。 例如,当找到最大价值的钥匙时,它们可能是有用的。 请注意,可以使用new AbstractMap.SimpleEntry(k,v)提供“plain”Entry new AbstractMap.SimpleEntry(k,v) 。

批量操作可能突然完成,抛出在应用程序中遇到的异常。 在处理这样的异常时,请注意,其他并发执行的函数也可能引发异常,或者如果没有发生第一个异常,则会这样做。

与顺序形式相比,加速比是常见的,但不能保证。 如果并行计算的基础工作比计算本身更昂贵,则涉及小地图上的简短功能的并行操作可能比顺序形式执行得更慢。 类似地,如果所有处理器正忙于执行不相关的任务,并行化可能不会导致太多的实际并行。

所有任务方法的所有参数都必须为非空值。

这个班是Java Collections Framework的会员 。

7、Callable

Java——JUC并发编程_第9张图片

  1. 可以有返回值
  2. 可以抛出异常
  3. 方法不同(run()/call)
  • 代码演示
package callable;

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

public class CallableTest {
    public static void main( String[] args ) {
//        new Thread(new MyThread()).start();
//        new Thread(new FutureTask()).start();
//        new Thread(new FutureTask(Callable)).start();
        new Thread().start();//怎么启动callable
        MyThread thread = new MyThread();
        //适配类
        FutureTask<Integer> futureTask = new FutureTask<>(thread);
        new Thread(futureTask,"A").start();
        new Thread(futureTask,"B").start();    //此时线程不是NEW状态,第一次调用时返回结果保存了
        try {
            Integer integer = futureTask.get(); //获取callable返回结果 (方法可能会产生阻塞!把他放到最后或者通过异步通信来处理)
            System.out.println(integer);
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        }

    }
}
class MyThread implements Callable<Integer> {


    @Override
    public Integer call( ) {
        System.out.println("call()");
        //耗时的操作
        return 1024;
    }
}

8、常用的辅助类

8.1、CountDownLatch(减法计数器)

Java——JUC并发编程_第10张图片

  • 代码演示
package Demo01;

import java.util.concurrent.CountDownLatch;

//计数器
public class CountDownLatchDemo {
    public static void main( String[] args ) throws InterruptedException {
        //总数是6
        CountDownLatch countDownLatch = new CountDownLatch(6);
        for (int i = 1; i < 7; i++) {
            new Thread(()->{
                System.out.println(Thread.currentThread().getName() + "Go out");
                countDownLatch.countDown();//数量-1
            },String.valueOf(i)).start();
        }
        countDownLatch.await(); //等待计数器归零,然后再向下执行
        System.out.println("Close Door");
    }
}

8.1.1、使用方法

  • countDownLatch.countDown();//数量-1
  • countDownLatch.await(); //等待计数器归零,然后再向下执行

8.2、CyclicBarrier(加法计数器)

Java——JUC并发编程_第11张图片

  • 代码演示
package Demo01;

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

public class CyclicBarrierDemo {
    public static void main( String[] args ) {
        //集齐七颗龙珠召唤神龙
        //召唤龙珠的线程
        CyclicBarrier cyclicBarrier = new CyclicBarrier(7,()->{
            System.out.println("召唤神龙成功");
        });

        for (int i = 1; i < 8; i++) {
            final int temp =i;
            new Thread(()->{
                System.out.println(Thread.currentThread().getName() + "收集了" + temp + "个龙珠");
                try {
                    cyclicBarrier.await();//等待
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } catch (BrokenBarrierException e) {
                    e.printStackTrace();
                }
            }).start();
        }
    }
}

8.3、Semaphore

Java——JUC并发编程_第12张图片

  • 代码演示
package Demo01;

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

public class SemaphoreDemo {
    public static void main( String[] args ) {
        //线程数量:停车位; 限流
        Semaphore semaphore = new Semaphore(3);
        for (int i = 1; i < 7; i++) {
            new Thread(()->{
                //acquire() 得到
                try {
                    semaphore.acquire();
                    System.out.println(Thread.currentThread().getName()+"抢到车位");
                    TimeUnit.SECONDS.sleep(2);
                    System.out.println(Thread.currentThread().getName() + "离开车位");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }finally{
                    //release() 释放
                    semaphore.release();
                }


            },String.valueOf(i)).start();
        }
    }
}

8.3.1、使用方法

  • semaphore.acquire();//获得,如果已经满了,等待,等待被释放为止;-1
  • semaphore.release();//释放,将当前的信号量释放+1,然后唤醒等待的线程
  • 作用:多个共享资源互斥的使用!并发限流,控制最大的线程数!

9、ReadWriteLock(读写锁)

Java——JUC并发编程_第13张图片

  • 代码演示
package Demo01;

import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
/*
    独占锁(写锁) 一次只能被一个线程占有
    共享锁(读锁) 多个线程可以同时占有
    读——读    //可以共存
    读——写    //不能共存
    写——写    //不能共存
 */
public class ReadWriteLockDemo {
    public static void main( String[] args ) {
        MyCacheLock myCache = new MyCacheLock();
        //写入
        for (int i = 1; i < 6; i++) {
            final int temp =i;
            new Thread(()->{
                myCache.put(temp+"",temp+"");
            },String.valueOf(i)).start();
        }

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

//加锁
class MyCacheLock{
    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);
            map.put(key,value);
            System.out.println(Thread.currentThread().getName() + "写入完毕") ;
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            readWriteLock.writeLock().unlock();
        }
    }

    //取,读;所有人都可以读
    public void get(String key){
        readWriteLock.readLock().lock();
        try {
            System.out.println(Thread.currentThread().getName() + "读取" + key);
            Object o = map.get(key);
            System.out.println(Thread.currentThread().getName() + "读取完毕") ;
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            readWriteLock.readLock().unlock();
        }
    }
}


/*
    自定义缓存
 */
class MyCache{
    private volatile Map<String,Object> map = new HashMap<>();
    //存,写
    public void put(String key,Object value){
        System.out.println(Thread.currentThread().getName() + "写入" + key);
        map.put(key,value);
        System.out.println(Thread.currentThread().getName() + "写入完毕") ;
    }

    //取,读
    public void get(String key){
        System.out.println(Thread.currentThread().getName() + "读取" + key);
        Object o = map.get(key);
        System.out.println(Thread.currentThread().getName() + "读取完毕") ;
    }
}

10、阻塞队列

Java——JUC并发编程_第14张图片

  • 什么情况下我们会使用 阻塞队列:多线程并发处理、线程池

10.1、抛出异常(add&&remove )

package bq;

import java.util.concurrent.ArrayBlockingQueue;

public class Test {
    public static void main( String[] args ) {
        test1();
    }
    /*
        抛出异常
     */
    public static void test1(){
        ArrayBlockingQueue blockingQueue = new ArrayBlockingQueue<>(3);
        System.out.println(blockingQueue.add("A"));
        System.out.println(blockingQueue.add("B"));
        System.out.println(blockingQueue.add("C"));
        //java.lang.IllegalStateException: Queue full   抛出异常!
        //System.out.println(blockingQueue.add("D"));
        System.out.println(blockingQueue.element());//查看队首元素是谁
        System.out.println("=======================");
        System.out.println(blockingQueue.remove());
        System.out.println(blockingQueue.remove());
        System.out.println(blockingQueue.remove());
        //java.util.NoSuchElementException  抛出异常!
        System.out.println(blockingQueue.remove());
    }
}

10.2、不抛出异常(offer&&poll)

package bq;

import java.util.concurrent.ArrayBlockingQueue;

public class Test {
    public static void main( String[] args ) {
        test2();
    }
    /*
        抛出异常
     */
    public static void test1(){
        ArrayBlockingQueue blockingQueue = new ArrayBlockingQueue<>(3);
        System.out.println(blockingQueue.add("A"));
        System.out.println(blockingQueue.add("B"));
        System.out.println(blockingQueue.add("C"));
        //java.lang.IllegalStateException: Queue full   抛出异常!
        //System.out.println(blockingQueue.add("D"));
        System.out.println("=======================");
        System.out.println(blockingQueue.remove());
        System.out.println(blockingQueue.remove());
        System.out.println(blockingQueue.remove());
        //java.util.NoSuchElementException  抛出异常!
        System.out.println(blockingQueue.remove());
    }
    /*
        有返回值没有异常
     */
    public static void test2(){
        //队列大小
        ArrayBlockingQueue blockingQueue = new ArrayBlockingQueue<>(3);
        System.out.println(blockingQueue.offer("A"));
        System.out.println(blockingQueue.offer("B"));
        System.out.println(blockingQueue.offer("C"));
        //System.out.println(blockingQueue.offer("D"));//false 不抛出异常!
        System.out.println(blockingQueue.peek());   //检查队首元素
        System.out.println("=======================");
        System.out.println(blockingQueue.poll());
        System.out.println(blockingQueue.poll());
        System.out.println(blockingQueue.poll());
        System.out.println(blockingQueue.poll());//null 不抛出异常
    }
}

10.3、阻塞(put&&take)

package bq;

import java.util.concurrent.ArrayBlockingQueue;

public class Test {
    public static void main( String[] args ) throws InterruptedException {
        test3();
    }
    /*
        抛出异常
     */
    public static void test1(){
        ArrayBlockingQueue blockingQueue = new ArrayBlockingQueue<>(3);
        System.out.println(blockingQueue.add("A"));
        System.out.println(blockingQueue.add("B"));
        System.out.println(blockingQueue.add("C"));
        //java.lang.IllegalStateException: Queue full   抛出异常!
        //System.out.println(blockingQueue.add("D"));
        System.out.println("=======================");
        System.out.println(blockingQueue.remove());
        System.out.println(blockingQueue.remove());
        System.out.println(blockingQueue.remove());
        //java.util.NoSuchElementException  抛出异常!
        System.out.println(blockingQueue.remove());
    }
    /*
        有返回值没有异常
     */
    public static void test2(){
        //队列大小
        ArrayBlockingQueue blockingQueue = new ArrayBlockingQueue<>(3);
        System.out.println(blockingQueue.offer("A"));
        System.out.println(blockingQueue.offer("B"));
        System.out.println(blockingQueue.offer("C"));
        //System.out.println(blockingQueue.offer("D"));//false 不抛出异常!
        System.out.println(blockingQueue.peek());   //检查队首元素
        System.out.println("=======================");
        System.out.println(blockingQueue.poll());
        System.out.println(blockingQueue.poll());
        System.out.println(blockingQueue.poll());
        System.out.println(blockingQueue.poll());//null 不抛出异常
    }
    /*
        等待,阻塞(一直等待)
     */
    public static void test3() throws InterruptedException {
        //队列大小
        ArrayBlockingQueue blockingQueue = new ArrayBlockingQueue<>(3);
        blockingQueue.put("A");
        blockingQueue.put("B");
        blockingQueue.put("C");
        //blockingQueue.put("D");//队列没有位置了,一直阻塞
        System.out.println(blockingQueue.take());
        System.out.println(blockingQueue.take());
        System.out.println(blockingQueue.take());
        System.out.println(blockingQueue.take());//没有这个元素,一直阻塞
    }
}

10.4、超时等待(不抛出异常(offer&&poll))

package bq;

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

public class Test {
    public static void main( String[] args ) throws InterruptedException {
        test4();
    }
    /*
        抛出异常
     */
    public static void test1(){
        ArrayBlockingQueue blockingQueue = new ArrayBlockingQueue<>(3);
        System.out.println(blockingQueue.add("A"));
        System.out.println(blockingQueue.add("B"));
        System.out.println(blockingQueue.add("C"));
        //java.lang.IllegalStateException: Queue full   抛出异常!
        //System.out.println(blockingQueue.add("D"));
        System.out.println("=======================");
        System.out.println(blockingQueue.remove());
        System.out.println(blockingQueue.remove());
        System.out.println(blockingQueue.remove());
        //java.util.NoSuchElementException  抛出异常!
        System.out.println(blockingQueue.remove());
    }
    /*
        有返回值没有异常
     */
    public static void test2(){
        //队列大小
        ArrayBlockingQueue blockingQueue = new ArrayBlockingQueue<>(3);
        System.out.println(blockingQueue.offer("A"));
        System.out.println(blockingQueue.offer("B"));
        System.out.println(blockingQueue.offer("C"));
        //System.out.println(blockingQueue.offer("D"));//false 不抛出异常!
        System.out.println(blockingQueue.peek());   //检查队首元素
        System.out.println("=======================");
        System.out.println(blockingQueue.poll());
        System.out.println(blockingQueue.poll());
        System.out.println(blockingQueue.poll());
        System.out.println(blockingQueue.poll());//null 不抛出异常
    }
    /*
        等待,阻塞(一直等待)
     */
    public static void test3() throws InterruptedException {
        //队列大小
        ArrayBlockingQueue blockingQueue = new ArrayBlockingQueue<>(3);
        blockingQueue.put("A");
        blockingQueue.put("B");
        blockingQueue.put("C");
        //blockingQueue.put("D");//队列没有位置了,一直阻塞
        System.out.println(blockingQueue.take());
        System.out.println(blockingQueue.take());
        System.out.println(blockingQueue.take());
        System.out.println(blockingQueue.take());//没有这个元素,一直阻塞
    }
    /*
        超时等待
     */
    public static void test4() throws InterruptedException {
        //队列大小
        ArrayBlockingQueue blockingQueue = new ArrayBlockingQueue<>(3);
        System.out.println(blockingQueue.offer("A"));
        System.out.println(blockingQueue.offer("B"));
        System.out.println(blockingQueue.offer("C"));
        System.out.println(blockingQueue.offer("D",2, TimeUnit.SECONDS));//等待超过两秒就退出
        System.out.println("=======================");
        System.out.println(blockingQueue.poll());
        System.out.println(blockingQueue.poll());
        System.out.println(blockingQueue.poll());
        System.out.println(blockingQueue.poll(2,TimeUnit.SECONDS));
    }
}

10.5、同步队列(SynchronizedQueue)

  • 没有容量
    进去一个元素,必须等待取出来之后,才能往里面放一个元素!
package bq;

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

//同步队列
/*
    和其他BlockingQueue不一样,SynchronousQueue不存储元素
    put了一个元素,必须从里面先take取出来,否则不能在put进去值
 */
public class SynchronizedQueueDemo {
    public static void main( String[] args ) {
        SynchronousQueue<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、线程池

  • 池化技术
    程序运行,本质:占用系统的资源!优化资源的使用!=》池化技术
    事先准备好一些资源,有人要用,就我这里拿,用完之后还给我
  • 线程池的好处
    1. 降低资源的消耗
    2. 提高响应的速度
    3. 方便管理

线程可以复用、可以控制最大并发数、管理线程

package pool;

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

//Executors 工具类、三大方法
//使用了线程池之后,使用线程池来创建线程
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 < 100; i++) {
                threadPool.execute(()->{
                    System.out.println(Thread.currentThread().getName() + " OK");
                });
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            //线程池用完,程序结束,关闭线程池
            threadPool.shutdown();
        }
    }
}

11.1、源码分析

    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亿
                                      60L, TimeUnit.SECONDS,
                                      new SynchronousQueue<Runnable>());
    }
  • 本质: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;
    }

11.2、自定义线程池

package pool;

import java.util.concurrent.*;

//Executors 工具类、三大方法
//使用了线程池之后,使用线程池来创建线程
public class Demo01 {
    public static void main( String[] args ) {
        //自定义线程池!
        ExecutorService threadPool = new ThreadPoolExecutor(2,
                5,
                3,
                TimeUnit.SECONDS,
                new LinkedBlockingDeque<>(3),
                Executors.defaultThreadFactory(),
                new ThreadPoolExecutor.AbortPolicy());  //阻塞队列满了,还有人进来,不处理这个人的,抛出异常



        try {
            //最大承载:Qeque+max
            //超过    java.util.concurrent.RejectedExecutionException:
            for (int i = 0; i < 9; i++) {
                threadPool.execute(()->{
                    System.out.println(Thread.currentThread().getName() + " OK");
                });
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            //线程池用完,程序结束,关闭线程池
            threadPool.shutdown();
        }
    }
}

11.2.1、四种拒绝策略

  1. new ThreadPoolExecutor.AbortPolicy() //阻塞队列满了,还有人进来,不处理这个人的,抛出异常
  2. new ThreadPoolExecutor.CallerRunsPolicy()); //阻塞队列满了,还有人进来,哪来的去哪里!
  3. new ThreadPoolExecutor.DiscardPolicy()); //阻塞队列满了,还有人进来,丢掉,不抛出异常
  4. new ThreadPoolExecutor.DiscardOldestPolicy()); //阻塞队列满了,还有人进来,把队列最靠前的任务丢弃,重新尝试执行当前任务Java——JUC并发编程_第15张图片

11.2.2、CPU密集型和IO密集型

  1. CPU密集型:几核,就是几,可以保持CPU的效率最高!
  2. IO密集型:>判断程序中十分耗IO线程

12、四大函数式接口(必须掌握)

  • 新时代的程序员:lambda表达式、链式编程、函数式接口、Stream流式计算
  • 函数接口:只有一个方法的接口
    Java——JUC并发编程_第16张图片

12.1、函数型接口和断定型接口

12.1.1、Function函数式接口

  • 代码测试
package function;

import java.util.function.Function;

/*
    Function 函数型接口,有一个输入参数,有一个输出
    只要是函数型接口就可以用lambda表达式简化
 */
public class Demo01 {
    public static void main( String[] args ) {
        //工具类:输入输入的值
//        Function function = new Function() {
//            @Override
//            public String apply( String str ) {
//                return str;
//            }
//        };
        Function<String ,String > function= (str)->{return str;};
        System.out.println(function.apply("123"));
    }
}

12.1.2、Predicate断定型接口

  • 代码演示
package function;

import java.util.function.Predicate;
/*
    断定型接口:有一个参数,返回值只能是 布尔值!
 */
public class Demo02 {
    public static void main( String[] args ) {
        //判断字符串是否为空
        Predicate<String > predicate = (s)->{
            return s.isEmpty();
        };
        System.out.println(predicate.test("123"));
    }
}

12.2、消费型接口和供给型接口

12.2.1、Consumer消费型接口

package function;

import java.util.function.Consumer;

/*
    Consumer 消费型接口:只有输入,没有返回值
 */
public class Demo03 {
    public static void main( String[] args ) {
        Consumer<String > consumer = (str)->{
            System.out.println(str);
        };
        consumer.accept("Java学不会");
    }
}

12.2.2、Supplier供给型接口

package function;

import java.util.function.Supplier;
/*
    Supplier 供给型接口  没有参数,只有返回值
 */
public class Demo04 {
    public static void main( String[] args ) {
        Supplier<Integer> supplier = ()->{return 1024;};
        System.out.println(supplier.get());
    }
}

13、Stream流式计算

什么是Stream流式计算
大数据:存储+计算
存储:集合、MySQL等
计算:都是交个流来操作,流不存储,相当于数据库的查询

  • 实例:

    import java.util.Arrays;
    import java.util.Comparator;
    import java.util.List;
    
    public class Stream_Demo {
        public static void main(String[] args) {
            User u1 = new User("a",20,1);
            User u2 = new User("b",21,2);
            User u3 = new User("c",22,3);
            User u4 = new User("Sky",23,4);
            User u5 = new User("Song",24,5);
    
            List<User> userList = Arrays.asList(u1,u2,u3,u4,u5);
    
            userList.stream()
            		//过滤年龄大于22岁的人
                    .filter((user)->{return user.getAge() > 22;})
                    //返回大写的名字
                    .map((user)->{return user.getName().toUpperCase();})
                    .sorted(Comparator.reverseOrder())//倒序
                    .forEach(System.out::println);//打印
        }
    }
    
    

14、ForkJoin

ForkJoin特点:工作窃取(大数据量时使用)

  • 测试类

    package forkjoin;
    
    import java.util.concurrent.ExecutionException;
    import java.util.concurrent.ForkJoinPool;
    import java.util.concurrent.ForkJoinTask;
    import java.util.stream.LongStream;
    
    public class Test {
        public static void main( String[] args ) throws ExecutionException, InterruptedException {
    //        test1();    //389
    //        test2();    //257
    //        test3();    //210
        }
    
        //普通的
        public static void test1(){
            long start = System.currentTimeMillis();
                //分支合并
                long sum = 0;
                for (long i = 1; i <= 10_0000_0000; i++) {
                    sum+=i;
                }
    
            long end = System.currentTimeMillis();
            System.out.println("sum="+sum+"时间:"+(end-start));
        }
        //forkjoin
        public static void test2() throws ExecutionException, InterruptedException {
            long start = System.currentTimeMillis();
            ForkJoinPool forkJoinPool = new ForkJoinPool();
            ForkJoinTask<Long> task = new Demo01(1, 10_0000_0000);
            ForkJoinTask<Long> submit = forkJoinPool.submit(task);  //提交任务
            Long sum = submit.get();
    
            long end = System.currentTimeMillis();
            System.out.println("sum="+sum+"时间:"+(end-start));
        }
    
        //Stream并行流()
        public static void test3(){
            long start = System.currentTimeMillis();
            long sum = LongStream.rangeClosed(1, 10_0000_0000).parallel().reduce(0, Long::sum);
            long end = System.currentTimeMillis();
            System.out.println("sum="+sum+"时间:"+(end-start));
        }
    }
    
    
  • 测试:

package forkjoin;

import java.util.concurrent.RecursiveTask;

/*
    求和计算
    如何使用forkjoin
        1.forkjoinpool通过它来执行
        2.计算任务
        3.计算类要继承ForkJoin
 */
public class Demo01 extends RecursiveTask<Long> {
    private long start;     //1
    private long end;       //2_0000_0000
    //临界值
    private Long temp = 100000L;

    public Demo01( long start, long end ) {
        this.start = start;
        this.end = end;
    }



    //计算方法
    @Override
    protected Long compute( ) {
        if((end-start)<temp){
            //分支合并
            long sum = 0;
            for (long i = start; i <= end; i++) {
                sum+=i;
            }
            return sum;
        }else { //forkjoin
            long middle = (start+end)/2;           //中间值
            Demo01 demo01 = new Demo01(start, middle);
            demo01.fork();  //拆分任务,把任务亚茹线程队列
            Demo01 demo02 = new Demo01(middle+1, end);
            demo02.fork();  //拆分任务,把任务亚茹线程队列
            return  demo01.join() + demo02.join();
        }
    }
}

15、异步回调

package future;

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

/*
    异步调用:CompletableFuture
        1.异步执行
        2.成功回调
        3.失败回调
 */
public class Demo01 {
    public static void main( String[] args ) throws ExecutionException, InterruptedException {
//        //没有返回值的异步回调 runAsync
//        CompletableFuture completableFuture = CompletableFuture.runAsync(()->{
//            try {
//                TimeUnit.SECONDS.sleep(2);
//            } catch (InterruptedException e) {
//                e.printStackTrace();
//            }
//            System.out.println(Thread.currentThread().getName() + "runAsync=》Void");
//        });
//
//        System.out.println("Java学不会");
//        completableFuture.get();    //获取执行结果

        //有返回值的异步回调 supplyAsync
        CompletableFuture<Integer> completableFuture = CompletableFuture.supplyAsync(()->{
            System.out.println(Thread.currentThread().getName()+"completableFuture");
            int i=10/0;
            return 1024;
        });
        System.out.println(completableFuture.whenComplete(( t, u ) -> {
            System.out.println("t=>" + t);  //正常的返回结果
            System.out.println("u=>" + u);  //错误信息:
        }).exceptionally(( e ) -> {
            e.getMessage();
            return 233;
        }).get());
    }
}

16、JMM

  • JMM:Java内存模型,不存在的东西,概念!约定
  • 关于JMM的一些同步约定
    1. 线程解锁前,必须把共享变量立刻刷回主存。
    2. 线程加锁前,必须读取主存中的最新值到工作内存中!
    3. 加锁和解锁是同一把锁。
  • 八种操作:
    1. lock (锁定):作用于主内存的变量,把一个变量标识为线程独占状态
    2. unlock (解锁):作用于主内存的变量,它把一个处于锁定状态的变量释放出来,释放后的变量才可以被其他线程锁定
    3. read (读取):作用于主内存变量,它把一个变量的值从主内存传输到线程的工作内存中,以便随后的load动作使用
    4. load (载入):作用于工作内存的变量,它把read操作从主存中变量放入工作内存中
    5. use (使用):作用于工作内存中的变量,它把工作内存中的变量传输给执行引擎,每当虚拟机遇到一个需要使用到变量的值,就会使用到这个指令
    6. assign (赋值):作用于工作内存中的变量,它把一个从执行引擎中接受到的值放入工作内存的变量副本中
    7. store (存储):作用于主内存中的变量,它把一个从工作内存中一个变量的值传送到主内存中,以便后续的write使用
    8. write  (写入):作用于主内存中的变量,它把store操作从工作内存中得到的变量的值放入主内存的变量中
  • 代码演示
package tvolatile;

import java.util.concurrent.TimeUnit;

public class JMMDemo {
    private static int num =0;
    public static void main( String[] args ) {//main
        new Thread(()->{    //线程1
            while(num==0){

            }
        }).start();
        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        num=1;
        System.out.println(num);
    }
}
  • 问题: 程序不知道主内存的值已经被修改过了

17、Volatile

Volatile是Java虚拟机提供轻量级的同步机制
1. 保证可见性
2. 不保证原子性
3. 静止命令重排

  • 保证可见性
package tvolatile;

import java.util.concurrent.TimeUnit;

public class JMMDemo {
    //不加Volatile程序就会死循环
    //加玻璃他提了可以保证可见性
    private volatile static int num =0;
    public static void main( String[] args ) {//main
        new Thread(()->{    //线程1   //对主内存的变化不知道
            while(num==0){

            }
        }).start();
        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        num=1;
        System.out.println(num);
    }
}
  • 不保证原子性
    原子性:不可分割。(线程A在执行任务的时候,不能被打扰的,也不能被风格,要么同时成功,要么同时失败)
package tvolatile;

//不保证原子性
public class VDome01 {
    //volatile不保证原子性
    private volatile static int num = 0;
    public static void add(){
        num++;
    }

    public static void main( String[] args ) {
        for (int i = 1; i < 21; i++) {
            new Thread(()->{
                for (int i1 = 0; i1 < 1000; i1++) {
                    add();
                }
            }).start();
        }

        while (Thread.activeCount()>2){	//main gc
            Thread.yield();
        }
        System.out.println(Thread.currentThread().getName() + " " + num);

    }
}

  • 如果不加lock或Synchronized要弄成肉泥则对,怎么保证原子性(使用原子类
package tvolatile;

import java.util.concurrent.atomic.AtomicInteger;

//不保证原子性
public class VDome01 {
    //volatile不保证原子性
    private volatile static AtomicInteger num = new AtomicInteger();
    public static void add(){
        num.getAndIncrement();      //AtomicInteger+1 方法 CAS
    }

    public static void main( String[] args ) {
        for (int i = 1; i < 21; i++) {
            new Thread(()->{
                for (int i1 = 0; i1 < 1000; i1++) {
                    add();
                }
            }).start();
        }

        while (Thread.activeCount()>2){	//main gc
            Thread.yield();
        }
        System.out.println(Thread.currentThread().getName() + " " + num);

    }
}
  • 指令重排

什么是指令重排:你写的程序,计算机并不是按照你写的那样去执行
源代码——>编译器优化的重排——>指令并行也可能会重排——>内存系统也会重排——>执行
处理器在进行指令重排的时候,考虑数据直接的依赖性

  • volatile可以避免指令重排
    内存屏障、cpu指令。作用:
    1. 保证特定的操作执行顺序!
    2. 可以保证某些变量的内存可见性(利用这些特性,就可以保持)

18、单例模式

18.1、饿汉式

package single;
//饿汉式单例
public class Hungry {
    //可能会浪费空间
    private byte[] data1 = new byte[1024*1024];
    private byte[] data2 = new byte[1024*1024];
    private byte[] data3 = new byte[1024*1024];
    private byte[] data4 = new byte[1024*1024];
    private Hungry(){

    }
    private final static Hungry HUNGRY = new Hungry();
    public static Hungry getInstance(){
        return HUNGRY;
    }

}

18.2、DCL懒汉式

package single;

import com.sun.xml.internal.ws.runtime.config.TubelineFeatureReader;

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;

//懒汉式单例
//道高一尺魔高一丈
public class LazyMan {

    private static boolean flag = false;

    private LazyMan(){
        synchronized (LazyMan.class){
            if(flag==false){
                flag= true;
            }else {
                throw new RuntimeException("不要视图通过反射破坏异常");
            }

        }
        System.out.println(Thread.currentThread().getName() + " ok");
    }
    public volatile static LazyMan lazyMan;
    //双重检测锁模式的 懒汉单例模式 DCL懒汉锁
    public static LazyMan getInstance(){
        if(lazyMan==null){
            synchronized (LazyMan.class){
                if(lazyMan==null){
                    lazyMan = new LazyMan();    //不防止指令重排,需要加volatile
                    /*
                        1.分配内存
                        2.执行构造方法,初始化对象
                        3.把这个对象指向这个空间

                        123
                        132 A
                            B   //此时lazyMan还没有完成构造
                     */
                }
            }
        }

        return lazyMan;
    }
    //反射
    public static void main( String[] args ) throws Exception {
//        LazyMan instance = LazyMan.getInstance();

        Field flag = LazyMan.class.getDeclaredField("flag");
        flag.setAccessible(true);

        Constructor<LazyMan> declaredConstructor = LazyMan.class.getDeclaredConstructor(null);
        declaredConstructor.setAccessible(true);

        LazyMan instance = declaredConstructor.newInstance();

        flag.set(instance,false);

        LazyMan instance2 = declaredConstructor.newInstance();
        System.out.println(instance);
        System.out.println(instance2);
    }
}

18.3、静态内部类

package single;
//静态内部类
public class Holder {
    private Holder(){

    }
    public static Holder getInstance(){
        return InnerClass.HOLDER;
    }
    public static class InnerClass{
        private static final Holder HOLDER = new Holder();
    }

}

18.4、枚举

package single;

import java.lang.reflect.Constructor;

//enum 是一个什么? 本身也是一个Class类
public enum EnumSingle {
    INSTANCE;
    public EnumSingle getInstance(){
        return INSTANCE;
    }
}

class Tesst{
    public static void main( String[] args ) throws NoSuchMethodException {
        EnumSingle instance1 = EnumSingle.INSTANCE;
        EnumSingle instance2 = EnumSingle.INSTANCE;
        Constructor<EnumSingle> declaredConstructor = EnumSingle.class.getDeclaredConstructor();
        declaredConstructor.setAccessible(true);
        System.out.println(instance1);
        System.out.println(instance2);
    }
}

19、理解CAS

什么是CAS

package cas;

import java.util.concurrent.atomic.AtomicInteger;

public class CASDemo {

    //CAS   compareAndSet : 比较并交换!
    public static void main( String[] args ) {
        AtomicInteger atomicInteger = new AtomicInteger(2022);
        //期望、更新
        //public final boolean compareAndSet(int expect, int update)
        //如果期望的值达到了,那么就更新,否则不更新 CAS 是CPU的并发原因
        System.out.println(atomicInteger.compareAndSet(2022, 2023));
        System.out.println(atomicInteger.get());
        
        System.out.println(atomicInteger.compareAndSet(2022, 2024));
        System.out.println(atomicInteger.get());
    }
}
  • Unsafe类
    Java——JUC并发编程_第17张图片- CAS: 比较当前工作内存中的值和内存中的值,如果智哥是期望的,那么执行操作!如果不是一直循环!
    1. 缺点:
      1. 循环会耗时
      2. 一次性只能保证一个共享变量的原子性

19.1、ABA问题(狸猫换太子)

package cas;

import java.util.concurrent.atomic.AtomicInteger;

public class CASDemo {

    //CAS   compareAndSet : 比较并交换!
    public static void main( String[] args ) {
        AtomicInteger atomicInteger = new AtomicInteger(2022);
        

        //期望、更新
        //public final boolean compareAndSet(int expect, int update)
        //如果期望的值达到了,那么就更新,否则不更新 CAS 是CPU的并发原因
        //===========捣乱的线程===========
        System.out.println(atomicInteger.compareAndSet(2022, 2018));
        System.out.println(atomicInteger.get());
        System.out.println(atomicInteger.compareAndSet(2018, 2022));
        System.out.println(atomicInteger.get());
		
		//===========期望的线程===========
        System.out.println(atomicInteger.compareAndSet(2022, 2020));
        System.out.println(atomicInteger.get());
    }
}

20、原子应用

解决ABA问题,引入原子引用!对应的思想:乐观锁

package cas;

import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicStampedReference;

public class CASDemo {

    //CAS   compareAndSet : 比较并交换!
    public static void main( String[] args ) {
//        AtomicInteger atomicInteger = new AtomicInteger(2022);
        //AtomicStampedReference 注意:如果泛型是一个包装类,注意对象的引用问题
        AtomicStampedReference<Integer> atomicStampedReference = new AtomicStampedReference<>(100,1);

        new Thread(()->{
            try {
                TimeUnit.SECONDS.sleep(5);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            int stamp = atomicStampedReference.getStamp();//获得版本号
            System.out.println("a1=>" + stamp);

            System.out.println(atomicStampedReference.compareAndSet(100, 101,
                    atomicStampedReference.getStamp(), atomicStampedReference.getStamp() + 1));
            System.out.println("a2=>" + atomicStampedReference.getStamp());

            System.out.println(atomicStampedReference.compareAndSet(101, 100,
                    atomicStampedReference.getStamp(), atomicStampedReference.getStamp() + 1));
            System.out.println("a3=>" + atomicStampedReference.getStamp());
        },"a").start();


        new Thread(()->{
            System.out.println("===============");
            int stamp = atomicStampedReference.getStamp();//获得版本号
            System.out.println("b1=>" + stamp);
            try {
                TimeUnit.SECONDS.sleep(1);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(atomicStampedReference.compareAndSet(100, 106,
                    atomicStampedReference.getStamp(), atomicStampedReference.getStamp() + 1));
            System.out.println("b2=>" + atomicStampedReference.getStamp());
            System.out.println(atomicStampedReference.getReference());
        },"b").start();
    }
}

21、可重入锁

21.1、Synchronized

package lock;

import java.util.concurrent.TimeUnit;

//Synchronized
public class Demo01 {
    public static void main( String[] args ) {
        Phone phone = new Phone();
        new Thread(()->{
            phone.sms();
        },"A").start();
        new Thread(()->{
            phone.sms();
        },"B").start();
    }
}

class Phone{
    public synchronized void sms(){
        System.out.println(Thread.currentThread().getName() + " sms");

        call();     //这里也有锁
    }
    public synchronized void call(){
        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(Thread.currentThread().getName() + " call");
    }
}

21.2、Lock

package lock;

import java.util.Locale;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class Demo02 {
    public static void main( String[] args ) {
        Phone2 phone = new Phone2();
        new Thread(()->{
            phone.sms();
        },"A").start();
        new Thread(()->{
            phone.sms();
        },"B").start();
    }
}

class Phone2{
    Lock lock = new ReentrantLock();
    public  void sms(){
        lock.lock();    //Lock锁必须配对,否则会死锁
        try {
            System.out.println(Thread.currentThread().getName() + " sms");

            call();     //这里也有锁
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }
    public void call(){
        lock.lock();
        try {
            try {
                TimeUnit.SECONDS.sleep(1);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName() + " call");
        } finally {
            lock.unlock();
        }
    }
}


22、自旋锁

  • 自旋锁
package lock;

import java.util.concurrent.atomic.AtomicReference;

/*
    自旋锁
 */
public class SpinlockDemo {
    AtomicReference<Thread> atomicReference = new AtomicReference<>();
    //加锁
    public void myLock(){
        Thread thread = Thread.currentThread();


        //自旋锁
        while (!atomicReference.compareAndSet(null, thread)) {

        }
        System.out.println(thread.getName()+"=======>myLock");
    }
    //减锁
    public void myUnLock(){
        Thread thread = Thread.currentThread();

        atomicReference.compareAndSet(thread, null);
        System.out.println(thread.getName() + "======>myUnLock");
    }
}

  • 测试
package lock;

import java.util.concurrent.TimeUnit;

public class TestSpinLock {
    public static void main( String[] args ) {
        //底层使用的自旋锁CAS
        SpinlockDemo lock = new SpinlockDemo();

        new Thread(()->{
            lock.myLock();
            try {
                TimeUnit.SECONDS.sleep(3);
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                lock.myUnLock();
            }
        },"T1").start();
        new Thread(()->{
            lock.myLock();
            try {
                TimeUnit.SECONDS.sleep(1);
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                lock.myUnLock();
            }
        },"T2").start();


    }
}

24、死锁

如何排查死锁
当我们出现死锁的时候,首先需要使用jps -l命令查看运行的程序
我们能看到DeadLockDemo这个类,一直在运行
Java——JUC并发编程_第18张图片
在使用jstack查看堆栈信息
通过查看最后一行,我们看到 Found 1 deadlock,即存在一个死锁
Java——JUC并发编程_第19张图片
Java——JUC并发编程_第20张图片

你可能感兴趣的:(Java笔记,juc,java)