JUC学习From狂神

线程进程
进程

  • 一个进程可包含多个线程,至少包含一个
  • java默认有两个线程,一个main 一个GC
    线程:开了一个写字板的进程,写字,自动保存(线程负责)

Java开启线程:Thread、 Runnable 、Callable
Java真的可以开启线程吗? 不行

  /**
     * Causes this thread to begin execution; the Java Virtual Machine
     * calls the <code>run</code> method of this thread.
     * <p>
     * The result is that two threads are running concurrently: the
     * current thread (which returns from the call to the
     * <code>start</code> method) and the other thread (which executes its
     * <code>run</code> method).
     * <p>
     * It is never legal to start a thread more than once.
     * In particular, a thread may not be restarted once it has completed
     * execution.
     *
     * @exception  IllegalThreadStateException  if the thread was already
     *               started.
     * @see        #run()
     * @see        #stop()
     */
    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多核,多个线程同时执行

并发编程的本质==》充分利用cpu的资源

//获取处理器数量
System.out.println(Runtime.getRuntime().availableProcessors());

线程的状态

//新生,线程还没有启动

  • NEW,
    //运行,在虚拟机中执行
  • RUNNABLE,
    //阻塞,阻塞的线程正在等待监视器锁
  • BLOCKED,
    //等待,一个线程无限期地等待另一个线程
  • WAITING,
    //超时等待,在指定的等待时间内处于此状态
  • TIMED_WAITING,
    //终止,已经退出的线程
  • TERMINATED;
    在这里插入图片描述

wait sleep的区别
1.来自不同的类
wait->Object
sleep->Thread
2.关于锁的释放
wait会释放锁,sleep抱着锁睡觉不会释放锁
3.使用的范围不同
wait 必须在同步代码块中
sleep 可以在任何地方睡
4.是否需要捕获异常
wait 不需要捕获异常 wait也必须捕获异常 InterruptedException 中断异常
sleep 必须捕获异常

Lock锁

传统Synchronized

Lock接口
在这里插入图片描述

ReentrantLock, ReentrantReadWriteLock.ReadLock, ReentrantReadWriteLock.WriteLock

在这里插入图片描述
NonfairSync //非公平锁
FairSync //公平锁

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

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

生产者和消费者问题

面试的:单例模式、排序算法、生产者消费者、死锁
1.生产者消费者synchronized版

/**
 * @ClassName A
 * @Description 线程之间的通信问题,生产者消费者问题    等待唤醒 通知唤醒
 *              线程交替执行 A B 操作同一变量 num =0
 *              A num+1
 *              B num-1
 * @Author lihongling
 * @Date 
 **/
public class A {
    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 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();
    }

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

再有更多的线程的时候会有问题

package com.beibei.pcdemo;

/**
 * @ClassName A
 * @Description 线程之间的通信问题,生产者消费者问题    等待唤醒 通知唤醒
 *              线程交替执行 A B 操作同一变量 num =0
 *              A num+1
 *              B num-1
 * @Author lihongling
 * @Date 2021/6/2 10:38
 **/
