多线程进阶==JUC并发编程狂神说

多线程进阶==>JUC并发编程

视频地址

1.什么是JUC

java.util工具包

业务: 普通的线程代码, 之前都是用的thread或者runnable接口

但是相比于callable来说,thread没有返回值,且效率没有callable高

2. 线程和进程

线程,进程

进程 : 一个运行中的程序的集合; 一个进程往往可以包含多个线程,至少包含一个线程

java默认有几个线程? 两个 main线程 gc线程

线程 : 线程(thread)是操作系统能够进行运算调度的最小单位。

对于java而言如何创建thread: 继承自thread,实现runnable接口,实现callable接口

Java真的可以开启线程吗? 开不了的,底层是用native关键词修饰.调用本地实现

    public synchronized void start() {
        /**
         * This method is not invoked for the main method thread or "system"
         * group threads created/set up by the VM. Any new functionality added
         * to this method in the future may have to also be added to the VM.
         *
         * A zero status value corresponds to state "NEW".
         */
        if (threadStatus != 0)
            throw new IllegalThreadStateException();

        /* Notify the group that this thread is about to be started
         * so that it can be added to the group's list of threads
         * and the group's unstarted count can be decremented. */
        group.add(this);

        boolean started = false;
        try {
            start0();
            started = true;
        } finally {
            try {
                if (!started) {
                    group.threadStartFailed(this);
                }
            } catch (Throwable ignore) {
                /* do nothing. If start0 threw a Throwable then
                  it will be passed up the call stack */
            }
        }
    }
	//本地方法,调用底层c++, java无法操作硬件
    private native void start0();

并发,并行

并发编程: 并发和并行

并发(多线程操作同一个资源,交替执行)

  • CPU一核, 模拟出来多条线程,天下武功,唯快不破,快速交替

并行(多个人一起行走, 同时进行)

  • CPU多核,多个线程同时进行 ; 使用线程池操作
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来自线程类

  2. 关于锁的释放

    ​ wait会释放锁, sleep不会释放锁

  3. 使用的范围不同

    ​ wait必须在同步代码块中

    ​ sleep可以在任何地方睡

  4. 是否需要捕获异常

    ​ wait不需要捕获异常

    ​ sleep需要捕获异常

3. Lock锁(重点)

传统synchronized

本质: 队列和锁,放在方法上锁的是this,放在代码块中锁的是()里面的对象

synchronized(obj){
    
}

Lock 接口

实现类

reentrantLock构造器

  	public ReentrantLock() {
        sync = new NonfairSync(); //无参默认非公平锁
    }
 	public ReentrantLock(boolean fair) {
        sync = fair ? new FairSync() : new NonfairSync();//传参为true为公平锁
    }

公平锁: 十分公平: 可以先来后到,一定要排队

非公平锁: 十分不公平,可以插队(默认)

public class SaleTicketDemo {

    public static void main(String[] args) {
        Ticket ticket = new Ticket();

        new Thread(()->{for(int i = 0; i < 40; i++) ticket.sale();}, "a").start();
        new Thread(()->{for(int i = 0; i < 40; i++) ticket.sale();}, "b").start();
        new Thread(()->{for(int i = 0; i < 40; i++) ticket.sale();}, "c").start();

    }
}

class Ticket {

    private int ticketNum = 30;
    private Lock lock = new ReentrantLock();

    public void sale() {
        lock.lock();
        try {
            if (this.ticketNum > 0) {
                System.out.println(Thread.currentThread().getName() + "购得第" + ticketNum-- + "张票, 剩余" + ticketNum + "张票");
            }
            //增加错误的发生几率
            Thread.sleep(10);
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }

}

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. 生产者和消费者问题

面试高频: 单例模式, 八大排序,生产者消费者,死锁

Synchronized实现 wait notify

package com.czp.syncconsumer;

public class Test {

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

        new Thread(()->{
            for (int i = 0; i < 20; i++) {
                try {
                    data.increment();
                    System.out.println(Thread.currentThread().getName() + "让Num增加, num => " + data.getNum());
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }

            }
        }).start();
        new Thread(()->{
            for (int i = 0; i < 20; i++) {
                try {
                    data.decrement();
                    System.out.println(Thread.currentThread().getName() + "让Num减少, num => " + data.getNum());
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }

            }
        }).start();

    }
}

class Data {

    private int num = 0;

    public int getNum() {
        return num;
    }

    public synchronized void increment() throws InterruptedException {
        if (num != 0) {
            this.wait();
        }
        num++;
        this.notifyAll();
    }

    public synchronized void decrement() throws InterruptedException {
        if (num == 0) {
            this.wait();
        }
        num--;
        this.notifyAll();
    }
}

问题存在,ABCD4个线程是否安全, 不安全 会有虚假唤醒

在这里插入图片描述

if判断改为while判断

因为if只会执行一次,执行完会接着向下执行if()外边的

而while不会,直到条件满足才会向下执行while()外边的

JUC版本生产者和消费者问题

在这里插入图片描述

