JUC基础认知

99 阅读12分钟

一、线程和进程

1.进程:一个程序,如qq.exe、music.exe程序的集合

  • 一个进程往往可以包含多个线程,至少含有一个进程
  • java默认有两个线程,main 和 GC
  • 线程:程序里面的一个执行方式 如 发送、保存等

Java三种方式: Thread、Runnable、Callable

 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 */
            }
        }
    }
    //java没办法自己开启,调用底层C++操作操作系统
    private native void start0();

二、并发、并行

1.并发(多线程操作同一个资源)

  • cpu一核,模拟出来多条线程,快速交替 2.并行(多个人一起行走)
  • cpu多核,多个线程同时执行:线程池
    public static void main(String[] args) {
        //获取cpu的核数
        //cpu密集型 IO密集型
        System.out.println(Runtime.getRuntime().availableProcessors());
    }

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

 public enum State {
       
       //新生
        NEW,
        
        //运行
        RUNNABLE,

        //阻塞
        BLOCKED,

        //等待
        WAITING,

        //超时等待
        TIMED_WAITING,

        //终止
        TERMINATED;
    }

三、wait/sleep区别 1.来自不同的类

wait => Object

sleep=> Thread

2.关锁的释放 wait会释放锁,sleep 睡觉了,不会是释放

3.使用的范围是不同的

wait必须在同步代码块中,不用捕获异常

sleep 可以在任何地方,必须捕获异常

四、LOCK锁

1.传统synchronized 和 Lock区别

  • synchronized 内置java关键字 , Lock是一个java类
  • synchronized 无法判断获取锁的状态 lock 可以判断是否获取到了锁
  • synchronized 自动释放锁 lock手动释放锁
  • synchronized 线程1阻塞 线程2一直等 lock不一定会等
  • synchronized 可重入锁,不可中断 非公平 lock 可重入锁可以判断锁 非公平(可以自己设置)
  • synchronized 适合锁少量的代码同步问题 lock适合锁大量的同步代码

锁 将资源私有化

五、8锁现象

package com.cxx.juc.day003;

import java.util.concurrent.TimeUnit;

/**
 * 8锁,就是关于锁的8个问题
 * 1.标准情况下,两个线程先打印是先发短信还是打电话
 * 2.发短信延迟4s,两个线程先打印是先发短信还是打电话
 * 3.添加一个普通方法,先发短信还是hello
 * 4.两个对象,两个同步方法,发短信还是打电话
 * 5.增加两个静态的同步方法 一个对象 先打印还是发短信
 * 6.两个对象 增加两个静态的同步方法,先打印还是打电话
 * 7.一个静态方法。1个普通方法 ,一个对象 先打印 发短信还是打电话
 * 8.1个静态的同步方法,1个普通的同步方法,先打印发短信还是打电话
 * @author cxx
 * @version 1.0
 * @date 2021/12/20 9:23
 */
public class lock4 {