public class A {
    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();
        new Thread(()->{
            for (int i = 0; i < 10; i++) {
                try {
                    data.increment();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        },"C").start();
        new Thread(()->{
            for (int i = 0; i < 10; i++) {
                try {
                    data.decrement();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        },"D").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();
    }

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

在这里插入图片描述
在这里插入图片描述
=将if改为while即可解决此问题=

package com.beibei.pcdemo;

/**
 * @ClassName A
 * @Description 线程之间的通信问题,生产者消费者问题    等待唤醒 通知唤醒
 *              线程交替执行 A B 操作同一变量 num =0
 *              A num+1
 *              B num-1
 * @Author lihongling
 * @Date 2021/6/2 10:38
 **/
public class A {
    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();
        new Thread(()->{
            for (int i = 0; i < 10; i++) {
                try {
                    data.increment();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        },"C").start();
        new Thread(()->{
            for (int i = 0; i < 10; i++) {
                try {
                    data.decrement();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        },"D").start();
    }


}
class Data{//数字 资源类
    private int number = 0;
    //+1
    public synchronized void increment() throws InterruptedException {
        //用if会存在虚假唤醒的情况,所有的wait应该发生在循环中
       // if(number !=0){
        while(number !=0){
            //等待
            this.wait();
        }
        number++;
        System.out.println(Thread.currentThread().getName()+"->"+number);
        //通知其余线程我+1结束
        this.notifyAll();
    }

    public  synchronized void decrement() throws InterruptedException {
        //用if会存在虚假唤醒的情况,所有的wait应该发生在循环中
        //if(number == 0){
        while(number ==0){
            //等待
            this.wait();
        }
        number--;
        System.out.println(Thread.currentThread().getName()+"->"+number);
        //通知其余线程我-1结束
        this.notifyAll();
    }
}

2.JUC版生产者消费者
在这里插入图片描述
在这里插入图片描述

package com.beibei.pcdemo;

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

/**
 * @ClassName A  JUC 版
 * @Description 线程之间的通信问题,生产者消费者问题    等待唤醒 通知唤醒
 *              线程交替执行 A B 操作同一变量 num =0
 *              A num+1
 *              B num-1
 * @Author lihongling
 * @Date 
 **/
public class B{
    public static void main(String[] args) {
        Data2 data = new Data2();
      /*  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();
        new Thread(()->{
            for (int i = 0; i < 10; i++) {
                try {
                    data.increment();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        },"C").start();
        new Thread(()->{
            for (int i = 0; i < 10; i++) {
                try {
                    data.decrement();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        },"D").start();
    }


}

class Data2{//数字 资源类
    private int number = 0;
    private Lock lock = new ReentrantLock();
    Condition condition = lock.newCondition();
//    condition.await();//等待
//    condition.signalAll();//唤醒
    //+1
    public void increment() throws InterruptedException {
        lock.lock();//加锁
        try{
            //会存在虚假唤醒的情况,所有的wait应该发生在循环中
            while(number !=0){
                //等待
                condition.await();
            }
            number++;
            System.out.println(Thread.currentThread().getName()+"->"+number);
            //通知其余线程我+1结束
            condition.signalAll();
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            lock.unlock();//释放锁
        }
    }

    public void decrement() throws InterruptedException {
        lock.lock();//加锁
        try{
            //会存在虚假唤醒的情况,所有的wait应该发生在循环中
            while(number ==0){
                //等待
                condition.await();
            }
            number--;
            System.out.println(Thread.currentThread().getName()+"->"+number);
            //通知其余线程我-1结束
            condition.signalAll();
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            lock.unlock();//释放锁
        }
    }
}

Condition


/**
 * @ClassName C
 * @Description  A执行完调用B,B执行完调用C,C执行完调用D
 * @Author lihongling
 * @Date 
 **/
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 number =1;//1A执行,2B执行,3C执行
    public void printA(){
        lock.lock();
        try {
            //业务,判断-》执行-》通知
            while(number!=1){
                condition1.await();
            }
            System.out.println(Thread.currentThread().getName()+"=>AAAA");
            //唤醒,唤醒指定的人B
            number=2;
            condition2.signal();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }

    }
    public void printB(){
        lock.lock();
        try {
            //业务,判断-》执行-》通知
            while(number!=2){
                condition2.await();
            }
            System.out.println(Thread.currentThread().getName()+"=>BBBB");
            //唤醒,唤醒指定的人C
            number=3;
            condition3.signal();

        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }
    public void printC(){
        lock.lock();
        try {
            //业务,判断-》执行-》通知
            while(number!=3){
                condition3.await();
            }
            System.out.println(Thread.currentThread().getName()+"=>CCCC");
            //唤醒,唤醒指定的人C
            number=1;
            condition1.signal();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }
}

8锁现象

如何判断锁的是谁!永远的知道什么锁,锁到底锁的是谁
深刻理解锁

8锁,就是关于锁的8个问题

  • 1.标准情况下,两个线程是先打印 打电话 还是发短信? 1发短信 2打电话
  • 2.发短信延迟4s,两个线程是先打印 打电话 还是发短信? 1发短信 2打电话
  • 3.增加了一个普通方法hello,先打印 发短信还是hello? 1.hello1.发短信(PS如果把发短信的延迟4秒去掉就不一定了)
  • 4.2个对象2个同步方法,先打印 发短信还是打电话? 1.打电话 2.发短信
  • 5.加了两个静态的同步方法,先打印发短信 打电话? 1.发短信 2打电话
  • 6.两个对象执行静态同步方法,先打印发短信 打电话? 1.发短信 2打电话
  • 7.一个静态同步方法一个普通同步放,一个对象,先打印发短信 打电话?1.打电话 2发短信(这是两个锁,静态的同步方法锁的是class,普通的锁的是调用者)
  • 8.一个静态同步方法一个普通同步放,两个个对象,先打印发短信 打电话?1.打电话 2发短信
/**
 * @ClassName Lock8
 * @Description 8锁,就是关于锁的8个问题
 *              1.标准情况下,两个线程是先打印 打电话 还是发短信? 1发短信 2打电话
 *              2.发短信延迟4s,两个线程是先打印 打电话 还是发短信? 1发短信 2打电话
 * @Author lihongling
 * @Date 
 **/
public class Lock8Test1 {
    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("发短信sendSms");
    }
    public synchronized void call(){
        System.out.println("打电话call");
    }
}
/**
 * @ClassName Lock8
 * @Description 8锁, 就是关于锁的8个问题
 *              3.增加了一个普通方法hello,先打印 发短信还是hello? 1.hello1.发短信(PS如果把发短信的延迟4秒去掉就不一定了)
 *              4.2个对象2个同步方法,先打印 发短信还是打电话? 1.打电话 2.发短信
 * @Author lihongling
 * @Date 
 **/
public class Lock8Test2 {
    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(()->{
            phone.hello();
        },"B").start();*/
        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("发短信sendSms");
    }
    public synchronized void call(){
        System.out.println("打电话call");
    }
    //这里没有锁,不是同步方法,不受锁的影响
    public void hello(){
        System.out.println("hello");
    }
}
/**
 * @ClassName Lock8
 * @Description 8锁, 就是关于锁的8个问题
 *              5.加了两个静态的同步方法,先打印发短信 打电话? 1.发短信 2打电话
 *              6.两个对象执行静态同步方法,先打印发短信 打电话? 1.发短信 2打电话
 * @Author lihongling
 * @Date 
 **/
public class Lock8Test3 {
    public static void main(String[] args) {
        //因为是调用静态同步代码块,两个对象的锁只有一个,锁的是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(()->{
            //  phone.call();
           phone2.call();
        },"B").start();

    }
}

//phone3是唯一的一个class对象(全局唯一)
class Phone3{
    //synchronized 锁的对象是方法的调用者,由于两个方法用的同一个锁所以谁先拿到谁执行
    //static 静态方法,类一加载就有了,synchronized锁的是class
    public static synchronized void sendSms(){
        try {
            TimeUnit.SECONDS.sleep(4);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("发短信sendSms");
    }
    public static synchronized void call(){
        System.out.println("打电话call");
    }
}
/**
 * @ClassName Lock8
 * @Description 8锁, 就是关于锁的8个问题
 *              7.一个静态同步方法一个普通同步放,一个对象,先打印发短信 打电话?1.打电话 2发短信(这是两个锁,静态的同步方法锁的是class,普通的锁的是调用者)
 *              8.一个静态同步方法一个普通同步放,两个个对象,先打印发短信 打电话?1.打电话 2发短信
 * @Author lihongling
 * @Date 
 **/
public class Lock8Test4 {
    public static void main(String[] args) {
        //因为是调用静态同步代码块,两个对象的锁只有一个,锁的是class
        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(()->{
            //  phone.call();
           phone2.call();
        },"B").start();

    }
}

//phone3是唯一的一个class对象(全局唯一)
class Phone4{
    //synchronized 锁的对象是方法的调用者,由于两个方法用的同一个锁所以谁先拿到谁执行
    //static 静态方法,类一加载就有了,synchronized锁的是class

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

new this 具体的一个手机 锁的是调用者
static 锁的是class唯一的一个模版

集合类不安全

1.List

/**
 * @ClassName ListTest
 * @Description
 * @Author lihongling
 * @Date
 **/
public class ListTest {
    public static void main(String[] args) {
        //单线程情况
      /*  List<String> list = Arrays.asList("1","2","3");
        list.forEach(System.out::println);*/

        //java.util.ConcurrentModificationException 并发修改异常
        //并发下ArrayList是不安全的
        //List<String> list1 = new ArrayList<>();

        /**
         *解决方案
         * 1.使用线程安全的Vector,jdk1.0就有了,使用的是同步的add方法  List<String> list1 = new Vector<>();
         * 2.使用Collections,转变为安全的list。 List<String> list1 = Collections.synchronizedList(new ArrayList<>());
         * 3.List<String> list1 = new CopyOnWriteArrayList<>();
         *   CopyOnWriteArrayList 比Vector好在哪
         *     Vector使用的是synchronized,一般使用这个效率比较满,CopyOnWriteArrayList是使用的Lock
         *     CopyOnWriteArrayList写入时复制一份再写入
         *
         */
        // List<String> list1 = new Vector<>();
        //List<String> list1 = Collections.synchronizedList(new ArrayList<>());
        /**
         *  CopyOnWrite 写入时复制 是计算机设计领域的一种优化策略
         *  多个线程调用的时候,读取的时候,固定的,写入(覆盖)
         *  在写入的时候避免覆盖造成数据问题
         */
        //读写分离思想 MyCat
        List<String> list1 = new CopyOnWriteArrayList<>();

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

2.Set

package com.beibei.unsafe;

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

/**
 * @ClassName SetTest
 * @Description
 * @Author lihongling
 * @Date 
 **/

public class SetTest {
    public static void main(String[] args) {

        //也发生了并发问题,ConcurrentModificationException
        //Set<String> set = new HashSet<>();
        /**
         *解决方法
         * 1. Set<String> set = Collections.synchronizedSet(new HashSet<>()); //使用synchronized转换为安全的
         * 2.Set<String> set = new CopyOnWriteArraySet<>(new HashSet<>());//写入时复制来保证安全和效率
         */

        //Set<String> set = Collections.synchronizedSet(new HashSet<>());
        Set<String> set = new CopyOnWriteArraySet<>(new HashSet<>());
        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的底层是什么?
1.hashset的本质就是hashmap
2.hashset的add本质就是map的key是无法重复的
在这里插入图片描述
在这里插入图片描述
//不变的值
在这里插入图片描述

在这里插入图片描述
3.Map

package com.beibei.unsafe;

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

/**
 * @ClassName MapTest
 * @Description
 * @Author lihongling
 * @Date 2021/6/4 13:44
 **/
public class MapTest {
    public static void main(String[] args) {
        //map是这样用的吗?默认等价于什么?
        //狂神说老师:工作中不这样用hashmap,可能我是假工作了,我看代码中都是这样用的呀??
        //默认等价于new HashMap<>(16,0.75)

        /**
         * 源码
         *    //The default initial capacity - MUST be a power of two.
         *    //位运算16
         *static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; // aka 16
         * //最大容量2的30次方
         *static final int MAXIMUM_CAPACITY = 1 << 30;
         *    //The load factor used when none specified in constructor.
         *    //加载因子
         * static final float DEFAULT_LOAD_FACTOR = 0.75f;
         */
        //加载因子,初始化容量
        //出现并发问题ConcurrentModificationException
        //Map<String, Object> map = new HashMap<>();
        /**
         * 解决方案
         * 1.Map<String, Object> map = Collections.synchronizedMap(new HashMap<>());
         * 2.Map<String, Object> map = new ConcurrentHashMap<>(new HashMap<>());
         */
        //Map<String, Object> map = Collections.synchronizedMap(new HashMap<>());
        Map<String, Object> map = new ConcurrentHashMap<>(new HashMap<>());

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

    }
}

Callable

在这里插入图片描述
1.Callable可以有返回值
2.Callable可以抛出异常
3.方法不同run()/call(),Callable是用call

怎么启动Callable?
在这里插入图片描述
在这里插入图片描述
使用FutureTask来启动

public class CallableTest {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        //这两个方法等价
        //new Thread(new Runnable()).start();
        //new Thread(new FutureTask<>()).start();
        new Thread().start();//怎么启动Callable
        MyThread thread = new MyThread();
        FutureTask futureTask = new FutureTask(thread);
        //两个线程会打印几次,打印一次
        new Thread(futureTask,"A").start();
        new Thread(futureTask,"B").start();//结果会被缓存提交效率
        //这个get方法可能会产生阻塞,因为要等待返回结果,如果是个耗时的操作就会去等待,
        //把获取返回值的放到最后,或者使用异步通信
        Integer i= (Integer) futureTask.get();//获取Callable的返回结果
        System.out.println(i);
    }
}

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

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

常用的同步辅助类

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()+"Get out");
                countDownLatch.countDown();//数量-1
            },String.valueOf(i)).start();
        }
        countDownLatch.await();//等待计数器归零,然后再向下执行
        System.out.println("close -_-");

    }
}

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

CyclicBarrier

加法计数器

public class CyclicBarrierDemo {
    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(()->{
                //lamada能操作到i吗?new Thread()本质是新建了一个类,是拿不到i数据的
                //System.out.println(i);
                //定义一个final的中间变量就可以拿到了
                //System.out.println(temp);
                System.out.println(Thread.currentThread().getName()+"Get到:"+temp);
                try {
                    cyclicBarrier.await();//等待达到7就会执行 奥特曼现身
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } catch (BrokenBarrierException e) {
                    e.printStackTrace();
                }
            }).start();

        }
    }
}
Semaphore

计数信号量
16个车抢6 个车位

public class SemaphoreDemo {
    public static void main(String[] args) {
        //线程数量,如停车位
        Semaphore semaphore = new Semaphore(6);
        //16个车抢6个车位
        for (int i = 0; i < 16; i++) {
            new Thread(()->{
                try {
                    //acquire 得到
                    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();
        }

    }
}

原理:
semaphore.acquire(); 获得,假设满了,就等待,等待被释放为止
semaphore.release(); 释放,会将当前的信号量释放到-1,然后唤醒等待的线程
作用:多个资源线程互斥使用!并发限流,控制最大的线程数!

读写锁

在这里插入图片描述
未加锁之前

public class ReadWriteLockDemo {
    public static void main(String[] args) {
        //未加锁,还未写入完成就读取了
        MyCache myCache = new MyCache();
        //只写入
        for (int i = 0; i < 100; i++) {
            //因为new Thread 无法读取到i,需要一个临时final的对象
            final int temp=i;
            new Thread(()->{
                myCache.put(temp+"",temp);

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

        //只读取
        for (int i = 0; i < 100; i++) {
            //因为new Thread 无法读取到i,需要一个临时final的对象
            final int temp=i;
            new Thread(()->{
                myCache.get(temp+"");

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


    }

}
/**
 * 自定义缓存
 */
class MyCache{
    //volatile 保证原子性
    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()+"读取完成");
    }
}

在这里插入图片描述
加锁之后

/**
 * @ClassName ReadWriteLockDemo
 * @Description  读占锁(写锁),一次只能被一个线程占用
 *               共享锁(读锁),多个线程可以同时占用
 *               ReadWriteLock
 *               读-读 可以共存
 *               读-写 不可以共存
 *               写-写 不能共存
 * @Author lihongling
 * @Date 2021/6/7 17:35
 **/
public class ReadWriteLockDemo {
    public static void main(String[] args) {
        //未加锁,还未写入完成就读取了
        // MyCache myCache = new MyCache();
        MyCacheLock myCache = new MyCacheLock();
        //只写入
        for (int i = 0; i < 100; i++) {
            //因为new Thread 无法读取到i,需要一个临时final的对象
            final int temp=i;
            new Thread(()->{
                myCache.put(temp+"",temp);
            },String.valueOf(i)).start();
        }

        //只读取
        for (int i = 0; i < 100; i++) {
            //因为new Thread 无法读取到i,需要一个临时final的对象
            final int temp=i;
            new Thread(()->{
                myCache.get(temp+"");

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


    }

}
/**
 * 自定义缓存
 */
class MyCache{
    //volatile 保证原子性
    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()+"读取完成");
    }
}

class MyCacheLock{
    //volatile 保证原子性
    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()+"写入完成");
        } 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();
        }
    }
}

阻塞队列

队列:先进先出
阻塞
写入:如果队列满了,就必须阻塞等待
取 :如果队列是空的,必须阻塞等待生产
在这里插入图片描述

Deque:双端队列
AbstractQueue:非阻塞队列

BlockingQueue

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

学会使用队列

添加、移除

四组API

1.抛出异常
2.不会抛出异常
3.阻塞等待
4.超时等待

方式抛出异常有返回值阻塞等待超时等待
添加addoffer()putoffer(…)
移除removepoll()takepoll(…)
检测队首元素elementpeek
  /**
     * 抛出异常
     */
    public static void test1(){
        //3 队列的大小
        ArrayBlockingQueue arrayBlockingQueue = new ArrayBlockingQueue<>(3);
        System.out.println("========写=======");
        System.out.println("a:"+arrayBlockingQueue.add("a"));
        System.out.println("b:"+arrayBlockingQueue.add("b"));
        System.out.println("c:"+arrayBlockingQueue.add("c"));
        //IllegalStateException Queue full 抛出异常
      //  System.out.println("d:"+arrayBlockingQueue.add("d"));
        System.out.println("========取=======");
        System.out.println(arrayBlockingQueue.remove());
        System.out.println(arrayBlockingQueue.remove());
        System.out.println(arrayBlockingQueue.remove());
        //NoSuchElementException  抛出异常
        //System.out.println(arrayBlockingQueue.remove());
    }
/**
     * 不抛出异常
     */
    public static void test2(){
        ArrayBlockingQueue arrayBlockingQueue = new ArrayBlockingQueue<>(3);
        System.out.println("========写=======");
        System.out.println("a:"+arrayBlockingQueue.offer("a"));
        System.out.println("b:"+arrayBlockingQueue.offer("b"));
        System.out.println("c:"+arrayBlockingQueue.offer("c"));
        //false 不跑出异常
        System.out.println("d:"+arrayBlockingQueue.offer("d"));
        System.out.println("========取=======");
        System.out.println(arrayBlockingQueue.poll());
        System.out.println(arrayBlockingQueue.poll());
        System.out.println(arrayBlockingQueue.poll());
        //null 有返回值无异常
        System.out.println(arrayBlockingQueue.poll());
    }
 /**
     *等待,阻塞
     * 一致阻塞等到死
     */
    public static void test3() throws InterruptedException {
        ArrayBlockingQueue arrayBlockingQueue = new ArrayBlockingQueue(3);
        arrayBlockingQueue.put("a");
        arrayBlockingQueue.put("b");
        arrayBlockingQueue.put("c");
        //队列没有位置了,一直阻塞等待
        //arrayBlockingQueue.put("d");
        System.out.println(arrayBlockingQueue.take());
        System.out.println(arrayBlockingQueue.take());
        System.out.println(arrayBlockingQueue.take());
        //没有元素可取了,一直阻塞等待
        System.out.println(arrayBlockingQueue.take());

    }
 /**
     *等待,阻塞
     * 阻塞等待超时
     */
    public static void test4() throws InterruptedException {
        ArrayBlockingQueue arrayBlockingQueue = new ArrayBlockingQueue(3);
        arrayBlockingQueue.offer("a");
        arrayBlockingQueue.offer("b");
        arrayBlockingQueue.offer("c");
        //等待写入,超过2s就退出
        arrayBlockingQueue.offer("d", 2,TimeUnit.SECONDS);
        System.out.println(arrayBlockingQueue.poll());
        System.out.println(arrayBlockingQueue.poll());
        System.out.println(arrayBlockingQueue.poll());
        //等待取出,超过2s退出
        arrayBlockingQueue.poll(2,TimeUnit.SECONDS);
    }
SynchronousQueue同步队列

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

public class SynchronousQueueDemo {
    public static void main(String[] args) {
        SynchronousQueue<String> synchronousQueue = new SynchronousQueue<String>();
        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();
    }
}

线程池

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

池化技术
程序的运行,本质:占用系统的资源!优化资源的使用—>池化技术
线程池、连接池、内存池、对象池、、、。。。。创建和销毁,十分浪费资源

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

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

线程池:三大方法

/**
 * @ClassName Demo01
 * @Description Executors 工具类、3大方法
 * @Author 
 * @Date 
 **/
public class Demo01 {
    public static void main(String[] args) {

       // Executors.newSingleThreadExecutor();//单个线程池
      //  Executors.newFixedThreadPool(5);//创建一个固定的线程池的大小5
       // Executors.newCachedThreadPool();//可伸缩的
        // ExecutorService threadPool = Executors.newSingleThreadExecutor();
        //ExecutorService threadPool = Executors.newFixedThreadPool(5);
        ExecutorService threadPool = Executors.newCachedThreadPool();
        try {
            for (int i = 0; i < 30; i++) {
                //使用线程池后,使用线程池来创建线程
                //new Thread().start();

                threadPool.execute(()->{
                    System.out.println(Thread.currentThread().getName()+":ok");

                });
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            //线程池用完要关闭
            threadPool.shutdown();
        }

    }
}

7大参数
源码分析

 public static ExecutorService newSingleThreadExecutor(ThreadFactory threadFactory) {
        return new FinalizableDelegatedExecutorService
            (new ThreadPoolExecutor(1, 1,
                                    0L, TimeUnit.MILLISECONDS,
                                    new LinkedBlockingQueue<Runnable>(),
                                    threadFactory));
}
 public static ExecutorService newFixedThreadPool(int nThreads, ThreadFactory threadFactory) {
        return new ThreadPoolExecutor(nThreads, nThreads,
                                      0L, TimeUnit.MILLISECONDS,
                                      new LinkedBlockingQueue<Runnable>(),
                                      threadFactory);
}
 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) {
        this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
             Executors.defaultThreadFactory(), defaultHandler);
    }
  public ThreadPoolExecutor(int corePoolSize, //核心线程池大小
                              int maximumPoolSize, //最大核心线程池大小
                              long keepAliveTime, //存活时间,超时了,没有人调用就会释放
                              TimeUnit unit,//超时单位
                              BlockingQueue<Runnable> workQueue,//阻塞队列
                              ThreadFactory threadFactory,//线程工厂,创建线程的,一般不用动
                              RejectedExecutionHandler handler//拒绝策略) {
        if (corePoolSize < 0 ||
            maximumPoolSize <= 0 ||
            maximumPoolSize < corePoolSize ||
            keepAliveTime < 0)
            throw new IllegalArgumentException();
        if (workQueue == null || threadFactory == null || handler == null)
            throw new NullPointerException();
        this.acc = System.getSecurityManager() == null ?
                null :
                AccessController.getContext();
        this.corePoolSize = corePoolSize;
        this.maximumPoolSize = maximumPoolSize;
        this.workQueue = workQueue;
        this.keepAliveTime = unit.toNanos(keepAliveTime);
        this.threadFactory = threadFactory;
        this.handler = handler;
    }

阿里的开发手册建议使用ThreadPoolExecutor来创建线程,用Executors不安全,新手开发不注意底层实现容易耗费过多资源

手动创建线程池

四种拒绝策略
在这里插入图片描述

    /**
     * 自定义线程池
     * 四种拒绝策略
     *    new ThreadPoolExecutor.AbortPolicy());//银行满了还有人进来,就不处理这个人的了,抛出异常RejectedExecutionException
     *    new ThreadPoolExecutor.CallerRunsPolicy()//哪来的去哪里,让回来的人去执行这条线程 这里就会让main线程去执行
     *    new ThreadPoolExecutor.DiscardPolicy()//队列满了,丢掉任务(不执行后续的),不会抛出异常
     *    new ThreadPoolExecutor.DiscardOldestPolicy()//队列满了,尝试去和最早的竞争(竞争成功则执行竞争失败就抛弃不执行),也不会抛出异常
     */
    public static void main(String[] args) {
        //(单核的情况下来大于9个人就会报错)银行常开窗口2个,最多开5个窗口,等待3秒没有人就减少打开窗口数,可等候4人,窗口已全部打开等候人数也已满,再来人则抛出异常
        //多核,超过其处理量则会开新的线程,最大线程都打开且超出阻塞的人数则会抛出异常

        ExecutorService threadPool = new ThreadPoolExecutor(2,5,3,
                TimeUnit.SECONDS,new LinkedBlockingDeque<>(4),Executors.defaultThreadFactory(),
                //java.util.concurrent.RejectedExecutionException
                //new ThreadPoolExecutor.AbortPolicy()//银行满了还有人进来,就不处理这个人的了,抛出异常
                //new ThreadPoolExecutor.CallerRunsPolicy()//哪来的去哪里,让回来的人去执行这条线程 这里就会让main线程去执行
                //new ThreadPoolExecutor.DiscardPolicy()//队列满了,丢掉任务(不执行后续的),不会抛出异常
                new ThreadPoolExecutor.DiscardOldestPolicy()//队列满了,尝试去和最早的竞争(竞争成功则执行竞争失败就抛弃不执行),也不会抛出异常
        );
        try {
            for (int i = 0; i < 100; i++) {
                //使用线程池后,使用线程池来创建线程
                //new Thread().start();

                threadPool.execute(()->{
                    System.out.println(Thread.currentThread().getName()+":ok");

                });
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            //线程池用完要关闭
            threadPool.shutdown();
        }
    }

小结
了解.CPU密集型,IO密集型(调优)
最大线程该如何定义?
1.CPU密集型 电脑多少核就可以有多少条线程同时执行,可以几核就定义几,可以保持CPU效率最高!
2.IO密集型 判断你程序中十分耗IO的线程有多少个,只要大于它即可,一般设置大于它的两倍

/**
     * 最大线程该如何定义?
     * 1.CPU密集型   电脑多少核就可以有多少条线程同时执行,可以几核就定义几,可以保持CPU效率最高!
     * 2.IO密集型   判断你程序中十分耗IO的线程有多少个,只要大于它即可,一般设置大于它的两倍
     * 程序  15个大型任务 IO十分占用资源
     */
    public static void main(String[] args) {
        //获取cpu的核数
        System.out.println(Runtime.getRuntime().availableProcessors());

        ExecutorService threadPool = new ThreadPoolExecutor(2,Runtime.getRuntime().availableProcessors(),3,
                TimeUnit.SECONDS,new LinkedBlockingDeque<>(4),Executors.defaultThreadFactory(),
                new ThreadPoolExecutor.CallerRunsPolicy()
        );
        try {
            for (int i = 0; i < 100; i++) {
                //使用线程池后,使用线程池来创建线程
                //new Thread().start();

                threadPool.execute(()->{
                    System.out.println(Thread.currentThread().getName()+":ok");

                });
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            //线程池用完要关闭
            threadPool.shutdown();
        }
    }

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

新时代程序员必须掌握:lambda表达式、链式编程、函数式接口、Stream流式计算

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

@FunctionalInterface
public interface Runnable {
    public abstract void run();
}
//java中超级多FunctionalInterface
//简化编程模型,在新版本的框架底层大量应用
//forEach(消费者类的函数式接口)

四大函数式接口: Consumer Function Predicate Supplier

在这里插入图片描述
代码测试:

Function 函数式接口

**
 * @ClassName Demo01
 * @Description  Function 函数式接口  有一个输入参数 有一个输出参数
 *              只要是函数式接口,就可以用lambda表达式简化
 * @Author
 * @Date
 **/
public class Demo01 {
    public static void main(String[] args) {
        //工具类,输出输入的值
        Function function = new Function<String,String>(){
            @Override
            public String apply(String s) {
                return s;
            }
        };
        //lambda表达式简化版
        Function function1 = (s)->{
            return s;
        };
        //更精简版
        Function function2 = s->{
            return s;
        };
        System.out.println(function.apply("beibei"));
        System.out.println(function1.apply("short beibei"));
        System.out.println(function2.apply("short simple beibei"));
    }
}

Predicate 断定型接口:有一个输入参数,返回值是boolean
在这里插入图片描述
/**

  • @ClassName PredicateDemo01

  • @Description Predicate 断定型接口:有一个输入参数,返回值是boolean
    **/
    public class PredicateDemo01 {

    public static void main(String[] args) {
    //判断字符串是否为空
    Predicate predicate = new Predicate(){
    @Override
    public boolean test(String s) {
    return s.isEmpty();
    }
    };
    Predicate predicate1 =s->{
    return s.isEmpty();
    };
    System.out.println(predicate.test(“beibei”));
    System.out.println(predicate1.test(""));

    }
    }

Consumer 消费型接口 只有入参 没有返回值
在这里插入图片描述

public class ConsumerDemo01 {
    public static void main(String[] args) {
        Consumer<String> consumer = new Consumer<String>() {
            @Override
            public void accept(String str) {
                System.out.println(str);

            }
        };
        Consumer<String> consumer1 = str-> {
          System.out.println(str);
        };
        consumer.accept("beibei");
        consumer1.accept("short beiebi");
    }
}

Supplier 供给型接口 没有参数只有返回值
在这里插入图片描述

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

        Supplier supplier1 = ()-> {
            return "short beibei";
        };
        System.out.println(supplier.get());
        System.out.println(supplier1.get());
    }
}

Stream流式计算

什么是流式计算?

大数据存储+计算
集合、MySQL本质就是存储东西的
计算都应该交给流来计算
在这里插入图片描述

/**
 * @ClassName Test
 * @Description
 * 题目要求:一分钟内完成此题,只能用一行代码
 * 现有6个用户,筛选:
 * 1.ID必须是偶数
 * 2.年龄必须大于23
 * 3.用户名转为大写字母
 * 4.用户名字母倒着排序
 * 5.只输出一个用户
 **/
public class Test {
    public static void main(String[] args) {
        User u1 = new User(1,"a",21);
        User u2 = new User(2,"b",22);
        User u3 = new User(3,"c",23);
        User u4 = new User(4,"d",24);
        User u5 = new User(5,"e",25);
        User u6 = new User(6,"f",26);
        //集合就是存储
        List<User> list = Arrays.asList(u1,u2,u3,u4,u5,u6);

        //计算交给Stream流
        //lambda表达式、链式编程、函数式接口、Stream流式计算
        list.stream().filter(u->{return u.getId()%2==0;})
                .filter(u->{return u.getAge()>23;})
                .map(u->{return u.getName().toUpperCase();})
               // .sorted((z1,z2)->{return z1.compareTo(z2);})//正序
                .sorted((z1,z2)->{return z2.compareTo(z1);})//倒序
                .limit(1)
                .forEach(System.out::println);

    }
}
ForkJoin

ForkJoin

ForkJoin 1.7之后出现的,并行执行任务!提高效率,大数据量!
大数据:Map Reduce(把大任务拆分为小任务)

ForkJoin特点:工作窃取 (当A、B两个线程执行时,A只执行了部分,B已经执行完成时B会拿A还未执行的任务去执行)
ForkJoin里面维护的都是双端队列,所以才可以这样去操作
在这里插入图片描述

在这里插入图片描述

/**
 * @ClassName ForkJoinDemo01
 * @Description 求和计算任务  low  middle forkJoin high stream并行流
 * 如何使用forkJoin
 * 1.forkJoinPool 通过它来执行
 * 2.forkJoinPool。execute(ForkJoinTask<?> task)
 * 3.计算类要集成ForkJoinTask
 **/
public class ForkJoinDemo01 extends RecursiveTask<Long> {

    //据说比较low的
    public static void count1() {
        long sum=0;
        long startTime = System.currentTimeMillis();
        for (long i = 0; i <= 10_0000_0000L; i++) {
            sum+=i;
        }
        long endTime = System.currentTimeMillis();
        System.out.println("sum="+(endTime-startTime));

     //   System.out.println(sum);
    }

    private Long start;
    private Long end;
    //临界值,超过临界值就拆分
    private Long temp = 10000L;
    public ForkJoinDemo01(Long start,Long end){
        this.start=start;
        this.end=end;
    }
    //据说middle
    public static void count2() throws ExecutionException, InterruptedException {
        long startTime = System.currentTimeMillis();
        ForkJoinPool forkJoinPool = new ForkJoinPool();
        ForkJoinDemo01 tesk = new ForkJoinDemo01(0L, 10_0000_0000L);
        forkJoinPool.execute(tesk);//执行一个任务,无返回值
      /*  ForkJoinTask<Long> submit = forkJoinPool.submit(tesk);//提交一个任务,有返回值

        Long sum = submit.get();*/

        long endTime = System.currentTimeMillis();
        System.out.println("sum="+(endTime-startTime));
    }
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        count1();//353ms
        count2();//3ms
        count3();//289ms

    }

    @Override
    protected Long compute() {
        if((end-start) < temp){
            Long sum = 0L;
            for (Long i = start; i <= end; i++) {
                sum+=i;
            }
            return sum;
        }else{//forkJoin
          //分支合并计算
            long middle = (start+end)/2;//中间值
            ForkJoinDemo01 task1 = new ForkJoinDemo01(start, middle);
            task1.fork();//拆分任务,把任务压入线程队列
            ForkJoinDemo01 task2 = new ForkJoinDemo01(middle, end);
            task2.fork();
            return task1.join()+task2.join();
        }

    }

    //据说high,并行流
    public static void count3(){
        long startTime = System.currentTimeMillis();
        //Stream并行流
        long sum = LongStream.rangeClosed(0L,10_0000_0000L).parallel().reduce(0,Long::sum);
        long endTime = System.currentTimeMillis();
        System.out.println("sum="+(endTime-startTime));
    }


}

为毛我的ForkJoin效率最高?

异步回调

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

/**
 * @ClassName FutureDemo01
 * @Description 异步调用 CompletableFuture
 *  //异步执行
 *  //成功回调
 *  //失败回调
 **/
public class FutureDemo01 {
   /* public static void main(String[] args) {
        //Void没有返回值的异步调用
        CompletableFuture<Void> completableFuture = CompletableFuture.runAsync(()->{
        *//*    try {
                TimeUnit.SECONDS.sleep(2);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }*//*
            System.out.println(Thread.currentThread().getName()+"runAsync=>Void");
        });
        System.out.println("123");//会先打印
        //获取执行结果
        try {
            completableFuture.get();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        }
    }*/
   public static void main(String[] args) throws ExecutionException, InterruptedException {
       //有返回值的supplyAsync异步回调
       CompletableFuture<Integer> completableFuture=CompletableFuture.supplyAsync(()->{
           System.out.println(Thread.currentThread().getName()+"supplyAsync=>Integer");
           //int i=6/0;//测试错误的情况
           return 520;
       });
       System.out.println("123");//会先打印

       //成功和失败的回调
       System.out.println(completableFuture.whenComplete((t, u) -> {
           System.out.println("t=>" + t);//正常的返回结果,错误时为null
           System.out.println("u=>" + u);//错误信息:java.lang.ArithmeticException: / by zero,如果没有错误信息则显示null
       }).exceptionally(e -> {
           System.out.println(e.getMessage());
           return 500;//可以获取到错误的返回结果
       }).get());
   }
}
JMM

谈论对Volatile的理解

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

可见性是如何保证的?JMM

JMM

JMM: java内存模型,不存在的东西,概念!约定!
关于JMM的一些同步的约定
1.线程解锁前,必须把共享变量立刻刷回主存
2.线程加锁前,必须读取主存中的最新值到工作内容中
3.加锁和解锁是同一把锁

线程 工作内存 主内存

8种操作:
内存交互操作
  内存交互操作有8种,虚拟机实现必须保证每一个操作都是原子的,不可在分的(对于double和long类型的变量来说,load、store、read和write操作在某些平台上允许例外)

  • lock (锁定):作用于主内存的变量,把一个变量标识为线程独占状态

  • unlock (解锁):作用于主内存的变量,它把一个处于锁定状态的变量释放出来,释放后的变量才可以被其他线程锁定

  • read (读取):作用于主内存变量,它把一个变量的值从主内存传输到线程的工作内存中,以便随后的load动作使用

  • load (载入):作用于工作内存的变量,它把read操作从主存中变量放入工作内存中

  • use (使用):作用于工作内存中的变量,它把工作内存中的变量传输给执行引擎,每当虚拟机遇到一个需要使用到变量的值,就会使用到这个指令

  • assign (赋值):作用于工作内存中的变量,它把一个从执行引擎中接受到的值放入工作内存的变量副本中

  • store (存储):作用于主内存中的变量,它把一个从工作内存中一个变量的值传送到主内存中,以便后续的write使用

  • write  (写入):作用于主内存中的变量,它把store操作从工作内存中得到的变量的值放入主内存的变量中
     
      JMM对这八种指令的使用,制定了如下规则:

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

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

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

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

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

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

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

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

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

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

            }

        }).start();
        try {
            TimeUnit.SECONDS.sleep(2);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        num =1;//程序并没有像详细中停下来,线程将主内存中的num改为了1但是线程1不知道还是0所以未停止
        System.out.println(num);
    }
}
Volatile

1.保证可见性

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

            }

        }).start();
        try {
            TimeUnit.SECONDS.sleep(2);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        num =1;//程序并没有像详细中停下来,线程将主内存中的num改为了1但是线程1不知道还是0所以未停止
        System.out.println(num);
    }
}