在这里插入图片描述

任何一个新的技术,绝对不是仅仅覆盖了原来的技术,一定有优势和补充

Condition 精准的通知和唤醒线程
在这里插入图片描述

5. 8锁现象

package com.czp.lock;

import java.util.concurrent.TimeUnit;

/**
 * 8锁就是关于锁的八个问题
 * 1. 标准情况下,两个线程先打印 发短信 还是打电话
 * AsendMessage
 * Bcall
 *  synchronized 放在方法上锁的是方法调用者
 *
 */
public class Test1 {

    public static void main(String[] args) {
        Phone phone = new Phone();

        new Thread(()->{
            phone.sendMessage();
        },"A").start();

        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        new Thread(()->{
            phone.call();
        },"B").start();
    }
}

class Phone{

    public synchronized void sendMessage(){
        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(Thread.currentThread().getName() + "sendMessage");
    }

    public synchronized void call(){
        System.out.println(Thread.currentThread().getName() + "call");
    }
}
package com.czp.lock;

import java.util.concurrent.TimeUnit;

/**
 * 增加了一个普通方法
 * 3. 发短信还是hello
 *
 * 4.
 */
public class Test2 {
    public static void main(String[] args) {
        Phone1 phone = new Phone1();
        Phone1 phone2 = new Phone1();

        new Thread(() -> {
            phone.sendMessage();
        }, "A").start();

        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        new Thread(() -> {
//            phone.hello();
            phone2.call();
        }, "B").start();
    }
}

class Phone1 {

    public synchronized void sendMessage() {
        try {
            TimeUnit.SECONDS.sleep(4);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(Thread.currentThread().getName() + "sendMessage");
    }

    public synchronized void call() {
        System.out.println(Thread.currentThread().getName() + "call");
    }
    //这里没有锁,不受锁的影响
    public void hello(){
        System.out.println("hello");
    }
}
package com.czp.lock;

import java.util.concurrent.TimeUnit;

/**
 * 5.增加两个静态的同步方法
 * synchronized 锁的是方法调用者
 * static 静态方法类一加载就有了 锁的是CLass
 *
 * 6. 两个对象,两个静态同步方法,先发短信还是先打电话
 *
 */
public class Test3 {

    public static void main(String[] args) {
        Phone2 phone2 = new Phone2();
        Phone2 phone3 = new Phone2();

        new Thread(() -> {
            phone2.sendMessage();
        }, "A").start();

        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        new Thread(() -> {
            phone3.call();
        }, "B").start();
    }
}
class Phone2 {

    public static synchronized void sendMessage() {
        try {
            TimeUnit.SECONDS.sleep(4);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(Thread.currentThread().getName() + "sendMessage");
    }

    public static synchronized void call() {
        System.out.println(Thread.currentThread().getName() + "call");
    }
}
package com.czp.lock;

import java.util.concurrent.TimeUnit;

/**
 * 7.一个静态同步方法,一个普通同步方法 ,一个对象,先输出哪一个
 *
 * 8. 一个静态同步方法,一个普通同步方法, 两个对象,先打印哪一个
 */
public class Test4 {
    public static void main(String[] args) {
        Phone3 phone3 = new Phone3();
        Phone3 phone4 = new Phone3();

        new Thread(() -> {
            phone3.sendMessage();
        }, "A").start();

        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        new Thread(() -> {
            phone4.call();
        }, "B").start();
    }
}

class Phone3 {

    public static synchronized void sendMessage() {
        try {
            TimeUnit.SECONDS.sleep(4);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(Thread.currentThread().getName() + "sendMessage");
    }

    //普通同步方法
    public synchronized void call() {
        System.out.println(Thread.currentThread().getName() + "call");
    }


}

小结

new this 具体的一个手机

static Class 唯一的模板

6.集合类不安全

list 不安全

//java.util.ConcurrentModificationException 并发修改异常!
public class ListTest {

    public static void main(String[] args) {
        //并发下 arrayList 是不安全的
        /**
         * 解决方案
         * 1. 使用vector解决
         * 2. List<String> arrayList = Collections.synchronizedList(new ArrayList<>());
         * 3. List<String> arrayList = new CopyOnWriteArrayList<>();
         */
        //copyOnWrite 写入时复制  COW 计算机程序设计领域的一种优化策略
        //多个线程调用的时候, list, 读取的时候固定的,写入的时候,可能会覆盖
        //在写入的时候避免覆盖造成数据问题
        //CopyOnWriteArrayList 比 vector牛逼在哪里

        //读写分离
        List<String> arrayList = new CopyOnWriteArrayList<>();
        for (int i = 0; i < 100; i++) {
            new Thread(()->{
                arrayList.add(UUID.randomUUID().toString().substring(0,5));
                System.out.println(arrayList);
            },String.valueOf(i)).start();
        }
    }
}

set 不安全

/**
 * 同理可证
 */
public class SetTest {

