JUC并发编程

1.什么是JUC

其实就是三个包:
java.uitl.concurrent
java.uitl.concurrent.atomic
java.uitl.concurrent.locks

2.线程和进程

java默认有几个线程
2个,main线程,GC线程

线程有几种状态?

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

wait/sleep的区别?
1.来自不同的类
wait来自于Object
sleep来自于Thread
企业当中,休眠使用下面的代码

import java.util.concurrent.TimeUnit;
    TimeUnit.DAYS.sleep(1);
    TimeUnit.SECONDS.sleep(1);

2.关于锁的释放
wait会释放锁,sleep睡觉了,抱着锁睡觉,不会释放锁。

3.使用的范围是不同的
wait 必须在同步代码块中
sleep 可以在任何地方睡

3.LOCK锁

传统的锁 Synchronized


// 基本的卖票例子
// 真正的多线程开发,公司中的开发,降低耦合性
// 线程就是一个单独的资源类,没有任何附属的操作!
// 1.属性,方法
public class ThreadDemo {
    public static void main(String[] args) {
        // 并发: 多线程操作同一个资源类 ,把资源丢入线程
        Ticket ticket = new Ticket();
        // @FunctionalInterface 函数式接口 jdk1.8 lambda表达式
        // (参数)->{代码}
        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();
    }
}
// 资源类
class Ticket {
    //    属性,方法
    private int num = 50;

    // 卖票的方式
    // synchronized 本质:队列,锁
    public synchronized void sale() {
        if (num > 0) {
            System.out.println(Thread.currentThread().getName() + "卖出了" + (num--) + "剩余" + num);
        }
    }
}

Lock类

// lock三部曲
// 1.new ReentrantLock();
// 2.lock.lock; 加锁
// 3.finally => lock.unlock() 解锁
class Ticket2 {
    //    属性,方法
    private int num = 50;
    // 获取一把非公平锁
    Lock lock = new ReentrantLock();

    public void sale() {
        lock.lock(); // 加锁
        try {
            // 业务代码
            if (num > 0) {
                System.out.println(Thread.currentThread().getName() + "卖出了" + (num--) + "剩余" + num);
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            lock.unlock(); // 解锁
        }
    }
}
  public ReentrantLock() {
        sync = new NonfairSync(); // 默认无参为非公平锁
    }

    public ReentrantLock(boolean fair) {
        sync = fair ? new FairSync() : new NonfairSync();
        // 传参决定是公平锁还是非公平锁
    }
// 公平锁 十分公平:可以先来后到
// 非公平锁 十分不公平:可以插队(默认)

Synchronized和Lock的区别
1.Synchronized是内置的java关键字,Lock是一个Java类
2.Synchronized无法判断锁的状态,Lock 可以判断是否获取到了锁
3.Synchronized会自动释放锁,lock必须要手动释放锁,如果不释放锁,会死锁
4.Synchronized:线程1 获得锁 线程2会等待。线程1阻塞,线程2会傻傻的等待。
Lock锁就不一定会等待下去; // lock.tryLock()

/*tryLock()方法是有返回值的,返回值是Boolean类型。它表示的是用来尝试获取锁:成功获取则返回true;获取失败则返回false,这个方法无论如何都会立即返回。不会像synchronized一样,一个线程获取锁之后,其他锁只能等待那个线程释放之后才能有获取锁的机会。
一般情况下的tryLock获取锁匙这样使用的:*/
//实例化Lock接口对象
Lock lock = ...;
//根据尝试获取锁的值来判断具体执行的代码
if(lock.tryLock()) {
     try{
         //处理任务
     }catch(Exception ex){
         
     }finally{
     	//当获取锁成功时最后一定要记住finally去关闭锁
         lock.unlock();   //释放锁
     } 
}else {
	//else时为未获取锁,则无需去关闭锁
    //如果不能获取锁,则直接做其他事情
}

5.Synchronized 可重入锁,不可以中断,非公平;Lock,可重入锁,可以判断锁,非公平(可以自行设置)