2.不保证原子性

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

/**
 * @ClassName VolatileDemo02
 * @Description 不保证原子性
 **/
public class VolatileDemo02 {
    //每一次运算的数值不一样,不是期望的20000
    //private static int num =0;
    //每一次运算的数值仍然不一样,不是期望的20000
    private volatile static int num =0;
    public static void add(){
        num++;
    }
    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){//为什么是大于2 因为java默认两个线程main 和 gc
            Thread.yield();
        }
        System.out.println(Thread.currentThread().getName()+""+num);
    }
}

在这里插入图片描述

如果不加lock和synchronized,怎样保证原子性?
可使用原子类操作

public class VolatileDemo02 {
    //每一次运算的数值不一样,不是期望的20000
    //private static int num =0;
    //每一次运算的数值仍然不一样,不是期望的20000
    //private volatile static int num =0;
    //使用原子性的类来保证其原子性
    private volatile static AtomicInteger num =new AtomicInteger();//要不要volatile都可
    public static void add(){
       // num++;//不是一个原子性操作
        num.getAndDecrement();//AtomicInteger的+1方法,底层不是num++,而是底层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){//为什么是大于2 因为java默认两个线程main 和 gc
            Thread.yield();
        }
        System.out.println(Thread.currentThread().getName()+""+num);
    }
}

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