    public static void main(String[] args) {
        //两个对象两把锁,按延迟时间来
        phone4 phone = new phone4();

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

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

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

class phone4{

    //synchronized 锁的对象是方法的调用者
    // 两个方法用的是同一个问题 ,谁先拿到谁执行
    // static 静态方法 类一加载就有了 锁的是class
    public static synchronized void sendSms(){
        try {
            TimeUnit.SECONDS.sleep(2);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("发短信");
    }

    public static synchronized void call(){
        System.out.println("打电话");
    }

    //这里没有锁 不是同步方法,不受锁的影响
    public synchronized void hello(){
        System.out.println("hello");
    }

}

小结:

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

六、集合类不安全

package com.cxx.juc.day004;

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

/**
 * java.util.ConcurrentModificationException 并发修改异常
 *  并发下ArrayList 不安全
 *  解决方案
 *  1.List<String> list = new Vector<>(); 效率低
 *  2.      Collection<String> list = Collections.synchronizedCollection(
 *                 new ArrayList<>()
 *         );
 *  3,List<String> list = new CopyOnWriteArrayList<>();
 * @author cxx
 * @version 1.0
 * @date 2021/12/20 10:25
 */
public class ListTest {

    public static void main(String[] args) {


//        Collection<String> list = Collections.synchronizedCollection(
//                new ArrayList<>()
//        );
//        List<String> list = new Vector<>();
        //CopyOnWrite 写入时复制 cow 计算机程序设计领域的一种优化策略
        // 多个线程调用的时候,list,读取的时候,固定的 ,写入(覆盖)
        // 在写入的时候避免覆盖(复制),造成数据问题!
        List<String> list = new CopyOnWriteArrayList<>();

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

    }

}

hashset的底层是什么

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

七、callable

package com.cxx.juc.day004;

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

/**
 * TODO
 *
 * @author cxx
 * @version 1.0
 * @date 2021/12/20 13:48
 */
public class CallableTest {

    public static void main(String[] args) throws ExecutionException, InterruptedException {
        MyThread thread = new MyThread();
        FutureTask futureTask = new FutureTask<>(thread);
        new Thread(futureTask,"A").start();//适配类
        new Thread(futureTask,"B").start();
        Integer o = (Integer) futureTask.get(); //这个get可能引起阻塞,把他放到最后,或者用异步
        System.out.println(o);
    }


}

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

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

package com.cxx.juc.day004;

import java.util.concurrent.CountDownLatch;

/**
 * 计数器
 *
 * @author cxx
 * @version 1.0
 * @date 2021/12/20 15:38
 */
public class CountDownLatchDemo {


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

        CountDownLatch countDownLatch = new CountDownLatch(6);

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

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

        System.out.println("close door");

    }

}

原理:

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

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

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

package com.cxx.juc.day004;

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

/**
 * 加法计数器
 *
 * @author cxx
 * @version 1.0
 * @date 2021/12/20 15:57
 */
public class CyclicBarrierDemo {

    public static void main(String[] args) {
        CyclicBarrier cyclicBarrier = new CyclicBarrier(7,()->{
            System.out.println("召唤神龙成功");
        });

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

        }

    }


}


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

/**
 * TODO
 *
 * @author cxx
 * @version 1.0
 * @date 2021/12/20 16:08
 */
public class SemaphoreDemo {
    public static void main(String[] args) {
        Semaphore semaphore = new Semaphore(3);

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

            new Thread(()->{
                try {
                    semaphore.acquire();//得到
                    System.out.println(Thread.currentThread().getName() + "抢到车位");
                    TimeUnit.SECONDS.sleep(2);
                    System.out.println(Thread.currentThread().getName() + "离开车位");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                semaphore.release();//释放

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

        }
    }
}

semaphore.acquire(); 获得,假设如果已经蛮了,等待,等待被释放为止

semaphore.release();释放,会将当前的信号量+1,然后唤醒等待的线程

九、读写锁

package com.cxx.juc.day004;

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

/**
 * 写锁 (独占锁)一次只能被一些线程占有
 * 读锁 (共享锁)多个线程可以同时占有
 * 读-读 可以共存
 * 读-写 不能共存
 * 写-写 不能共存
 * @author cxx
 * @version 1.0
 * @date 2021/12/20 16:24
 */
public class ReadWriterLockDemo {

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

        for (int i = 0; i < 6; i++) {
            int finalI = i;
            new Thread(()->{
                myCache.put(finalI+"",finalI);
            },String.valueOf(i)).start();
        }

        for (int i = 0; i < 6; i++) {
            int finalI = i;
            new Thread(()->{
                myCache.get(finalI+"");
            },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();
        System.out.println(Thread.currentThread().getName() + "写入" +key);
        map.put(key,value);
        System.out.println(Thread.currentThread().getName() + "写入OK");
        readWriteLock.writeLock().unlock();
    }

    //所有人都可以读
    public void get(String key){
        readWriteLock.readLock().lock();
        System.out.println(Thread.currentThread().getName() + "读取" +key);
        map.get(key);
        System.out.println(Thread.currentThread().getName() + "读取OK");
        readWriteLock.readLock().unlock();
    }

}

10、阻塞队列

11、线程池

三大方法、7大参数、4种拒绝策略

池化技术

程序的运行,本质:占用系统的资源!优化资源的使用!

线程池、连接池、内存池、对象池

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

线程池的好处:

1.降低资源的消耗

2.提高响应的速度

3.方便管理

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

三大方法:

package com.cxx.juc.day005;

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

/**
 * TODO
 *
 * @author cxx
 * @version 1.0
 * @date 2021/12/21 9:54
 */
public class pooldemo1 {

    public static void main(String[] args) {
//        ExecutorService pool = Executors.newSingleThreadExecutor();//单个线程
//        ExecutorService pool =Executors.newFixedThreadPool(5);//创建一个固定的线程池的大小
        ExecutorService pool =Executors.newCachedThreadPool();//可伸缩的,

        try {
            for (int i = 0; i < 30 ; i++) {
                //使用线程池创建线程
                pool.execute(()->{
                    System.out.println(Thread.currentThread().getName() + "   okk");
                });
            }
        } finally {
            //线程池用完关闭
            pool.shutdown();
        }

    }
}

newSingleThreadExecutor

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

newFixedThreadPool

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

newCachedThreadPool

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

七大参数

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

四种拒绝策略

        new ThreadPoolExecutor.AbortPolicy(); // 满了抛出一次
        new ThreadPoolExecutor.CallerRunsPolicy(); //哪里来的去哪里
        new ThreadPoolExecutor.DiscardOldestPolicy(); //队列满了,丢掉任务,不会抛出异常
        new ThreadPoolExecutor.DiscardPolicy(); //队列满了,尝试去和最早的竞争,也不会抛出异常

小结和拓展 池最大小如何去设置 //1.cpu 密集型 几核 就是几,可以保持cpu的效率最高 Runtime.getRuntime().availableProcessors(), //2.IO 密集型 > 判断你的程序中十分耗IO的线程

package com.cxx.juc.day005;

import java.util.concurrent.*;

/**
 * TODO
 *
 * @author cxx
 * @version 1.0
 * @date 2021/12/21 9:54
 */
public class pooldemo1 {

    public static void main(String[] args) {
//        ExecutorService pool = Executors.newSingleThreadExecutor();//单个线程
//        ExecutorService pool =Executors.newFixedThreadPool(5);//创建一个固定的线程池的大小
//        ExecutorService pool =Executors.newCachedThreadPool();//可伸缩的,.

//        new ThreadPoolExecutor.AbortPolicy();
//        new ThreadPoolExecutor.CallerRunsPolicy();
//        new ThreadPoolExecutor.DiscardOldestPolicy();
//        new ThreadPoolExecutor.DiscardPolicy();

        //最大线程数该如何定义
        //1.cpu 密集型 几核 就是几,可以保持cpu的效率最高 Runtime.getRuntime().availableProcessors(),
        //2.IO 密集型 > 判断你的程序中十分耗IO的线程

        ThreadPoolExecutor pool = new ThreadPoolExecutor(
                2,
                Runtime.getRuntime().availableProcessors(),
                2, TimeUnit.SECONDS,
                new LinkedBlockingDeque<>(3),
                Executors.defaultThreadFactory(),
                new ThreadPoolExecutor.AbortPolicy());

        try {
            for (int i = 0; i < 8 ; i++) {
                //使用线程池创建线程
                pool.execute(()->{
                    System.out.println(Thread.currentThread().getName() + "   okk");
                });
            }
        } finally {
            //线程池用完关闭
            pool.shutdown();
        }

    }
}

16、JMM

请谈谈你对Volatile的理解

1.保证可见性

2.不保证原子性

3.禁止指令重拍

什么是JMM

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

关于JMM的一些同步的约定

1.线程解锁前,必须把共享变量立刻刷回主存

2.线程加锁前,必须读取主存中的最新值到工作内存中

3.加锁和解锁是同一把锁

17、Volatile

1.保证可见性

package com.cxx.juc.day006;

import java.util.concurrent.TimeUnit;

/**
 * TODO
 *
 * @author cxx
 * @version 1.0
 * @date 2021/12/22 9:41
 */
public class jmmdemo {
    private static volatile int num = 0;
    public static void main(String[] args) throws InterruptedException {

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

                }
            }).start();


        TimeUnit.SECONDS.sleep(2);

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



    }
}

2.不保证原子性

package com.cxx.juc.day006;

import java.util.concurrent.atomic.AtomicInteger;

/**
 * TODO
 *
 * @author cxx
 * @version 1.0
 * @date 2021/12/22 10:14
 */
public class jmmdemo2 {
    