 public ReentrantLock(boolean fair) {
        sync = fair ? new FairSync() : new NonfairSync();
        // 传参决定是公平锁还是非公平锁
    }

6.Synchronized 适合锁少量的代码同步问题,Lock适合锁大量的同步代码

1、可重入锁的概念
指的是同一个线程外层函数获得锁之后,内层递归函数仍然能获取该锁的代码,在同一个线程在外层方法获取锁的时候,在进入内层方法会自动获取锁。

1、公平锁
定义:多个线程按照先到先得的策略获取锁。
优点:所有线程都有机会获得锁,不会饿死
缺点:由于所有线程都会经历阻塞态,因此唤醒阻塞线程的开销会很大。
2、非公平锁
定义:所有的线程拼运气,谁运气好,谁就获取到锁
优点:可以减少CPU唤醒线程的开销,整体的吞吐效率会高
缺点:可能会有线程长时间甚至永远获取不到锁,导致饿死。

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

4.生产者和消费者问题

Synchronized 版本

/*
 * 线程之间的通信问题: 生产者和消费者问题! 等待唤醒,通知唤醒
 * 线程交替执行 A线程,B线程 操作同一个变量 num = 0
 * A num+1
 * B num-1
 * */
public class Demo01 {
    public static void main(String[] args) {
        Data data = new Data();
        new Thread(()->{
            for (int i = 0; i < 10; i++) {
                try {
                    data.increment();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        },"A").start();
        new Thread(()->{
            for (int i = 0; i < 10; i++) {
                try {
                    data.decrement();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        },"B").start();
    }
}
// 等待,业务,通知
class Data { // 数字 资源类
    private int num = 0;
    // +1
    public synchronized void increment() throws InterruptedException {
        if (num != 0) {
            // 等待
            this.wait();
        }
        num++;
        // 通知其他线程,我+1完毕
        System.out.println(Thread.currentThread().getName() + num);
        this.notifyAll();
    }
    // -1
    public synchronized void decrement() throws InterruptedException {
        if (num == 0) {
            // 等待
            this.wait();
        }
        num--;
        // 通知其他线程,我-1完毕
        System.out.println(Thread.currentThread().getName() + num);
        this.notifyAll();
    }
}

问题存在:现在是两个线程,如果有多个线程就不安全了

在这里插入图片描述
官方文档写的等待总要出现在while中,所以解决方法就是将上面代码中的if改为while

Lock版本
JUC版本的生产者消费者问题
在这里插入图片描述

通过Lock找到Condition
在这里插入图片描述
代码实现:

// 等待,业务,通知
class Data2 { // 数字 资源类
    private int num = 0;
    Lock lock = new ReentrantLock();
    Condition condition = lock.newCondition();
    // +1
    public void increment() throws InterruptedException {
        lock.lock();
        try {
            while (num != 0) {
                // 等待
                condition.await();
            }
            num++;
            // 通知其他线程,我+1完毕
            System.out.println(Thread.currentThread().getName() + num);
            condition.signalAll();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }finally {
            lock.unlock();
        }
    }
    // -1
    public void decrement() throws InterruptedException {
        lock.lock();
        try {
            while (num == 0) {
                // 等待
                condition.await();
            }
            num--;
            // 通知其他线程,我-1完毕
            System.out.println(Thread.currentThread().getName() + num);
            condition.signalAll();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }
}

Condition 精准的通知和唤醒线程

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 number = 1; // 1A 2B 3C

    public void printA(){
        lock.lock();
        try {
            // 业务 判断->执行->通知
            while (number != 1) {
                // 等待
                condition1.await();
            }
         System.out.println(Thread.currentThread().getName()+"A");
            // 唤醒B
            number = 2;
            condition2.signal();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }
    public void printB(){
        lock.lock();
        try {
            while (number != 2) {
                condition2.await();
            }
         System.out.println(Thread.currentThread().getName()+"B");
            number = 3;
            condition3.signal();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }finally {
            lock.unlock();
        }
    }
    public void printC(){
        lock.lock();
        try {
            while (number != 3) {
                condition3.await();
            }
         System.out.println(Thread.currentThread().getName()+"C");
            number = 1;
            condition1.signal();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }finally {
            lock.unlock();
        }
    }
}

5,8锁现象

如果判断锁的是谁!永远知道是什么锁,被锁的到底是谁

锁只会锁两个:对象和Class

/**
 * 8锁,就是关于锁的8个问题
 * 1.标准情况下,两个线程先打印,发短信还是打电话? 1.发短信 2.打电话
 * 2.sendSms延迟4s,两个线程先打印,发短信还是打电话? 1.发短信 2.打电话
 */
public class Demo01 {
    public static void main(String[] args) {
        Phone phone = new Phone();
        // phone.sendSms()先执行是因为锁的问题
        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("打电话");
    }
}

/**
 * 3.增加了一个普通方法后,先发短信还是hello // 普通方法 不是同步方法,不受锁的影响
 * 4.两个对象,两个同步方法,先执行哪个 // 打电话
 */
public class Demo02 {
    public static void main(String[] args) {
        // 两个对象,两个调用者,两把锁!
        Phone2 phone1 = new Phone2();
        Phone2 phone2 = new Phone2();
        // phone.sendSms()先执行是因为锁的问题
        new Thread(()->{
            phone1.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 void hello(){
        System.out.println("hello");
    }
}

/**
 * 5,增加两个静态的同步方法,只有一个对象,先打印哪个?发短信 类一加载就有了!锁的是Class
 * 6,两个对象!增加两个静态的同步方法,只有一个对象,先打印哪个?发短信 因为类模板只有一个
 */
public class Demo03 {
    public static void main(String[] args) {
        // 两个对象的Class类模板只有一个,static,锁的是class
        Phone3 phone1 = new Phone3();
        Phone3 phone2 = new Phone3();
        // phone.sendSms()先执行是因为锁的问题
        new Thread(()->{
            phone1.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("打电话");
    }
}
/**
 *7.1个静态同步方法,一个普通的方法,一个对象,先执行哪个? 打电话 锁的对象不同
 *8.1个静态同步方法,一个普通的方法,两个个对象,先执行哪个? 打电话 不是同一把锁
 */
public class Demo04 {
    public static void main(String[] args) {
        // 两个对象的Class类模板只有一个,static,锁的是class
        Phone4 phone1 = new Phone4();
        Phone4 phone2 = new Phone4();
        // phone.sendSms()先执行是因为锁的问题
        new Thread(()->{
            phone1.sendSms();
        },"A").start();
        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        new Thread(()->{
            phone2.call();
        },"B").start();
    }
}

// Phone3只有唯一的class对象
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("打电话");
    }
}

小结
本质就锁这两个

  • new 锁的this具体的一个手机
  • static 锁的Class唯一的一个模板

6,集合类不安全

List不安全


// java.util.ConcurrentModificationException 并发修改异常
public class ListTest {
    public static void main(String[] args) {
        // 并发下ArrayList不安全的
        /**
         * 解决方案:
         * 1.List<String> list = new Vector<>()  底层加了synchronized
         * 2.List<String> list1 = Collections.synchronizedList(list);
         * 3. List<String> list1 = new CopyOnWriteArrayList<>();
         */
        // CopyOnWrite 写入时复制   COW 计算机程序设计领域的一种优化策略
        // 多个线程调用的时候,list唯一,读取的时候是固定的
        // 写入的时候,避免覆盖 读写分离
        List<String> list = new ArrayList<>();

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

Set不安全

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

HashMap不安全

// ConcurrentModificationException
    // 1. Collections.synchronizedMap(map);
    // 2.Map<String,String> map1 = new ConcurrentHashMap<>();

public class MapTest {
    public static void main(String[] args) {
        Map<String,String> map = new HashMap<>();
        for (int i = 0; i < 30; i++) {
            new Thread(()->{
                map.put(Thread.currentThread().getName(), UUID.randomUUID().toString());
                System.out.println(map);
            },String.valueOf(i)).start();
        }
    }
}

7,Callable

  1. 可以有返回值
  2. 可以抛出异常
  3. 方法不同,run()/call()
public class CallableDemo {
    public static void main(String[] args) throws ExecutionException, InterruptedException {

        MyThread myThread = new MyThread();
        FutureTask<Integer> futureTask = new FutureTask<>(myThread);
        new Thread(futureTask,"A").start();
        new Thread(futureTask,"B").start(); // 只打印一组数据:结果会做缓存,提高效率
        Integer integer = (Integer) futureTask.get();// 可能会产生阻塞!把它放到最后或者使用异步通信
        System.out.println(integer);
    }
}

class MyThread implements Callable<Integer> {
    @Override
    public Integer call(){
        System.out.println("call()");
        // 耗时的操作
        return 1024;
    }
}

细节:

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

8,常用的辅助类(必会)

1.CountDownLatch 闭锁

// 计数器
public class CountDowmLatchDemo {
    public static void main(String[] args) throws InterruptedException {
        // 倒计时,总数是6  
        // 用在必须要执行任务的时候,再使用
        CountDownLatch countDownLatch = new CountDownLatch(6);
        for (int i = 0; i < 6; i++) {
            new Thread(()->{
                countDownLatch.countDown();
                System.out.println(Thread.currentThread().getName());
            },String.valueOf(i));
        }
        countDownLatch.await();// 等待计数器归零,然后再向下执行
//        countDownLatch.countDown(); // 执行-1操作
        System.out.println("关门");
    }
}

原理

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

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

2.CyclicBarrier 栅栏

public class CycliBarrierDemo {
    public static void main(String[] args) {
        /**
         * 集齐七颗龙珠召唤神龙
         */
        // 召唤龙珠的线程
        CyclicBarrier cyclicBarrier = new CyclicBarrier(7, () -> {
            System.out.println("召唤成功");
        });
        for (int i = 0; i < 7; i++) {
            final int temp = i;
            new Thread(()->{
                System.out.println(Thread.currentThread().getName()+temp);
                try {
                    cyclicBarrier.await();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } catch (BrokenBarrierException e) {
                    e.printStackTrace();
                }
            }).start();
        }
    }
}

3.Semaphore信号量

public class SemaphoreDemo {
    public static void main(String[] args) {
        // 线程数量: 停车位 限流
        Semaphore semaphore = new Semaphore(3);
        for (int i = 0; i < 6; 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 {
                    semaphore.release();
                }
//                release() 释放
            }).start();
        }
    }
}

原理
semaphore.acquire(); // 获取,假设已经满了,就等待,直到有信号量被释放为止
semaphore.release(); // 释放, 会将当前的信号量释放,然后唤醒等待的线程
作用

  1. 多个共享资源的互斥使用
  2. 并发限流,控制最大的线程数

9,读写锁

ReadWriteLock
在这里插入图片描述


/**
 * 独占锁(写锁) 一次只能被一个线程占用
 * 共享锁(读锁) 多个线程可以同时占有
 * ReadWriteLock
 * 读-读 可以共存
 * 读-写 不可共存
 * 写-写 不可共存
 */
public class ReadWriteLockDemo {
    public static void main(String[] args) {
        MyCache myCache = new MyCache();
        // 写入
        for (int i = 0; i < 5; i++) {
            final int temp = i;
            new Thread(() -> {
                myCache.put(temp + "", temp);
            }, String.valueOf(i)).start();
        }
        // 读取
        for (int i = 0; i < 5; i++) {
            final int temp = i;
            new Thread(() -> {
                myCache.get(temp + "");
            }, String.valueOf(i)).start();
        }
    }

}

/**
 * 自定义缓存
 */
class MyCache {
    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);
            map.get(key);
            System.out.println(Thread.currentThread().getName() + "读取成功");
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            readWriteLock.readLock().unlock();
        }
    }
}

10,阻塞队列

在这里插入图片描述
阻塞队列
在这里插入图片描述
BlockingQueue
在这里插入图片描述
什么情况下我们会使用阻塞队列?
多线程并发处理,线程池!

在这里插入图片描述

学会使用队列
添加,移除
四组API

方式抛出异常有返回值,不抛出异常阻塞 等待超时等待
添加add()offer()put()offer(E e, long timeout, TimeUnit unit)
移除remove()poll()take()poll(long timeout, TimeUnit unit)
检测队首元素element()peek()--
  /**
     * 抛出异常
     */
    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(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(blockingQueue.poll());
        System.out.println(blockingQueue.poll());
        System.out.println(blockingQueue.poll());
//        System.out.println(blockingQueue.poll()); 返回null 不抛出异常
    }

  /**
     * 等待 阻塞(一直阻塞)
     */
    public void test3() throws InterruptedException {
        ArrayBlockingQueue<Object> 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 void test4() throws InterruptedException {
        ArrayBlockingQueue<Object> blockingQueue = new ArrayBlockingQueue<>(3);
        blockingQueue.offer("a");
        blockingQueue.offer("b");
        blockingQueue.offer("c");
        // 等待2s,如果还是没有位置就超时退出
        blockingQueue.offer("d", 2, TimeUnit.SECONDS);

        System.out.println(blockingQueue.poll());
        System.out.println(blockingQueue.poll());
        System.out.println(blockingQueue.poll());
        // 超过2s就放弃
        blockingQueue.poll(2, TimeUnit.SECONDS);
    }

SynchronousQueue同步队列
没有容量,进去一个元素,必须等待取出来,才能再往里面放一个元素!
put,take

/**
 * 同步队列
 * 和其他的BlockingQueue不一样,SynchronousQueue不存储元素
 * 只要put一个元素,必须从里面先take取出来,否则不能再put进去值
 */
public class Demo02 {
    public static void main(String[] args) {
        // 同步队列
        BlockingQueue<String> blockingDeque= new SynchronousQueue<>();
        new Thread(()->{
            try {
                System.out.println(Thread.currentThread().getName()+"put1");
                blockingDeque.put("1");
                System.out.println(Thread.currentThread().getName()+"put2");
                blockingDeque.put("2");
                System.out.println(Thread.currentThread().getName()+"put3");
                blockingDeque.put("3");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        },"T1").start();
        new Thread(()->{
            try {
                TimeUnit.SECONDS.sleep(3);
                System.out.println(Thread.currentThread().getName()+blockingDeque.take()*斜体样式*);
                TimeUnit.SECONDS.sleep(3);
                System.out.println(Thread.currentThread().getName()+blockingDeque.take());
                TimeUnit.SECONDS.sleep(3);
                System.out.println(Thread.currentThread().getName()+blockingDeque.take());
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        },"T2").start();
    }
}

11,线程池

3大方法,7大参数,4种拒绝策略
池化技术
程序得运行,本质:占用系统的资源!优化资源的使用!=>池化技术
线程池,连接池,内存池,对象池。创建和销毁十分浪费资源
池化技术:事先准备好一些资源,有人要用,就来我这里拿,用完之后还给我。

线程池的好处
1.降低资源的消耗
2.提高响应的速度
3.方便管理
线程复用,可以控制最大并发数,管理线程

3大方法

// Executors工具类,3大方法
// 使用了线程池之后,使用线程池来创建线程
public class Demo01 {
    public static void main(String[] args) {
        // 单个线程,这个线程池只有一个线程
        ExecutorService threadPool = Executors.newSingleThreadExecutor();
        // 创建一个固定的线程池大小
        ExecutorService threadPool1 = Executors.newFixedThreadPool(5);
        // 可伸缩的,遇强则强,遇弱则弱
        ExecutorService threadPool2 = Executors.newCachedThreadPool();
        try {
            for (int i = 0; i < 10; i++) {
                // 使用了线程池之后,使用线程池来创建线程
                threadPool.execute(()->{
                    System.out.println(Thread.currentThread().getName()+"OK");
                });
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            // 线程池用完,程序结束,关闭线程池
            threadPool.shutdown();
        }
    }
}

7大参数
源码分析

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

   public static ExecutorService newCachedThreadPool() {
        return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
                                      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;
    }
  1. corePoolSize:核心线程池大小
  2. maximumPoolSize:最大核心线程池大小
  3. keepAliveTime:超时了,没有人调用就会释放
  4. unit:超时单位
  5. workQueue:阻塞队列
  6. threadFactory:线程工厂,创建线程的,一般不用动
  7. handler:拒绝策略

在这里插入图片描述

手动创建一个线程池

public class Demo01 {
    public static void main(String[] args) {
        // 自定义线程池
        ThreadPoolExecutor threadPool =
                new ThreadPoolExecutor(
                        2,
                        5,
                        3,
                        TimeUnit.SECONDS,
                        new LinkedBlockingQueue<>(3),
                        Executors.defaultThreadFactory(),
                        new ThreadPoolExecutor.AbortPolicy()// 银行满了,但是还有人进来,就不处理这个人的了,并且抛出异常
                        );
        try {
            // 最大承载:queue+max
            for (int i = 0; i < 10; i++) {
                // 使用了线程池之后,使用线程池来创建线程
                threadPool.execute(()->{
                    System.out.println(Thread.currentThread().getName()+"OK");
                });
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            // 线程池用完,程序结束,关闭线程池
            threadPool.shutdown();
        }
    }
}

4大拒绝策略

/**
 * new ThreadPoolExecutor.AbortPolicy() 超过最大承载,抛出java.util.concurrent.RejectedExecutionException异常
 * new ThreadPoolExecutor.CallerRunsPolicy() 哪来的去哪里,比如是从main线程过来的,就让它回main线程
 * new ThreadPoolExecutor.DiscardPolicy() 阻塞队列满了,不会抛出异常
 * new ThreadPoolExecutor.DiscardOldestPolicy() 阻塞队列满了,会尝试和最早的执行的那个线程竞争,竞争失败就丢弃,不抛出异常
 */

小结和拓展

最大线程数到底该如何定义?
1.CPU密集型,几核CPU就定义为几,可以保证CPU的效率最高

 // 获取CPU核心数
        System.out.println(Runtime.getRuntime().availableProcessors());
  // 自定义线程池CPU密集型写法
        ThreadPoolExecutor threadPool =
                new ThreadPoolExecutor(
                        2,
                     Runtime.getRuntime().availableProcessors(),
                        3,
                        TimeUnit.SECONDS,
                        new LinkedBlockingQueue<>(3),
                        Executors.defaultThreadFactory(),
                        new ThreadPoolExecutor.AbortPolicy()// 银行满了,但是还有人进来,就不处理这个人的了,并且抛出异常
                        );

2.IO密集型 判断你的程序中十分耗IO的线程有多少个,大于这个数就行,一般2倍。

12,四大函数式接口(重点,必须掌握)

新时代的程序员:lambda表达式,链式编程,函数式接口,Stream流式计算

函数式接口: 只有一个方法的接口

@FunctionalInterface
public interface Runnable {
    /**
     * When an object implementing interface <code>Runnable</code> is used
     * to create a thread, starting the thread causes the object's
     * <code>run</code> method to be called in that separately executing
     * thread.
     * <p>
     * The general contract of the method <code>run</code> is that it may
     * take any action whatsoever.
     *
     * @see     java.lang.Thread#run()
     */
    public abstract void run();
}

// 超级多@FunctionalInterface
// 简化编程模型,在新版本的框架底层大量应用
// foreach(消费者类型的函数式接口)

代码测试
1.Function 函数式接口

/**
 * Function 函数型接口
 * 有一个输入参数,有一个输出参数
 * 只要是函数式接口 可以用 lambda表达式简化
 */
public class Demo01 {
    public static void main(String[] args) {
//        Function function = new Function<String ,String>(){
//            @Override
//            public String apply(String s) {
//                return s;
//            }
//        };
        Function function = (str)->{
            return str;
        };
    }
}

2.断定型接口

/**
 * 断定型接口:有一个输入参数,返回值只能是 布尔值!
 */
public class Demo02 {
    public static void main(String[] args) {
        // 判断字符串是否为空
//        Predicate<String> predicate = new Predicate<String>() {
//            @Override
//            public boolean test(String s) {
//                return s.isEmpty();
//            }
//        };
        Predicate<String> predicate = String::isEmpty;
    }
}

Consumer消费型接口

/**
 * Consumer 消费型接口: 只有输入,没有返回值
 */
public class Demo03 {
    public static void main(String[] args) {
//        Consumer<String> consumer = new Consumer<String>() {
//            @Override
//            public void accept(String o) {
//                System.out.println(o);
//            }
//        };
        Consumer<String> consumer = System.out::println;
        consumer.accept("abc");
    }
}

Supplier供给型接口

/**
 * Supplier 供给型接口:没有参数,只有返回值
 */
public class Demo04 {
    public static void main(String[] args) {
        Supplier<String> supplier = new Supplier<String>() {
            @Override
            public String get() {
                return "123";
            }
        };
        supplier.get();
    }
}

13,Strean流式计算

什么是stream流式计算
大数据:存储+计算

集合,mysql本质就是存储东西的
计算都应该交给流来操作

public class Demo05 {
    public static void main(String[] args) {
        User a = new User(1, "a", 21);
        User b = new User(2, "b", 22);
        User c = new User(3, "c", 23);
        User d = new User(4, "d", 24);
        User e = new User(5, "e", 25);
        // 集合就是存储
        List<User> list = Arrays.asList(a, b, c, d, e);
        // 计算交给stream流
        // lambda表达式,链式编程,函数式接口,Stream流式计算
        list.stream()
                .filter(u -> u.getId() % 2 == 0)
                .filter(u -> u.getAge() > 23)
                .map(u -> u.getName().toUpperCase())
                .sorted((uu1, uu2) -> {
                    return uu2.compareTo(uu1);
                })
                .limit(1)
                .forEach(System.out::println);
    }
}

14,ForkJoin

什么是ForkJoin
ForkJoin在JDK1.7,并行执行任务!提升效率,大数据量!

大数据:Map Reduce(把大任务拆分成小任务)
在这里插入图片描述
ForkJoin特点:
工作窃取
在这里插入图片描述

这里面维护的双端队列在这里插入图片描述
代码:

/**
 * 求和计算
 * 如何使用ForkJoin
 * 1.forkJoinPool 通过它来执行
 * 2.计算任务 fork
 */
public class Demo01 extends RecursiveTask<Long> {
    private Long start;
    private Long end;
    // 临界值
    private Long temp = 10000L;

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

    @Override
    protected Long compute() {
        if ((end - start) < temp) {
            // 没有超过临界值就自己计算
            Long sum = 0L;
            for (int i = 0; i < 10_0000_0000; i++) {
                sum += i;
            }
            return sum;
        } else { // forkjoin 递归
            // 超过临界值
            // 分支合并计算
            long middle = (start + end) / 2; // 中间值
            Demo01 demo01 = new Demo01(start, middle);
            demo01.fork();
            Demo01 demo011 = new Demo01(middle + 1, end);
            demo011.fork();
            return demo01.join() + demo011.join();
        }
    }
}

public class Test {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        test3();
    }

    public static void test1() {
        Long sum = 0L;
        long start = System.currentTimeMillis();
        for (Long i = 0L; i < 10_0000_0000; i++) {
            sum += i;
        }
        long end = System.currentTimeMillis();
        System.out.println(sum + "时间:" + (end - start));
        // 499999999500000000时间:2533
    }

    public static void test2() throws ExecutionException, InterruptedException {
        // forkJoin
        long start = System.currentTimeMillis();
        ForkJoinPool forkJoinPool = new ForkJoinPool();
        ForkJoinTask<Long> task = new Demo01(0L, 10_0000_0000L);
        ForkJoinTask<Long> submit = forkJoinPool.submit(task);// 提交任务
        Long sum1 = submit.get();
        long end = System.currentTimeMillis();
        System.out.println(sum1 + "时间:" + (end - start));
    }

    public static void test3() {
        long start = System.currentTimeMillis();
        // Stream并行流
        long sum = LongStream.rangeClosed(0L, 10_0000_0000).parallel().reduce(0, Long::sum);
        long end = System.currentTimeMillis();
        System.out.println(sum + "时间:" + (end - start));
    }
}

流式计算最快!

15,异步回调

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

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

        // 有返回值的supplyAsync异步回调
        // ajax 成功和失败的回调
        // 返回的是错误信息
        CompletableFuture<Integer> completableFuture1 = CompletableFuture.supplyAsync(() -> {
            return 1024;
        });
        System.out.println(completableFuture1.whenComplete((t, u) -> {
            System.out.println(t + "-" + u);
        }).exceptionally((e) -> {
            e.printStackTrace();
            System.out.println(e.getMessage());
            return 233; // 可以获取到错误的返回结果
        }).get());
    }
}

16,JMM

请你谈谈对Volatile的理解
Volatile是java虚拟机提供的轻量级的同步机制
1,保证可见性
2.不保证原子性
3.禁止指令重排

什么是JMM
JMM: Java内存模型,不存在的东西,是概念!约定!

关于JMM的一些同步的约定:
1.线程解锁前,必须把共享变量立刻刷回主存
2.线程加锁前,必须读取主存中最新值到工作内存中
3.加锁跟解锁是同一把锁

线程 工作内存,主内存
8种操作:
在这里插入图片描述
在这里插入图片描述
(1)lock(锁定):作用于主内存的变量,把一个变量标记为一条线程独占状态
(2)unlock(解锁):作用于主内存的变量,把一个处于锁定状态的变量释放出来,释放后的变量才可以被其他线程锁定
(3)read(读取):作用于主内存的变量,把一个变量值从主内存传输到线程的工作内存中,以便随后的load动作使用
(4)load(载入):作用于工作内存的变量,它把read操作从主内存中得到的变量值放入工作内存的变量副本中
(5)use(使用):作用于工作内存的变量,把工作内存中的一个变量值传递给执行引擎
(6)assign(赋值):作用于工作内存的变量,它把一个从执行引擎接收到的值赋给工作内存的变量
(7)store(存储):作用于工作内存的变量,把工作内存中的一个变量的值传送到主内存中,以便随后的write的操作
(8)write(写入):作用于工作内存的变量,它把store操作从工作内存中的一个变量的值传送到主内存的变量中

public class Demo01 {
    private static int num = 0;
    public static void main(String[] args) throws InterruptedException {

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

            }
        }).start();

        TimeUnit.SECONDS.sleep(1);

        num = 1;
        System.out.println(num);
    }
}

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

在这里插入图片描述

17,Volatile

1.保证可见性

public class Demo01 {
    // 不加volatile程序就会死循环
    // volatile可以保证可见性
    private volatile static int num = 0;
    public static void main(String[] args) throws InterruptedException {

        new Thread(()->{ // 线程1 对主内存的变化不知道
            while (num == 0) {

            }
        }).start();

        TimeUnit.SECONDS.sleep(1);

        num = 1;
        System.out.println(num);
    }
}

2.不保证原子性
原子性:不可分割
线程A在执行任务的时候,不能被打扰的,也不能被分割。要么同时成功,要么同时失败

/**
 * 不保证原子性
 */
public class Demo02 {
    // volatile不保证原子性
    private volatile static int num = 0;
    public static void add() {
        num++;
    }
    public static void main(String[] args) {

        // 理论上num结果为20000
        for (int i = 0; i < 20; i++) {
            new Thread(() -> {
                for (int j = 0; j < 1000; j++) {
                    add();
                }
            }).start();
        }
        while (Thread.activeCount() > 2) { // main gc
            Thread.yield();
        }
        System.out.println(Thread.currentThread().getName() + num);
    }
}

如果不加lock和synchronized,怎么样保证原子性?

在这里插入图片描述
可以看到字节码文件,是做了多步骤操作的

使用原子类,解决原子性问题
在这里插入图片描述

原子类为什么这么高级?用到了CAS

public class Demo02 {
    // volatile不保证原子性
    private volatile static AtomicInteger num = new AtomicInteger(0);
    public static  void add() {
        num.getAndIncrement(); // AtomicInteger的+1方法,CAS
    }
    public static void main(String[] args) {

        // 理论上num结果为20000
        for (int i = 0; i < 20; i++) {
            new Thread(() -> {
                for (int j = 0; j < 1000; j++) {
                    add();
                }
            }).start();
        }
        while (Thread.activeCount() > 2) { // main gc
            Thread.yield();
        }
        System.out.println(Thread.currentThread().getName() + num);
    }
}

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

3.禁止指令重排
什么是指令重排:你写的程序,计算机并不是按照你写的那样去执行的。
源代码–>编译器优化的重排–>指令并行也可能会重排–>内存系统也会重排–>执行
在这里插入图片描述
处理器在进行指令重排的时候,会考虑数据的依赖性!
所以不会是4123;

可能造成影响的结果: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 是可以保证可见性,不能保证原子性,由于内存屏障,可以保证避免指令重排的现象产生。

18,彻底玩转单例模式

饿汉式 DCL懒汉式


/**
 * 饿汉式单例
 */
public class Hungry {
    // 可能会浪费空间
    private byte[] date = new byte[1024 * 1024];
    private byte[] date1 = new byte[1024 * 1024];
    private byte[] date2 = new byte[1024 * 1024];
    private byte[] date3 = new byte[1024 * 1024];

    private Hungry() {

    }

    // 饿汉式,一上来就把对象加载了
    private final static Hungry HUNGRY = new Hungry();

    public static Hungry getInstance() {
        return HUNGRY;
    }
}

/**
 * 懒汉式单例
 */
public class LazyMan {

    private LazyMan(){
        System.out.println(Thread.currentThread().getName());
    }
    // 加上volatile避免指令重排
    private volatile static LazyMan lazyMan;
    // 多线程并发下有问题
//    public static LazyMan getInstance(){
//        if (lazyMan == null) {
//            lazyMan = new LazyMan();
//        }
//        return lazyMan;
//    }

    // 双重检测锁模式 懒汉式单例 DCL懒汉式
    public static LazyMan getInstance(){
        if (lazyMan == null) {
            synchronized (LazyMan.class){
                if (lazyMan == null){
                    // 不是一个原子性操作
                    /**
                     * 底层步骤:
                     * 1.分配内存空间
                     * 2.执行构造方法,初始化对象
                     * 3.把这个对象指向这个空间
                     *
                     * 由于指令重排,可能执行顺序有变化
                     * 如果lazyman还没有完成构造,来了个线程B,
                     * 发现对象已经指向空间了,就会执行代码
                     */
                    lazyMan = new LazyMan();
                }
            }
        }
        return lazyMan;
    }

    // 多线程并发下有问题
    public static void main(String[] args) {
        for (int i = 0; i < 10; i++) {
            new Thread(()->{
                LazyMan.getInstance();
            }).start();
        }
    }
}

/**
 * 静态内部类
 */
public class Holder {
    private volatile static LazyMan lazyMan;
    private static boolean sheidoubuzhidao = false;
    private Holder() {
        synchronized (LazyMan.class) {
            if (sheidoubuzhidao == false) {
                sheidoubuzhidao = true;
            }else {
                throw new RuntimeException("不要使用反射破坏单例");
            }
        }
    }

    public static Holder getInstance() {
        return InnerClass.HOLDER;
    }

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

    //反射
    public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
        // 利用反射破环单例
        LazyMan instance = LazyMan.getInstance();
        Constructor<LazyMan> declaredConstructor = LazyMan.class.getDeclaredConstructor(null);
        declaredConstructor.setAccessible(true);
        LazyMan instance1 = declaredConstructor.newInstance();
        System.out.println(instance);
        System.out.println(instance1);
    }
}

单例不安全,反射

/**
 * 枚举 enum本身也是一个Class类
 *
 */
public enum EnumSingle {
    INSTANCE;
    public EnumSingle getInstance(){
        return INSTANCE;
    }
}

class Test{
    public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
        EnumSingle instance = EnumSingle.INSTANCE;
        Constructor<EnumSingle> declaredConstructor = EnumSingle.class.getDeclaredConstructor(String.class, int.class);
        declaredConstructor.setAccessible(true);
        EnumSingle instance1 = declaredConstructor.newInstance();

    }
}

19,深入理解CAS

什么是CAS
原子类通过CAS保证原子性

public class CASDemo {

    // CAS compareAndSet:比较并交换
    public static void main(String[] args) {
        AtomicInteger atomicInteger = new AtomicInteger(2020);
// public final boolean compareAndSet(int expect, int update)
        //expect 期望,update 更新
        // 如果我期望的值达到了,就更新,否则就不更新
        // CAS 是CPU的并发原语
        System.out.println(atomicInteger.compareAndSet(2020, 2021));
        System.out.println(atomicInteger.get());

        
        System.out.println(atomicInteger.compareAndSet(2020, 2021));
        System.out.println(atomicInteger.get());
    }
}

Unsafe类
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
CAS:比较当前工作内存中的值和主内存中的值,如果这个值是期望的,那么则执行操作!如果不是就一直循环。

缺点:
1,由于底层是自旋锁,循环会耗时
2,一次性只能保证一个共享变量的原子性
3.会存在ABA问题

CAS: ABA问题(狸猫换太子)

  // 捣乱的线程
        System.out.println(atomicInteger.compareAndSet(2020, 2021));
        System.out.println(atomicInteger.get());

        System.out.println(atomicInteger.compareAndSet(2020, 2021));
        System.out.println(atomicInteger.get());
        // 期望的线程
        System.out.println(atomicInteger.compareAndSet(2020, 2021));
        System.out.println(atomicInteger.get());

一个线程不知道另外一个线程把A改成过B

20,原子引用

带版本号的原子操作
解决ABA问题,引入原子引用!对应的思想就是乐观锁
在这里插入图片描述

在这里插入图片描述
定义版本号时,会踩到的坑:版本号用Intger太大了会有问题。工作中一般用对象
在这里插入图片描述

21,各种锁的理解

1,公平锁,非公平锁
公平锁:非常公平,不能够插队,线程先来后到!
非公平锁:非常不公平,可以插队,线程可以插队(默认)比如一个线程3s搞定,一个300s,就可以让3s的插队
2,可重入锁
可重入锁(递归锁)
在这里插入图片描述

3,自旋锁
在这里插入图片描述

/**
 * 自旋锁
 * spinlock
 */
public class Demo02 {

    // int 0
    // Thread null
    AtomicReference<Thread> atomicReference = new AtomicReference<>();

    // 加锁
    public void myLock() {
        Thread thread = Thread.currentThread();
        System.out.println(Thread.currentThread().getName() + "lock");
        // 自旋锁
        while (!atomicReference.compareAndSet(null, thread)) {

        }
    }

    // 解锁
    public void myUnLock() {
        Thread thread = Thread.currentThread();
        System.out.println(Thread.currentThread().getName() + "unlock");
        // 自旋锁
        atomicReference.compareAndSet(thread, null);
    }

}

4,死锁
1 . 产生死锁的必要条件:
(1)互斥条件:进程要求对所分配的资源进行排它性控制,即在一段时间内某资源仅为一进程所占用。
(2)请求和保持条件:当进程因请求资源而阻塞时,对已获得的资源保持不放。
(3)不剥夺条件:进程已获得的资源在未使用完之前,不能剥夺,只能在使用完时由自己释放。
(4)环路等待条件:在发生死锁时,必然存在一个进程–资源的环形链。
解决死锁的基本方法

2 . 预防死锁:
(1)资源一次性分配:一次性分配所有资源,这样就不会再有请求了:(破坏请求条件)
(2)只要有一个资源得不到分配,也不给这个进程分配其他的资源:(破坏请保持条件)
(3)可剥夺资源:即当某进程获得了部分资源,但得不到其它资源,则释放已占有的资源(破坏不可剥夺条件)
(4)资源有序分配法:系统给每类资源赋予一个编号,每一个进程按编号递增的顺序请求资源,释放则相反(破坏环路等待条件)

解决方法:
1,使用“jps -l”命令定位进程号
2,使用 “jstack 进程号”命令 查看堆栈进程信息
在这里插入图片描述

面试,工作中,排查问题:
1.看下有没有日志
2.看下堆栈信息

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

华华华华华12138

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

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

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

打赏作者

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

抵扣说明:

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

余额充值