在这里插入图片描述

3.禁止指令重排

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

int x=1;
int y=2;
x=x+5;
y=x+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是可以保证可见性、不能保证原子性,由于内存屏障,可以保证避免指令重排现象产生
Volatile在哪里使用最多?单例模式中使用多

彻底玩转单例模式

饿汉式、DCL懒汉式

饿汉式

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 static final  Hungry HUNGRY = new Hungry();

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

DCL懒汉式

/**
 * @ClassName LazyMan
 * @Description 懒汉式单例
 **/
public class LazyMan {

    private static boolean flag=false;
    private LazyMan(){
        System.out.println(Thread.currentThread().getName()+"-ok");
        //处理反射破坏单例的情况
        //如果两个都用反射初始化,在构造器里的方法就不再管用,用一个标志位去增强
       /* synchronized (LazyMan.class){
            if(lazyMan!=null){
                throw new RuntimeException("不要试图使用反射破坏单例");
            }
        }*/

        synchronized (LazyMan.class){
            if(flag==false){
                flag=true;
            }else {
                throw new RuntimeException("不要试图使用反射破坏单例");
            }
        }

    }
    //private static LazyMan lazyMan;
    private static volatile  LazyMan lazyMan;

    //单线程下确实单例是ok的,多线程会有问题
    /*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){//在需要的时候才去加载
                    lazyMan= new LazyMan();//在极端情况下会有问题,不是一个原子性操作
                    /**
                     * 1.分配内存空间
                     * 2.执行构造方法初始化对象
                     * 3.把这个对象指向这个空间
                     *
                     * 如果走向132,如果多线程的时候,线程A走了132,来了线程B内存空间中以为不是空对象就没有初始化就会有问题
                     *
                     * so 需要把对象定义为volatile
                     */
                }
            }
        }
        return lazyMan;
    }


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

            }).start();
        }
    }*/

    //反射- 反射可以破坏单例
    public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException, NoSuchFieldException {
        LazyMan lazyMan= LazyMan.getInstance();
        Field flag = LazyMan.class.getDeclaredField("flag");
        Constructor<LazyMan> declaredConstructor = LazyMan.class.getDeclaredConstructor(null);
        declaredConstructor.setAccessible(true);
        flag.set(lazyMan,false);//把标志位修改回来,用反射去破坏标志位
        LazyMan lazyMan2=declaredConstructor.newInstance();
        flag.set(lazyMan2,false);//把标志位修改回来,用反射去破坏标志位
        LazyMan lazyMan3=declaredConstructor.newInstance();
        System.out.println(lazyMan);//com.beibei.singletondemo.LazyMan@5a07e868
        System.out.println(lazyMan2);//com.beibei.singletondemo.LazyMan@76ed5528
        System.out.println(lazyMan3);//如果两个都用反射初始化,在构造器里的方法就不再管用-可用标志位处理
    }
}

静态内部类

/**
 * @ClassName Holder
 * @Description 静态内部类
 **/
public class Holder {
    //单例模式 构造器私有
    private Holder() {
    }

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

    }
}

单例不安全:会被反射破坏

枚举

/**
 * @ClassName EnumSingle
 * @Description 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 instance1 = EnumSingle.INSTANCE;
        EnumSingle instance2 = EnumSingle.INSTANCE;
        System.out.println(instance1);
        System.out.println(instance2);
        //测试反射会不会破坏

        //在idea中看编译的class文件有一个空餐构造器,反射未取到
        //javap -p EnumSingle.class 也有一个有参数 ,用jad工具发现是有参数的构造器
        //NoSuchMethodException: com.beibei.singletondemo.EnumSingle.<init>()
        //Constructor<EnumSingle> declaredConstructor = EnumSingle.class.getDeclaredConstructor(null);

        //java.lang.IllegalArgumentException: Cannot reflectively create enum objects
        //枚举不会被反射破坏单例
        Constructor<EnumSingle> declaredConstructor = EnumSingle.class.getDeclaredConstructor(String.class,int.class);
        declaredConstructor.setAccessible(true);
        EnumSingle enumSingle = declaredConstructor.newInstance();
        System.out.println(instance1);
        System.out.println(enumSingle);
    }
}
深入理解CAS

什么是CAS

大厂必须要深入研究底层!有所突破!修内功,操作系统,计算机网络原理

unsafe类

在这里插入图片描述

   public final int getAndIncrement() {
        return unsafe.getAndAddInt(this, valueOffset, 1);
   }
 
  public final int getAndAddInt(Object var1, long var2, int var4) {
        int var5;
        //自旋锁
        do {
           //获取内存地址的值
            var5 = this.getIntVolatile(var1, var2);
            //如果var5还是var1 var2内存地址的值,那么就将var5+1(内存操作效率很高)
        } while(!this.compareAndSwapInt(var1, var2, var5, var5 + var4));

        return var5;
    }

CAS:compareAndSet 比较内存中的值和主内存中的值,如果这个值是期望的,那么执行操作!如果不是就一致循环
缺点:
1.循环会耗时(因为底层是自旋锁)
2.一次性只能保证一个共享变量的原子性(因为是底层的cpu操作)
3.会存在ABA问题

CAS:ABA问题(狸猫换太子)
线程A:cas(1,2) 线程B在A还没有取到数据之前做了cas(1,3) cas(3,1)
虽然值还是1,但是已经不是原来的1了

ABA模拟

/**
 * @ClassName CasDemo02
 * @Description 模拟ABA
 **/