    //volatile不保证原子性
    //integer的原子类
    private volatile static  AtomicInteger num  = new AtomicInteger();

    public static void add(){
        num.getAndIncrement();
    }
    public static void main(String[] args) {
        for (int i = 0; i < 20 ; i++) {
            new Thread(()->{
                for (int j = 0; j < 10000; j++) {
                    add();
                }
            }).start();
        }

        while (Thread.activeCount()>2){  //mian gc
            Thread.yield();
        }

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

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

指令重排

1.什么是指令重排:

你写的程序,计算机并不是按照你写的那样去执行的

源代码--》编译器优化的重排--》 指令并行也可能重排 -- 》 内存系统也会重排 --》 执行

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

饿汉式:

package com.cxx.juc.day006;

/**
 * 饿汉式单利
 *
 * @author cxx
 * @version 1.0
 * @date 2021/12/22 11:03
 */
public class hurry {

    private hurry (){
    }

    private final static hurry hurry = new hurry();

    public static hurry getInstance(){
        return hurry;
    }


}

懒汉式:

package com.cxx.juc.day006;

import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;

/**
 * 懒汉式单例
 *
 * @author cxx
 * @version 1.0
 * @date 2021/12/22 11:06
 */
/*
 * 1.分配内存
 * 2.执行构造方法、初始化对象
 * 3.吧这个对象指向这个空间
 * 指令可能重排
 * 123
 * 132
 **/
public class Lazyman {

    private Lazyman(){
        synchronized (Lazyman.class){
            if(lazyman!=null){
                throw new RuntimeException("不要瞎搞");
            }
            System.out.println(Thread.currentThread().getName());
        }

    }
    private volatile static Lazyman lazyman;
    // 双重检查锁模式
    public static Lazyman getInstance(){
        if(lazyman == null){
            synchronized (Lazyman.class){
                if(lazyman == null)
                lazyman = new Lazyman(); //不是原子性操作
            }
        }
        return lazyman; //lazyman未完成构造  所以用到volatile进行指令重排
    }
    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 lazyman = declaredConstructor.newInstance();

        System.out.println(instance);
        System.out.println(lazyman);
    }

}



静态内部类:

package com.cxx.juc.day006;

/**
 * 静态内部类
 *
 * @author cxx
 * @version 1.0
 * @date 2021/12/22 15:34
 */
public class holder {