    public static void main(String[] args) {

//        Set<String> set = new HashSet<>();
        //如何解决hashSet线程安全问题
        //1. Set<String> set = Collections.synchronizedSet(new HashSet<>());
        Set<String> set = new CopyOnWriteArraySet<>();

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

hashSet底层是什么? hashMap

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

// add 的本质就是 map 的 key key是无法重复的
public boolean add(E e) {
    return map.put(e, PRESENT)==null;
}
private static final Object PRESENT = new Object();//这是一个不变的值

HashMap 不安全

map的基本操作

7. Callable()

在这里插入图片描述

  1. 可以有返回值
  2. 可以抛出异常
  3. 方法不同, run() => call()

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

public class CallableTest {

    public static void main(String[] args) throws ExecutionException, InterruptedException {
        FutureTask<Integer> futureTask = new FutureTask<>(new MyThread());
        new Thread(futureTask,"a").start();
        System.out.println(futureTask.get());

    }
}

class MyThread implements Callable<Integer> {


    @Override
    public Integer call() throws Exception {
        System.out.println("call()方法被调用了");
        return 1024;
    }
}

8. 常用的辅助类

8.1 CountDownLatch

在这里插入图片描述

//计数器
public class CountDownLatchDemo {

    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("close Door");
    }


}

原理:

countDownLatch.countDown(); //数量减1

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

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

8.2 cyclicBarrier

在这里插入图片描述

加法计数器

public class CyclicBarrierDemo {

    public static void main(String[] args) {
        /**
         * 集齐77个龙珠召唤神龙
         */
        // 召唤龙珠的线程
        CyclicBarrier cyclicBarrier = new CyclicBarrier(7, ()->{
            System.out.println("召唤神龙成功! ");
        });
        for (int i = 0; i < 7; i++) {
            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();
        }
    }
}

8.3 Semaphore

在这里插入图片描述

public class SemaphoreTest {

    public static void main(String[] args) {
        Semaphore semaphore = new Semaphore(3);

        for (int i = 0; i < 6; i++) {
            int temp = i;
            new Thread(()->{
                try {
                    semaphore.acquire(); //获取
                    System.out.println(temp + "号车抢到车位");
                    TimeUnit.SECONDS.sleep(5);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } finally {
                    semaphore.release(); //释放
                    System.out.println(temp + "号车离开车位");
                }
            }).start();
        }
    }
}

原理:

semaphore.acquire(); //获取信号量,假设如果已经满了,等待信号量可用时被唤醒

semaphore.release(); //释放信号量

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

9.读写锁

ReadWriteLock

在这里插入图片描述

package com.czp.lock;

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


/**
 * 独占锁(写锁) 一次只能由一个线程占有
 * 共享锁(读锁) 一次可以有多个线程占有
 * readWriteLock
 * 读-读 可以共存
 * 读-写  不能共存
 * 写-写 不能共存
 */
public class ReadWriteLock {

    public static void main(String[] args) {
        MyCacheLock myCache = new MyCacheLock();

        //写入操作
        for (int i = 0; i < 6; i++) {
            int temp = i;
            new Thread(() -> {
                myCache.put(temp + "", temp + "");
            }, String.valueOf(i)).start();
        }

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

class MyCacheLock {

    private volatile Map<String, Object> map = new HashMap<>();
    //读写锁
    private java.util.concurrent.locks.ReadWriteLock lock = new ReentrantReadWriteLock();

    // 存,写入的时候只有一个人操作
    public Object get(String key) {
        lock.readLock().lock();
        Object o = null;
        try {
            System.out.println(Thread.currentThread().getName() + "读取");
            try {
                TimeUnit.SECONDS.sleep(2);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            o = map.get(key);
            System.out.println(Thread.currentThread().getName() + "读取ok" + o);
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            lock.readLock().unlock();
        }
        return o;
    }

    public void put(String key, Object value) {
        lock.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 {
            lock.writeLock().unlock();
        }
    }

}

class MyCache {

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

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

        Object o = map.get(key);
        System.out.println(Thread.currentThread().getName() + "读取ok" + o);

        return o;
    }

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

}

10.阻塞队列

在这里插入图片描述

阻塞队列

在这里插入图片描述

Blockqueue

在这里插入图片描述
在这里插入图片描述

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

学会使用队列

添加,移除

四组API

方式抛出异常不会抛出异常,有返回值阻塞等待超时等待
添加操作add()offer() 供应put()offer(obj,int,timeunit.status)
移除操作remove()poll() 获得take()poll(int,timeunit.status)
判断队列首部element()peek() 偷看,偷窥

   /**
     * 抛出异常
     */
    public static void test1() {
        //队列的大小
        ArrayBlockingQueue queue = new ArrayBlockingQueue<>(3);
        System.out.println(queue.add("a"));
        System.out.println(queue.add("b"));
        System.out.println(queue.add("c"));
        //java.lang.IllegalStateException: Queue full
        //System.out.println(queue.add("d"));
        System.out.println("----------------------");
        System.out.println(queue.remove());
        System.out.println(queue.remove());
        System.out.println(queue.remove());
        //java.util.NoSuchElementException
        System.out.println(queue.remove());
        //抛出异常

    }
   /**
     * 有返回值没有异常
     */
    public static void test2(){
        ArrayBlockingQueue queue = new ArrayBlockingQueue(3);

        System.out.println(queue.offer("a"));
        System.out.println(queue.offer("b"));
        System.out.println(queue.offer("c"));
//        System.out.println(queue.offer("d"));       //offer 不抛出异常
        System.out.println(queue.poll());
        System.out.println(queue.poll());
        System.out.println(queue.poll());
//        System.out.println(queue.poll());   //null 不抛出异常
    }
    /**
     * 等待阻塞
     */
    public static void test3() throws InterruptedException {
        ArrayBlockingQueue queue = new ArrayBlockingQueue(3);
        queue.put("a");
        queue.put("b");
        queue.put("c");
//        queue.put("c");  队列没有位置就会阻塞
        System.out.println(queue.take());
        System.out.println(queue.take());
        System.out.println(queue.take());
    }

SynchronizedQueue 同步队列

没有容量,

进去一个元素,必须等待取出来之后,才能再往里面放一个元素

put take


/**
 * 同步队列
 * 和其他的lockQueue 不一样, SynchronousQueue 不存储元素
 */
public class SyncQueue {

    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();
            } finally {
            }
        },"T2").start();
    }
}

11. 线程池

线程池: 三大方法,七大参数,4种拒绝策略

池化技术

程序的运行,本质: 占用系统的资源 ! 优化CPU资源的使用 ===>池化技术

线程池, 连接池, 内存池, 对象池///…

池化技术: 实现准备好一些资源, 有人要用,就来我这里拿,用完之后还给我

线程池的好处:

  1. 降低资源消耗
  2. 提高响应速度
  3. 方便管理

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

线程池: 三大方法

//Executors 工具类
//使用了线程池之后要使用线程池创建线程
public class Demo01 {

    public static void main(String[] args) {
//        ExecutorService service = Executors.newSingleThreadExecutor();//单个线程
//        ExecutorService service = Executors.newFixedThreadPool(5);//创建一个固定的线程池的大小
        ExecutorService service = Executors.newCachedThreadPool();//可伸缩的,
        try {
            for (int i = 0; i < 10; i++) {
                service.execute(() -> {
                    System.out.println(Thread.currentThread().getName() + "ok");
                });
            }
            //线程池用完要关闭线程池
        } finally {
            service.shutdown();
        }
    }
}

7大参数

newSingleThreadExecutor构造器

public static ExecutorService newSingleThreadExecutor() {
        return new FinalizableDelegatedExecutorService
            (new ThreadPoolExecutor(1, 1,
                                    0L, TimeUnit.MILLISECONDS,
                                    new LinkedBlockingQueue<Runnable>()));
    }

newFixedThreadPool构造器

 public static ExecutorService newFixedThreadPool(int nThreads) {
        return new ThreadPoolExecutor(nThreads, nThreads,
                                      0L, TimeUnit.MILLISECONDS,
                                      new LinkedBlockingQueue<Runnable>());
    }

newCachedThreadPool构造器

public static ExecutorService newCachedThreadPool() {
        return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
                                      60L, TimeUnit.SECONDS,
                                      new SynchronousQueue<Runnable>());
    }

本质 所有线程池最终都调用的ThreadPoolExecutor

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.corePoolSize = corePoolSize;
        this.maximumPoolSize = maximumPoolSize;
        this.workQueue = workQueue;
        this.keepAliveTime = unit.toNanos(keepAliveTime);
        this.threadFactory = threadFactory;
        this.handler = handler;
    }

手动创建线程池

//自定义线程池,创建默认的线程工厂
        ExecutorService threadPool = new ThreadPoolExecutor(3, //核心线程池大小
                                                        5, //最大并发数
                                                        10, //超时时间
                                                        TimeUnit.SECONDS, //时间单位
                                                        new LinkedBlockingQueue<>(),//线程等候队列
                                                        Executors.defaultThreadFactory(), //线程创建工厂
                                                        new ThreadPoolExecutor.DiscardOldestPolicy());//拒绝策略

四种默认拒绝策略的实现类

在这里插入图片描述

/**
 * new ThreadPoolExecutor.AbortPolicy() 超出最大处理线程抛出异常
 * new ThreadPoolExecutor.CallerRunsPolicy() 从哪个线程创建就由那个线程执行
 * new ThreadPoolExecutor.DiscardPolicy() 队列满了不会抛出异常
 * new ThreadPoolExecutor.DiscardOldestPolicy() 尝试去和第一个竞争,也不会抛出异常
 */

小结和扩展

了解:最大线程到底应该如何定义

  1. CPU密集型 几核,就是几,可以保证CPU的效率最高
  2. IO 密集型 判断程序中十分耗I/O的线程, 大于两倍

12. java新特性

请参考java1.8新特性

13. ForkJoin

什么是ForkJoin

ForkJoin在JDK1.7,并行执行任务,大数据量!

大数据: Map Reduce( 把大任务拆分成小任务)

在这里插入图片描述

ForkJoin特点: 工作窃取

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

在这里插入图片描述

ForkJoin

在这里插入图片描述

package com.czp.forkjoin;

import java.time.Duration;
import java.time.Instant;
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) {
//        test1(1L, 200_0000_0000L);//执行时间为10570毫秒  sum = -2914184820805067776
        test2(1L, 200_0000_0000L);//执行时间为202979毫秒   sum = -2935156330807653376
//        test3(1L, 200_0000_0000L);//执行时间为15894毫秒   sum = -2914184800805067776
    }

    public static void test1(long start, long end) {
        Instant instant = Instant.now();
        long sum = 0;
        for (long i = start; i < end; i++) {
            sum += i;
        }
        Instant instant1 = Instant.now();
        Duration duration = Duration.between(instant, instant1);
        System.out.println("执行时间为" + duration.toMillis() + "毫秒");
        System.out.println("sum = " + sum);

    }

    public static void test2(long start, long end) {
        Instant instant = Instant.now();
        ForkJoinPool joinPool = new ForkJoinPool();
        ForkJoinTask<Long> task = new ForkJoinDemo(start, end);
        ForkJoinTask<Long> result = joinPool.submit(task);//提交任务
        Long sum = null;
        try {
            sum = result.get();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        }
        Instant instant1 = Instant.now();
        Duration duration = Duration.between(instant, instant1);
        System.out.println("执行时间为" + duration.toMillis() + "毫秒");
        System.out.println("sum = " + sum);

    }


    // stream 并行流
    public static void test3(Long start, Long end) {
        Instant instant = Instant.now();
        //range() 开区间   rangeClosed() 闭区间左开右闭
        long sum = LongStream.rangeClosed(start, end).parallel().reduce(0, Long::sum);
        Instant instant1 = Instant.now();
        Duration duration = Duration.between(instant, instant1);
        System.out.println("执行时间为" + duration.toMillis() + "毫秒");
        System.out.println("sum = " + sum);

    }
}


/**
 * 求和计算的任务
 * 1. forkjoinpool 通过它来执行
 * 2. 计算任务forkJoinPool, execute(forkjoinTask task)
 * 3. 计算类要继承自forkjointask
 */
public class ForkJoinDemo extends RecursiveTask<Long> {

    private long start;     // 1
    private long end;       // 20_0000_0000
    private long temp = 1_0000L;

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

    //计算方法
    @Override
    protected Long compute() {
        if (end - start < temp) {

            Long sum = 0L;
            for (Long i = start; i < end; i++) {
                sum += i;
            }
            return sum;
        } else {
            //分支合并计算
            long middle = (end + start) / 2;  //中间值
            ForkJoinDemo task1 = new ForkJoinDemo(start, middle);
            task1.fork(); // 拆分任务,把任务压入线程队列
            ForkJoinDemo task2 = new ForkJoinDemo(middle + 1, end);
            task2.fork();// 拆分任务,把任务压入线程队列


            return task1.join() + task2.join();
        }
    }
}

14. 异步回调

Future设计的初衷: 对将来的某个事件的结果进行建模

在这里插入图片描述

/**
 * 异步调用: CompletableFuture
 * 异步执行
 * 成功回调
 * 失败回调
 */
public class Demo01 {

    public static void main(String[] args) throws ExecutionException, InterruptedException {
        // 发起一个请求

//        CompletableFuture future = CompletableFuture.runAsync(()->{
//            System.out.println("发送一个没有返回值的异步请求");
//        });
//
//        System.out.println("1111");
//
//        future.get(); //获取执行结果

        //callable就可以实现异步调用,为什么要使用CompletableFuture呢,
        //CompletableFuture更加强大, callable只有成功回调
        //CompletableFuture可以有成功回调,异常(失败)回调
//        ExecutorService service = Executors.newCachedThreadPool();
//        Future<?> submit = service.submit(() -> {
//            System.out.println("这个是callable实现的异步调用,异步是目的,多线程是实现方式!!!");
//        });
//        System.out.println("1111");
//        submit.get();   //获取执行结果


        CompletableFuture<Integer> completableFuture = CompletableFuture.supplyAsync(() -> {
            System.out.println("供应型接口");
            return 1024;
        });

        //返回的结果
        Integer integer = completableFuture.whenComplete((t, u) -> {
            //成功方法
            System.out.println(t); //正常的返回结果
            //出现的异常
            System.out.println(u); // 错误信息
        }).exceptionally((e) -> {
            //失败方法
            e.printStackTrace();
            e.getMessage();
            return 111;
        }).get();

    }
}

15. JMM

请你谈谈你对Volate的理解

Volate是java虚拟机提供轻量级的同步机制

  1. 保证可见性
  2. 不保证原子性
  3. 禁止指令重排

JMM是什么

JMM: java内存模型,不存在的东西,概念!约定!

关于JMM的一些同步的约定

  1. 线程解锁前,必须把共享变量立刻刷回主存
  2. 线程加锁前,必须读取主存中的最新值到工作内存中!
  3. 加锁和解锁必须是同一把锁

线程 工作内存 主内存

在这里插入图片描述

在这里插入图片描述

操作说明
lock (锁定):作用于主内存的变量,把一个变量标识为线程独占状态
unlock (解锁):作用于主内存的变量,它把一个处于锁定状态的变量释放出来,释放后的变量才可以被其他线程锁定
read (读取):作用于主内存变量,它把一个变量的值从主内存传输到线程的工作内存中,以便随后的load动作使用
load (载入):作用于工作内存的变量,它把read操作从主存中变量放入工作内存中
use (使用):作用于工作内存中的变量,它把工作内存中的变量传输给执行引擎,每当虚拟机遇到一个需要使用到变量的值,就会使用到这个指令
assign (赋值):作用于工作内存中的变量,它把一个从执行引擎中接受到的值放入工作内存的变量副本中
store (存储):作用于主内存中的变量,它把一个从工作内存中一个变量的值传送到主内存中,以便后续的write使用
write  (写入):作用于主内存中的变量,它把store操作从工作内存中得到的变量的值放入主内存的变量中

八种规则

  • 不允许read和load、store和write操作之一单独出现。即使用了read必须load,使用了store必须write

  • 不允许线程丢弃他最近的assign操作,即工作变量的数据改变了之后,必须告知主存

  • 不允许一个线程将没有assign的数据从工作内存同步回主内存

  • 一个新的变量必须在主内存中诞生,不允许工作内存直接使用一个未被初始化的变量。就是怼变量实施use、store操作之前,必须经过assign和load操作

  • 一个变量同一时间只有一个线程能对其进行lock。多次lock后,必须执行相同次数的unlock才能解锁

  • 如果对一个变量进行lock操作,会清空所有工作内存中此变量的值,在执行引擎使用这个变量前,必须重新load或assign操作初始化变量的值

  • 如果一个变量没有被lock,就不能对其进行unlock操作。也不能unlock一个被其他线程锁住的变量

  • 对一个变量进行unlock操作之前,必须把此变量同步回主内存

问题: 程序不知道主内存中的值已经被修改过了

16. Volatile

保证可见性

public class JMMDemo {
    //不加volatile 程序就会死循环
    //加上volatile 可以保证可见性
    private volatile static int number = 0;

    public static void main(String[] args) {


        new Thread(()->{
            while(number == 0){

            }
        }).start();

        try {
            TimeUnit.SECONDS.sleep(2);
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
        }

        number = 1;

        System.out.println(number);
    }
}
  1. 不保证原子性

原子性: 不可分割

线程A在执行任务的时候.不能被打扰,也不能被分割,要么同时成功,要么同时失败

//测试不保证原子性
public class VDemo {

    private volatile static int num = 0;

    public static void main(String[] args) {

        for (int i = 0; i < 20; i++) {

            new Thread(()->{
                for (int j = 0; j < 1000; j++) {
                    add();
                }
            }).start();
        }

        while (Thread.activeCount() > 2){
            Thread.yield();
        }
        System.out.println(num);

    }

    public static void add(){
        num++;
    }
}

如果不加lock和synchronized,如何保证原子性

在这里插入图片描述

使用原子类,解决原子性问题

在这里插入图片描述

//测试不保证原子性
public class VDemo {

    //原子类的int
    private volatile static AtomicInteger num = new AtomicInteger(0);
    // AtomicInteger 调用的是底层的 CAS

    public static void main(String[] args) {

        for (int i = 0; i < 20; i++) {

            new Thread(()->{
                for (int j = 0; j < 1000; j++) {
                    add();
                }
            }).start();
        }

        while (Thread.activeCount() > 2){
            Thread.yield();
        }
        System.out.println(num);

    }

    public static void add(){
        num.getAndIncrement();
    }
}

这些类的底层都直接和操作系统挂钩 ! 在内存中修改值! UnSafe类是一个很特殊的存在

指令重排

什么是指令重排: 你写的程序,计算机并不是按照指定的的步骤执行

源代码—>编译器优化源代码–>指令并行也可能会重排—>内存系统也会重排 执行

int x = 1;
int y = 2;
x += 5;
y = 2 * x;

我们期望的是: 1234, 2134, 1324

可能造成影响的结果 a b x y 这四个值默认都是0;

线程A线程B
x = ay = b
b = 1a = 2

正常的结果: x = 0; y = 0;但是可能由于指令重排,

线程A线程B
b = 1a = 2
x = ay = b

指令重排导致的诡异结果x = 2; y = 1;

volatile可以避免指令重排:

内存屏障: CPU指令, 作用:

  1. 保证特定的操作的执行顺序!
  2. 可以保证某些变量的内存可见性(利用这些特性volatile实现了可见性)

在这里插入图片描述

Volatile 是可以保证可见性, 不能保证原子性,由于内存屏障可以避免指令重排的现象产生 !

17.单例模式

饿汉模式

package com.czp.single;

public class HungryMan {

    private static HungryMan HUNGRYMAN = new HungryMan();

    private HungryMan(){
    }

    public static HungryMan getInstance(){
        return HUNGRYMAN;
    }
}

懒汉模式

package com.czp.single;

//单线程安全
public class LazyMan {

    private static LazyMan lazyMan = null;

    private LazyMan(){
    }

    public static LazyMan getInstance(){
        if(lazyMan == null){
            lazyMan = new LazyMan();
        }
        return lazyMan;
    }


}

DCL 懒汉式

package com.czp.single;

import java.lang.reflect.Constructor;

public class LazyManThread {

    private static volatile LazyManThread lazyManThread = null;

    private static boolean isExist = false;

    private LazyManThread() {
        synchronized (LazyManThread.class) {
            if (!isExist) {
                isExist = true;
            } else {
                throw new RuntimeException("禁止使用反射创建该对象");
            }
        }

    }

    //
    private LazyManThread(int a){
        synchronized (LazyManThread.class){
            if(lazyManThread != null){
                throw new RuntimeException("禁止使用反射创建该对象");
            }
        }
    }

    public static LazyManThread getInstance() {
        //if只会判断一次,当两个线程同时判断时一个线程就会在同步代码块中等待
        if (lazyManThread == null) {
            //不直接使用同步的原因,提高执行效率
            synchronized (LazyManThread.class) {
                if (lazyManThread == null) {
                    lazyManThread = new LazyManThread();
                }
            }
        }

        /**
         * 由于对象创建不是原子性操作
         * 1. 分配内存空间
         * 2. 使用构造器创建对象
         * 3. 将对象指向内存空间
         */
        /**
         * 可能会发生指令重排
         * 123
         *
         * 132
         *
         * 这是就需使用volatile关键字来防止指令重排
         */
        return lazyManThread;
    }


    public static void main(String[] args) throws Exception {
//        LazyManThread instance = LazyManThread.getInstance();

        Constructor<LazyManThread> declaredConstructor = LazyManThread.class.getDeclaredConstructor();
        declaredConstructor.setAccessible(true);
        LazyManThread lazyManThread = declaredConstructor.newInstance();
        LazyManThread instance = declaredConstructor.newInstance();
        System.out.println(instance);
        System.out.println(lazyManThread);
    }
}

静态内部类


public class LazyMan1 {


    private LazyMan1() {}

    public static final LazyMan1 getInstance(){
        return innerClass.LAZY_MAN_1;
    }

    public static class innerClass {
            private static final LazyMan1 LAZY_MAN_1 = new LazyMan1();
    }
}

单例不安全,反射

package com.czp.single;

import java.lang.reflect.Constructor;

public enum EnumSingle {

    INSTANCE;


    public EnumSingle getInstance(){
        return INSTANCE;
    }
}

class Test{

    public static void main(String[] args) throws Exception {
        EnumSingle instance = EnumSingle.INSTANCE;
        Constructor<EnumSingle> declaredConstructor = EnumSingle.class.getDeclaredConstructor(String.class,int.class);
        declaredConstructor.setAccessible(true);
        EnumSingle instance1 = declaredConstructor.newInstance();
        System.out.println(instance);
        System.out.println(instance1);

    }
}

在这里插入图片描述

枚举最终反编译代码

// Decompiled by Jad v1.5.8g. Copyright 2001 Pavel Kouznetsov.
// Jad home page: http://www.kpdus.com/jad.html
// Decompiler options: packimports(3) 
// Source File Name:   EnumSingle.java

package com.czp.single;


public final class EnumSingle extends Enum
{

    public static EnumSingle[] values()
    {
        return (EnumSingle[])$VALUES.clone();
    }

    public static EnumSingle valueOf(String name)
    {
        return (EnumSingle)Enum.valueOf(com/czp/single/EnumSingle, name);
    }

    private EnumSingle(String s, int i)
    {
        super(s, i);
    }

    public EnumSingle getInstance()
    {
        return INSTANCE;
    }

    public static final EnumSingle INSTANCE;
    private static final EnumSingle $VALUES[];

    static 
    {
        INSTANCE = new EnumSingle("INSTANCE", 0);
        $VALUES = (new EnumSingle[] {
            INSTANCE
        });
    }
}

19.深入理解CAS

什么是CAS
在这里插入图片描述

UnSalf类

在这里插入图片描述
在这里插入图片描述

CAS: 比较当前工作内存中的值和主内存中的值,如果这个值是期望的,那么则执行操作! 如果不是就一直循环

缺点:

  1. 循环会耗时
  2. 一次性只能保证一个共享变量的原子性
  3. ABA问题

CAS : ABA问题(狸猫换太子)
在这里插入图片描述

在这里插入图片描述

20 . 原子引用

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

带版本号的原子操作 !

package com.czp.CAS;

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

public class CASDemo {

    public static void main(String[] args) {
        //integer
        AtomicStampedReference<Integer> stamp = new AtomicStampedReference<>(1, 1);

        new Thread(()->{
            System.out.println("a1=>" + stamp.getStamp());
            try {
                TimeUnit.SECONDS.sleep(2);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            stamp.compareAndSet(1, 2, stamp.getStamp(), stamp.getStamp()+1);
            System.out.println("a2=>" + stamp.getStamp());
            stamp.compareAndSet(2, 1, stamp.getStamp(), stamp.getStamp()+1);
            System.out.println("a3=>" + stamp.getStamp());
        }).start();

        new Thread(()->{
            System.out.println("b1=>" + stamp.getStamp());
            stamp.compareAndSet(1, 2, stamp.getStamp(), stamp.getStamp() + 1);
            System.out.println("b2=>" + stamp.getStamp());
        }).start();

    }
}

在这里插入图片描述

21. 各种锁的理解

1.公平锁, 非公平锁

公平锁: 非常公平,先来后到,不允许插队

非公平锁: 非常不公平, 允许插队

 	public ReentrantLock() {
        sync = new NonfairSync(); //无参默认非公平锁
    }
 	public ReentrantLock(boolean fair) {
        sync = fair ? new FairSync() : new NonfairSync();//传参为true为公平锁
    }

2. 可重入锁(递归锁)

释义: 可重入锁指的是可重复可递归调用的锁,在外层使用锁之后,在内层仍然可以使用,并且不发生死锁(前提得是同一个对象或者class),这样的锁就叫做可重入锁

synchronized版本的可重入锁


public class TestLock {

    public static void main(String[] args) {
        TestPhone phone = new TestPhone();
        new Thread(()->{
	    //在调用sendMessage的方法时已经为phone加上了一把锁
        //而call方法由为其加上了一把锁
            phone.sendMessage();
        }).start();
    }
}


class TestPhone {
	

    public synchronized void sendMessage() {

        System.out.println(Thread.currentThread().getName() + "sendMessage");
        call();
    }

    public synchronized void call() {

        System.out.println(Thread.currentThread().getName() + "call");

    }
}

在这里插入图片描述

3, 自旋锁

在这里插入图片描述

public class SpinLockDemo {

    AtomicReference<Thread> atomicReference = new AtomicReference<>();

    public void myLock(){
        Thread thread = Thread.currentThread();

        System.out.println(thread.getName() + "=======>Lock");

        //自旋锁
        //由两个线程操作
        //第一个直接获取成功不需要自旋
        //第二个由于thread不为null所以会自旋
        while(!atomicReference.compareAndSet(null, thread)){

        }
    }


    public void myUnLock(){
        Thread thread = Thread.currentThread();
        System.out.println(thread.getName() + "=====> unLock");
        atomicReference.compareAndSet(thread, null);
    }

    public static void main(String[] args) throws InterruptedException {

        SpinLockDemo lock = new SpinLockDemo();

        new Thread(()->{
            lock.myLock();

            try {
                TimeUnit.SECONDS.sleep(3);
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                lock.myUnLock();
            }

        }).start();


        TimeUnit.SECONDS.sleep(1);

        new Thread(()->{
            lock.myLock();

            try {
                TimeUnit.SECONDS.sleep(3);
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                lock.myUnLock();
            }

        }).start();
    }
}

3. 死锁

互斥

占有等待

循环等待

不可抢占

在这里插入图片描述

如何排查死锁

package com.czp.lock;

import java.util.concurrent.TimeUnit;

public class KillLock implements Runnable {
    private String stringA;
    private String stringB;

    public KillLock(String stringA, String stringB) {
        this.stringA = stringA;
        this.stringB = stringB;
    }

    @Override
    public void run() {
        synchronized (stringA) {

            System.out.println(Thread.currentThread().getName() + "lock" + stringA + "try to lock stringB");
            try {
                TimeUnit.SECONDS.sleep(2);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            synchronized (stringB) {
                System.out.println(Thread.currentThread().getName() + "lock" + stringB + "try to lock stringA");
            }
        }
    }

    public static void main(String[] args) {
        String a = "a";
        String b = "b";

        new Thread(new KillLock(a, b)).start();
        new Thread(new KillLock(b, a)).start();
    }
}

  1. 使用jps -l定位进程号

在这里插入图片描述

  1. 使用jstack 查看进程信息找到死锁问题

jstack 进程号

Java stack information for the threads listed above:
===================================================
"Thread-1":
        at com.czp.lock.KillLock.run(KillLock.java:25)
        - waiting to lock <0x00000000d5f169c8> (a java.lang.String)
        - locked <0x00000000d5f169f8> (a java.lang.String)
        at java.lang.Thread.run(Thread.java:745)
"Thread-0":
        at com.czp.lock.KillLock.run(KillLock.java:25)
        - waiting to lock <0x00000000d5f169f8> (a java.lang.String)
        - locked <0x00000000d5f169c8> (a java.lang.String)
        at java.lang.Thread.run(Thread.java:745)

Found 1 deadlock.

如何排查问题?

  1. 日志
  2. 堆栈信息
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值