public class CasDemo02 {
    //CAS compareAndSet 比较并交换
    public static void main(String[] args) {

        //ABA处理=》对于我们SQL就是乐观锁;对于我们代码就是原子引用
        AtomicInteger atomicInteger = new AtomicInteger(2020);
        System.out.println(atomicInteger.get());
        //==========捣乱的线程============
        System.out.println(atomicInteger.compareAndSet(2020, 2021));
        System.out.println(atomicInteger.get());


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

    }
}
原子引用

用来处理ABA问题,引入原子引用!对应的思想:乐观锁!

狂神视屏用期望值2020失败了。失败原因:
Integer使用了对象缓存机制,默认范围为-128-127,推荐使用静态工厂方法valueOf获取对象实例,而不是new,因为valueOf使用缓存,而new一定会创建新的对象分配新的内训空间
在这里插入图片描述
一堆文字可能也看不懂,是因为底层比较相等用的==Integer数值在-128到127可以用/==比较超出以后需要用equals比较


/**
 * @ClassName CastDemo03
 * @Description 用原子引用处理ABA的情况。原理类似于乐观锁
 **/
public class CastDemo03 {
    public static void main(String[] args) {
        //狂神视屏用期望值2020失败了。失败原因:Integer使用了对象缓存机制,默认范围为-128-127,推荐使用静态工厂方法valueOf获取对象实例,而不是new,因为valueOf使用缓存,而new一定会创建新的对象分配新的内训空间
        //1类似于SQl乐观锁的版本号
        //AtomicStampedReference 注意:如果泛型是一个包装类,注意对象的引用问题
        AtomicStampedReference<Integer> atomicInteger = new AtomicStampedReference<>(102, 1);
        new Thread(()->{
            int stamp = atomicInteger.getStamp();//获取版本号
            System.out.println("a1 = " + stamp);
            try {
                TimeUnit.SECONDS.sleep(2);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            //修改版本号 atomicInteger.getStamp()+1
            System.out.println("a2"+atomicInteger.compareAndSet(102, 103, atomicInteger.getStamp(), atomicInteger.getStamp() + 1));
            System.out.println("a2 = " + atomicInteger.getStamp());
            System.out.println("a3"+atomicInteger.compareAndSet(103, 102, atomicInteger.getStamp(), atomicInteger.getStamp() + 1));
            System.out.println("a3 = " + atomicInteger.getStamp());
        },"a").start();

        new Thread(()->{
            int stamp = atomicInteger.getStamp();//获取版本号
            System.out.println("b1 = " + stamp);
            try {
                TimeUnit.SECONDS.sleep(3);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("b2"+atomicInteger.compareAndSet(102, 555,stamp, stamp + 1));
            System.out.println("b2 = " + atomicInteger.getStamp());

        },"b").start();
    }
}
各种锁的理解
公平锁、非公平锁

公平锁:非常公平,不能插队,必须先来后到
非公平锁:非常不公平,可以插队,比如1个线程3s一个执行3h,3s的就有可能插队,(默认都是非公平)
默认非公平:
在这里插入图片描述
可修改默认公平还是非公平
在这里插入图片描述

可重入锁

可重入锁(递归锁):拿到外面的锁之后,就可以拿到里面的锁(自动获得)
如以下代码所示,是要把一个线程把sms(外面的锁)和call(里面的锁)都走完其余的线程才能拿到资源

public class ReentrantLockDemo02 {
    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(); //对应本方法中的lock.unlock();
        //lock.lock();//如果锁两次就要有2个解锁否则就会死锁其余人拿不到资源
        try {
            System.out.println(Thread.currentThread().getName()+"sms");
            call();//这里也有锁
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }
    public void call(){
        lock.lock();
        try {
            System.out.println(Thread.currentThread().getName()+"call");
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }

    }
}
public class SynchronizedDemo01 {

    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(){
        System.out.println(Thread.currentThread().getName()+"call");

    }
}

自旋锁

自旋锁(spinlock)
在这里插入图片描述

/**
 * @ClassName SpinlockDemo03
 * @Description 自旋锁
 **/
public class SpinlockDemo03 {
    //默认不写如果是int类型默认就是0
    //Thread 对象 默认 null
    AtomicReference<Thread> atomicReference = new AtomicReference<>();
    //加锁
    public void myLock(){
        Thread thread = Thread.currentThread();
        System.out.println(Thread.currentThread().getName()+"=>myLock");
        //自旋锁
        while(!atomicReference.compareAndSet(null,thread)){//一直自旋直到拿到锁

        }
        System.out.println(Thread.currentThread().getName()+"===拿到了锁");
    }
    //解锁
    public void myUnlock(){
        Thread thread = Thread.currentThread();
        System.out.println(Thread.currentThread().getName()+"=>myUnLock");
        atomicReference.compareAndSet(thread,null);
    }

}
public class TestSpinLock {
    public static void main(String[] args) {

       /* ReentrantLock reentrantLock = new ReentrantLock();
        reentrantLock.lock();
        reentrantLock.unlock();*/

        //自定义的锁
        SpinlockDemo03 spinlock = new SpinlockDemo03();
        new Thread(()->{
            spinlock.myLock();
            try {
                TimeUnit.SECONDS.sleep(5);
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                spinlock.myUnlock();
            }
        },"T1").start();
        new Thread(()->{
            spinlock.myLock();
            try {
                TimeUnit.SECONDS.sleep(1);
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                spinlock.myUnlock();
            }
        },"T2").start();
    }
}

执行结果:
在这里插入图片描述

死锁

死锁是什么?

两个线程互相抢夺资源;A持有A的锁,B持有B的锁,A要去拿B的锁,B要去拿A的锁

public class DeadLockDemo {
    public static void main(String[] args) {

        String lockA="lockA";
        String lockB="lockB";
        new Thread(new MyThread(lockA,lockB),"T1").start();
        new Thread(new MyThread(lockB,lockA),"T2").start();
    }
}

class MyThread implements Runnable{
    private String lockA;
    private String lockB;

    public MyThread(String lockA, String lockB) {
        this.lockA=lockA;
        this.lockB=lockB;
    }

    @Override
    public void run() {

        synchronized (lockA){
            System.out.println(Thread.currentThread().getName()+"lock:"+lockA+"=>get:"+lockB);
            try {
                TimeUnit.SECONDS.sleep(2);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            synchronized (lockB){
                System.out.println(Thread.currentThread().getName()+"lock:"+lockB+"=>get:"+lockA);

            }
        }
    }
}

排查解决问题

工具,jdk bin目录下 jps
1.jps定位进程号jps -l(类似于linux的ls -i)
2.查看进程的堆栈信息 jstack 进程号,找到死锁信息
在这里插入图片描述
面试,工作中排查问题
1.日志
2.堆栈信息

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值