    private holder(){
    }

    public static holder getInstance(){
        return InstanClass.holder;
    }

    public static class InstanClass{
        private static final holder holder = new holder();
    }
}

19、深入理解CAS

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

缺点: 1.循环会耗时 2.一次性只能保证一个共享变量的原子性

ABA问题: 引入原子引用,乐观锁原理

package com.cxx.juc.day006;

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

/**
 * TODO
 *
 * @author cxx
 * @version 1.0
 * @date 2021/12/22 17:24
 */
public class cas {

    //AtomicStampedReference 如果是一个包装类会有对象 引用问题
    static AtomicStampedReference<Integer> atomicStampedReference = new AtomicStampedReference<Integer>(2,1);


    //cas 比较并交换
    public static void main(String[] args) {
        new Thread(()->{
            int stamp = atomicStampedReference.getStamp();
            System.out.println("A1:  " + stamp);
            try {
                TimeUnit.SECONDS.sleep(2);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("A2: "+atomicStampedReference.compareAndSet(2, 3, stamp, stamp + 1));
            System.out.println("A2: "+atomicStampedReference.getStamp());
            System.out.println("A3: "+atomicStampedReference.compareAndSet(3, 2, stamp, stamp + 1));
            System.out.println("A3: "+atomicStampedReference.getStamp());
        }).start();

        new Thread(()->{
            int stamp = atomicStampedReference.getStamp();
            System.out.println("B1:  " + stamp);
            try {
                TimeUnit.SECONDS.sleep(2);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("B2:  " +atomicStampedReference.compareAndSet(2, 9, stamp, stamp + 1));
            System.out.println("B2:  " + atomicStampedReference.getStamp());
        }).start();


    }


}

21、各种锁的理解

公平锁:非常公平,不能插队

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

可重入锁: “可重入”,可重入就是说某个线程已经获得某个锁,可以再次获取锁而不会出现死锁

synchronized:

package com.cxx.juc.day007;

/**
 * 可重入锁
 *
 * @author cxx
 * @version 1.0
 * @date 2021/12/23 9:59
 */
public class synca {

    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()+"发短信");
        call();//这里也有锁
    }

    public synchronized void call() {
        System.out.println(Thread.currentThread().getName()+"打电话");
    }
}



Lock:

package com.cxx.juc.day007;

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

/**
 * TODO
 *
 * @author cxx
 * @version 1.0
 * @date 2021/12/23 10:05
 */
public class lockb {

    public static void main(String[] args) {

        phone1 phone = new phone1();
        new Thread(()->{
            phone.sms();
        },"A").start();

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

    }


}

class phone1 {

    Lock lock = new ReentrantLock();

    public  void sms() {

        lock.lock(); //细节问题,锁必须配对,加了两把锁,必须有两把钥匙,不然死锁
        lock.lock();
        try {
            System.out.println(Thread.currentThread().getName()+"发短信");
            call();
        } finally {
            lock.unlock();
            lock.unlock();
        }

    }

    public  void call() {
        lock.lock();
        try {
            System.out.println(Thread.currentThread().getName()+"打电话");
        } finally {
            lock.unlock();
        }
    }
}

自旋锁:

package com.cxx.juc.day007;

import java.util.concurrent.atomic.AtomicReference;

/**
 * TODO
 *
 * @author cxx
 * @version 1.0
 * @date 2021/12/23 10:26
 */
public class spinlock {

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

    public void slock(){
        Thread thread = Thread.currentThread();
        System.out.println(Thread.currentThread().getName()+"  lock OK");
        while (!atomicReference.compareAndSet(null,thread)) {}
    }

    public void sunlock(){
        Thread thread = Thread.currentThread();
        System.out.println(Thread.currentThread().getName()+"  unlock OK");
        atomicReference.compareAndSet(thread,null);
    }





}

package com.cxx.juc.day007;

import java.util.concurrent.TimeUnit;

/**
 * TODO
 *
 * @author cxx
 * @version 1.0
 * @date 2021/12/23 10:31
 */
public class spindemo {

    public static void main(String[] args) {

        spinlock spinlock = new spinlock();

        new Thread(()->{
            spinlock.slock();
            try {
                TimeUnit.SECONDS.sleep(3);
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                spinlock.sunlock();
            }
        },"A").start();


        new Thread(()->{
            spinlock.slock();
            try {
                TimeUnit.SECONDS.sleep(1);
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                spinlock.sunlock();
            }
        },"B").start();

    }
}

死锁: