1. CompletableFuture

1.线程基础知识复习-Future

    1. 本源的Future接口相关架构

image.png

    1. 编码案例 —- get()方法 ```java

import java.util.concurrent.ExecutionException; import java.util.concurrent.FutureTask; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException;

/**

  • 一旦调用get()方法,不管是否计算完成都会导致阻塞,
  • 如果是get(1,TimeUnit) 就会等待指定的时间 , 如果在指定的时间内没有完成就会抛出异常 **/ public class FutureTaskDemo_2 {

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

    1. FutureTask<String> futureTask = new FutureTask<>(() -> {
    2. TimeUnit.SECONDS.sleep(5);
    3. System.out.println("进入Future方法=====");
    4. return "执行完成";
    5. });
    6. new Thread(futureTask, "t1").start();
    7. // get方法会一直阻塞 , 等待结果执行完成
    8. // String s = futureTask.get();
    9. // System.out.println("s = " + s);
    10. // 会阻塞1秒钟, 如果1秒内没有执行完成, 就会结束掉方法抛出异常
    11. String s1 = futureTask.get(1, TimeUnit.SECONDS);
    12. System.out.println("s1 = " + s1);

    } } ```

  • 3.isDone 使用轮询的方式 ```java FutureTask futureTask = new FutureTask<>(() -> {

    1. TimeUnit.SECONDS.sleep(2);
    2. return "123123";
    3. });
    4. new Thread(futureTask, "t1").start();
    5. while (true) { // 使用轮询策略 , 如果想要异步获取结果,通常都会以轮询的方式去获取结果,尽量不要阻塞
    6. if (futureTask.isDone()) {
    7. System.out.println(futureTask.get());
    8. break;
    9. }
    10. }
  1. > 如果想完成一些复杂的任务,
  2. > - 将两个异步计算合成一个异步计算,这两个异步计算互相独立,同时第二个又依赖第一个的结果。
  3. > - Future集合中某个任务最快结束时,返回结果。
  4. > - 等待Future结合中的所有任务都完成。
  5. <a name="GvNrZ"></a>
  6. ## 2.CompletableFuture和CompletionStage
  7. <a name="MZoMC"></a>
  8. ### 1.CompletableFuture和CompletionStage源码分别介绍
  9. - 类架构图说明
  10. ![image.png](https://cdn.nlark.com/yuque/0/2022/png/25783451/1641222491675-20a5e29f-f796-41aa-ae13-210d5de0cc2a.png#clientId=ucc18ef2e-aa4f-4&crop=0&crop=0&crop=1&crop=1&from=paste&height=279&id=u7ca8d9f1&margin=%5Bobject%20Object%5D&name=image.png&originHeight=558&originWidth=1166&originalType=binary&ratio=1&rotation=0&showTitle=false&size=28408&status=done&style=none&taskId=u28bf472a-4d5d-4010-9a6a-acecbe88634&title=&width=583)
  11. - 接口CompletionStage
  12. 代表异步计算过程中的某一个阶段,一个阶段完成以后可能会触发另外一个阶段,有些类似Linux系统的管道分隔符传参数。
  13. - CompletableFuture
  14. Java8中, CompletableFuture提供了非常强大的Future的扩展功能,可以帮助我们简化异步编程,并且提供了函数式编程的能力,可以通过一个回调的方式的处理计算,也提供转换和组合的能力CompletableFuture的方法<br />CompletableFuture实现了FutureCompletableStage
  15. <a name="Ff8ls"></a>
  16. ### 2.CompletableFuture 四个核心静态方法
  17. - 1.runAsync 无返回值
  18. ```java
  19. public static CompletableFuture<Void> runAsync(Runnable runnable)
  20. public static CompletableFuture<Void> runAsync(Runnable runnable,Executor executor)
  1. import java.util.concurrent.*;
  2. public class CompletableFuture_Demo_2 {
  3. final static ThreadPoolExecutor threadPollExecutor = new ThreadPoolExecutor(
  4. 2, 10, Integer.MAX_VALUE, TimeUnit.SECONDS,
  5. new LinkedBlockingQueue<>(), Executors.defaultThreadFactory(), new ThreadPoolExecutor.DiscardPolicy());
  6. public static void main(String[] args) {
  7. CompletableFuture<Void> voidCompletableFuture1 = CompletableFuture.runAsync(() -> {
  8. System.out.println(Thread.currentThread().getName() + "无参数无返回值=========");
  9. });
  10. CompletableFuture<Void> voidCompletableFuture2 = CompletableFuture.runAsync(() -> {
  11. System.out.println(Thread.currentThread().getName() + "自定义线程池====无参数无返回值=========");
  12. }, threadPollExecutor);
  13. threadPollExecutor.shutdown();
  14. }
  15. }
  • supplyAsync有返回值
    1. public static <U> CompletableFuture<U> supplyAsync(Supplier<U> supplier)
    2. public static <U> CompletableFuture<U> supplyAsync(Supplier<U> supplier,Executor executor)
    ```java

import java.util.concurrent.*;

public class CompletableFuture_Demo_applyAsync_2 {

  1. final static ThreadPoolExecutor threadPollExecutor = new ThreadPoolExecutor(
  2. 2, 10, Integer.MAX_VALUE, TimeUnit.SECONDS,
  3. new LinkedBlockingQueue<>(), Executors.defaultThreadFactory(), new ThreadPoolExecutor.DiscardPolicy());
  4. public static void main(String[] args) throws ExecutionException, InterruptedException {
  5. CompletableFuture<String> stringCompletableFuture1 = CompletableFuture.supplyAsync(() -> Thread.currentThread().getName() + "21323123123");
  6. CompletableFuture<String> stringCompletableFuture2 = CompletableFuture.supplyAsync(() -> Thread.currentThread().getName() + "21323123123", threadPollExecutor);
  7. // 获取异步结果
  8. String s1 = stringCompletableFuture1.get();
  9. System.out.println("s1 = " + s1);
  10. String s2 = stringCompletableFuture2.get();
  11. System.out.println("s2 = " + s2);
  12. threadPollExecutor.shutdown();
  13. }

}

  1. > 上述Executor executor参数说明
  2. > 没有指定Executor的方法,直接使用默认的ForkJoinPool.commonPool() 作为它的线程池执行异步代码。
  3. > 如果指定线程池,则使用我们自定义的或者特别指定的线程池执行异步代码
  4. > **join get 区别**
  5. > **区别: **
  6. > **----> join 不会抛出异常, **
  7. > **----> get 会抛出异常**
  8. > **相同:**
  9. > **----> 都会阻塞,等待结果的返回**
  10. <br />
  11. <a name="qvHVC"></a>
  12. ### 3.CompletableFuture常用方法
  13. - 1.获得结果和触发计算
  14. ```java
  15. import java.util.concurrent.CompletableFuture;
  16. import java.util.concurrent.ExecutionException;
  17. import java.util.concurrent.TimeUnit;
  18. import java.util.concurrent.TimeoutException;
  19. public class CompletableFutureDemo_3 {
  20. public static void main(String[] args) throws ExecutionException, InterruptedException, TimeoutException {
  21. CompletableFuture<String> stringCompletableFuture = CompletableFuture.supplyAsync(() -> {
  22. /* try {
  23. TimeUnit.SECONDS.sleep(3);
  24. } catch (InterruptedException e) {
  25. e.printStackTrace();
  26. }*/
  27. return Thread.currentThread().getName();
  28. });
  29. /**
  30. * 阻塞获取结果
  31. */
  32. // String s = stringCompletableFuture.get();
  33. /**
  34. * 阻塞获取结果 , 如果在2秒内没有获取到结果抛出异常
  35. */
  36. // String s = stringCompletableFuture.get(2, TimeUnit.SECONDS);
  37. /**
  38. * 立即获取结果 , 在没有完成计算的情况下立即给我一个结果
  39. */
  40. // String s = stringCompletableFuture.getNow("替代结果");
  41. /**
  42. * 阻塞获取结果 , 不抛出异常
  43. */
  44. // String s = stringCompletableFuture.join();
  45. /**
  46. *
  47. */
  48. TimeUnit.SECONDS.sleep(2);
  49. if (stringCompletableFuture.complete("获取阻塞的值")) {
  50. System.out.println("s = " + stringCompletableFuture.get());
  51. }
  52. }
  53. }
  • 2.对计算结果进行处理 ```java

public class CompletableFutureDemo_4 {

  1. public static void main(String[] args) throws ExecutionException, InterruptedException, TimeoutException {
  2. // completablefuture_thenApply();
  3. completablefuture_handle();
  4. }
  5. /**
  6. * handle 结果出现异常也会将值往下面传递 ,直到最后的 whenComplete
  7. */
  8. private static void completablefuture_handle() {
  9. CompletableFuture.supplyAsync(() -> {
  10. int num = 10 / 0;
  11. return Thread.currentThread().getName();
  12. }).handle((v, e) -> {
  13. if (e == null) {
  14. return v + "handle";
  15. } else {
  16. return v + "handle";
  17. }
  18. }).whenComplete((v, e) -> {
  19. if (e == null) {
  20. System.out.println(v + "顺利执行完成了");
  21. } else {
  22. System.out.println("抛出异常了, 请处理异常" + e.getMessage());
  23. }
  24. }).exceptionally(e -> "出现异常了");
  25. }
  26. /**
  27. * thenApply 出现异常直接到whenComplete , 不会接收异常
  28. */
  29. private static void completablefuture_thenApply() {
  30. CompletableFuture<String> stringCompletableFuture = CompletableFuture.supplyAsync(() -> {
  31. return Thread.currentThread().getName();
  32. }).thenApply((v) -> {
  33. return v + "thenApply";
  34. }).whenComplete((v, e) -> {
  35. if (e == null) {
  36. System.out.println(v + "顺利执行完成了");
  37. } else {
  38. System.out.println("抛出异常了, 请处理异常" + e.getMessage());
  39. }
  40. }).exceptionally(e -> {
  41. System.out.println(e.getMessage());
  42. return "出现异常了";
  43. });
  44. }

}

  1. - 3.对计算结果进行消费
  2. ```java
  3. import java.util.concurrent.CompletableFuture;
  4. /**
  5. *
  6. */
  7. public class CompletableFutureDemo_5 {
  8. public static void main(String[] args) {
  9. handle_thenApply();
  10. handle_thenAccept();
  11. handle_thenRun();
  12. }
  13. /**
  14. * 接收任务的处理 , 无返回结果:
  15. * 比如A处理完成之后 , 在执行B , B是不需要A的执行结果 , 后续也不需要将B的结果返回
  16. */
  17. private static void handle_thenRun() {
  18. CompletableFuture.supplyAsync(() ->
  19. Thread.currentThread().getName()).thenRun(() ->
  20. System.out.println("thenRun没有任何返回值===========")); // void
  21. }
  22. /**
  23. * 接收任务的处理结果,并消费处理,无返回结果:
  24. * 比如A处理完成之后 , 需要将结果交给B , 后续也不需要将B处理的结果返回
  25. */
  26. private static void handle_thenAccept() {
  27. CompletableFuture.supplyAsync(() -> Thread.currentThread().getName()).thenAccept((v) -> {
  28. System.out.println("v = " + v);
  29. }); // void
  30. }
  31. /**
  32. * 接收任务的处理结果,并消费处理,有返回结果:
  33. * 比如A处理完成之后 , 需要将结果交给B , 同时也需要将B的结果返回
  34. */
  35. private static void handle_thenApply() {
  36. CompletableFuture.supplyAsync(() -> Thread.currentThread().getName()).thenApply(v -> v);
  37. }
  38. }
  • 4.对计算速度进行选用 ```java

public class CompletableFutureDemo_6 {

  1. /**
  2. * @param args
  3. */
  4. public static void main(String[] args) throws ExecutionException, InterruptedException {
  5. CompletableFuture<String> stringCompletableFuture = CompletableFuture.supplyAsync(() -> {
  6. try {
  7. TimeUnit.SECONDS.sleep(3);
  8. } catch (InterruptedException e) {
  9. e.printStackTrace();
  10. }
  11. return Thread.currentThread().getName();
  12. }).applyToEither(
  13. CompletableFuture.supplyAsync(() -> {
  14. try {
  15. TimeUnit.SECONDS.sleep(1);
  16. } catch (InterruptedException e) {
  17. e.printStackTrace();
  18. }
  19. return Thread.currentThread().getName();
  20. }), (v) -> v
  21. );
  22. String s = stringCompletableFuture.get();
  23. System.out.println("s = " + s);
  24. }

}

  1. - 5.对计算结果进行合并(thenCombine)
  2. ```java
  3. import java.util.concurrent.CompletableFuture;
  4. import java.util.concurrent.ExecutionException;
  5. public class CompletableFutureDemo_7 {
  6. /**多个CompletionStage任务都完成后,最终能把两个任务的结果一起交给thenCombine 来处理
  7. * @param args
  8. */
  9. public static void main(String[] args) throws ExecutionException, InterruptedException {
  10. CompletableFuture<String> stringCompletableFuture = CompletableFuture.supplyAsync(() -> "AAAAA").thenCombine(
  11. CompletableFuture.supplyAsync(() -> "BBBBB").thenCombine(
  12. CompletableFuture.supplyAsync(() -> "CCCCCC"), (v1, v2) -> v1 + v2).thenCombine(
  13. CompletableFuture.supplyAsync(() -> "DDDDD"), (v1, v2) -> v1 + v2), (v1, v2) -> v1 + v2).thenCombine(
  14. CompletableFuture.supplyAsync(() -> "EEEEE"), (v1, v2) -> v1 + v2);
  15. System.out.println("stringCompletableFuture.get() = " + stringCompletableFuture.get());
  16. }
  17. }
  • 6.对计算结果进行合并并对类型进行转换(thenCompose) ```java import java.util.concurrent.CompletableFuture; import java.util.concurrent.ExecutionException;

public class CompletableFutureDemo_8 {

  1. public static void main(String[] args) throws ExecutionException, InterruptedException {
  2. CompletableFuture<Integer> integerCompletableFuture = CompletableFuture.supplyAsync(() -> "1").thenCompose(v -> CompletableFuture.supplyAsync(() ->
  3. String.valueOf(Integer.parseInt(v) + 123)
  4. ).thenCompose(v1 -> CompletableFuture.supplyAsync(() -> Integer.parseInt(v1) + 1)));
  5. System.out.println("integerCompletableFuture = " + integerCompletableFuture.get());
  6. }

}

  1. <a name="T38Dr"></a>
  2. # 2. JUC锁
  3. <a name="ispaM"></a>
  4. ## 1. 悲观锁
  5. 适合写操作多的场景,先加锁可以保证写操作时数据正确。<br />认为自己在使用数据的时候一定有别的线程来修改数据,因此在获取数据的时候会先加锁,确保数据不会被别的线程修改。 <br />synchronized 关键字和 Lock 的实现类都是悲观锁
  6. ```java
  7. import java.util.concurrent.atomic.AtomicInteger;
  8. import java.util.concurrent.locks.Lock;
  9. import java.util.concurrent.locks.ReentrantLock;
  10. public class LeBeiLock {
  11. private static Lock lock = new ReentrantLock(false);
  12. public static void main(String[] args) {
  13. // 悲观锁的实现方式
  14. synchronized (LeBeiLock.class) {
  15. }
  16. // 悲观锁的实现方式
  17. lock.lock();
  18. try {
  19. System.out.println("加锁成功");
  20. } finally {
  21. lock.unlock();
  22. }
  23. }
  24. }

2. 乐观锁

采用版本号机制 CAS(Compare-and-Swap,即比较并替换)算法实现

适合读操作多的场景,不加锁的特点能够使其读操作的性能大幅提升。

乐观锁认为自己在使用数据时不会有别的线程修改数据,所以不会添加锁,只是在更新数据的时候去判断之前有没有别的线程更新了这个数据。

如果这个数据没有被更新,当前线程将自己修改的数据成功写入。如果数据已经被其他线程更新,则根据不同的实现方式执行不同的操作

乐观锁在Java中是通过使用无锁编程来实现,最常采用的是CAS算法,Java原子类中的递增操作就通过CAS自旋实现的。

  1. // 乐观锁
  2. AtomicInteger atomicInteger = new AtomicInteger();
  3. atomicInteger.incrementAndGet();
  • 通过8种情况演示锁运行案例,看看我们到底锁的是什么 ```java

import java.util.concurrent.TimeUnit;

public class Lock8Demo_2 {

  1. /**
  2. * lock1() / lock2()
  3. * 当一个实例对象了里面有多个synchronized的实例方法时候 , 只能有一个线程去访问该方法, 意思就是在某个时间内 ,
  4. * 只能有持有锁的的对象才能方法, 其他方法要想获取锁就得等待释放锁才能获取
  5. * <p>
  6. * lock3():
  7. * hello是不带有锁, 不需要受到限制 , 直接打印
  8. * <p>
  9. * lock4():
  10. * 因为是有两个对象, 每个对象里面都会有一个实例化锁
  11. * <p>
  12. * lock5/lock6:
  13. * --> 对于普通方法而言, 锁的是当前实例对象,通常指的是this.具体的一部手机,所有的普通同步方法用的都是同一个对象, 也就是同一把锁
  14. * --> 对于静态方法而言, 锁的是当前类对象, 指向类锁Phone.class , 类锁在全局都是唯一 , 不会因为new的次数而增加
  15. * <p>
  16. * lock7/lock8
  17. * --> 类锁和对象锁是不同的 , 两者互不影响
  18. *
  19. * @param args
  20. */
  21. public static void main(String[] args) {
  22. // lock1();
  23. // lock2();
  24. // lock3();
  25. // lock4();
  26. // lock5();
  27. // lock6();
  28. // lock7();
  29. lock8();
  30. }
  31. /**
  32. * 标准访问 a,b两个线程 , 是先打电话还是先发邮件
  33. */
  34. public static void lock1() {
  35. Phone1 phone = new Phone1();
  36. new Thread(phone::sendEmail, "a").start();
  37. new Thread(phone::sendSms, "b").start();
  38. }
  39. /**
  40. * 标准访问 a,b两个线程 , a线程先沉睡3秒 是先打电话还是先发邮件
  41. */
  42. public static void lock2() {
  43. Phone1 phone = new Phone1();
  44. new Thread(() -> {
  45. phone.sendEmail();
  46. }, "a").start();
  47. new Thread(() -> {
  48. phone.sendSms();
  49. }, "b").start();
  50. }
  51. /**
  52. * 标准访问 a,b两个线程 , a线程先沉睡3秒 新增一个hello() method. 是先打印hello world 还是发送邮件
  53. */
  54. public static void lock3() {
  55. Phone1 phone = new Phone1();
  56. new Thread(phone::hello, "a").start();
  57. new Thread(phone::sendEmail, "b").start();
  58. }
  59. /**
  60. * 标准访问 a,b两个线程 , 有两部手机 , 是先打印发送短信还是发送邮件
  61. */
  62. public static void lock4() {
  63. Phone1 phone = new Phone1();
  64. Phone1 phone2 = new Phone1();
  65. new Thread(() -> {
  66. phone.sendEmail();
  67. }, "a").start();
  68. new Thread(() -> {
  69. phone2.sendSms();
  70. }, "b").start();
  71. }
  72. /**
  73. * 两个静态同步方法, 1部手机,是先打印发送短信还是打电话
  74. */
  75. public static void lock5() {
  76. new Thread(Phone1::sendEmail_, "a").start();
  77. new Thread(Phone1::sendSms_, "b").start();
  78. }
  79. /**
  80. * 两个静态同步方法, 2部手机,是先打印发送短信还是打电话
  81. */
  82. public static void lock6() {
  83. Phone1 phone1 = new Phone1();
  84. Phone1 phone2 = new Phone1();
  85. new Thread(() -> {
  86. phone1.sendEmail_();
  87. }, "a").start();
  88. new Thread(() -> {
  89. phone2.sendSms_();
  90. }, "b").start();
  91. }
  92. /**
  93. * 一个静态同步方法 一个普通同步方法, 1部手机,是先打印发送短信还是打电话
  94. */
  95. public static void lock7() {
  96. Phone1 phone = new Phone1();
  97. new Thread(() -> {
  98. phone.sendEmail_();
  99. }, "a").start();
  100. new Thread(() -> {
  101. phone.sendSms();
  102. }, "b").start();
  103. }
  104. /**
  105. * 一个静态同步方法 一个普通同步方法, 2部手机,是先打印发送短信还是打电话
  106. */
  107. public static void lock8() {
  108. Phone1 phone1 = new Phone1();
  109. Phone1 phone2 = new Phone1();
  110. new Thread(() -> {
  111. phone1.sendEmail_();
  112. }, "a").start();
  113. new Thread(() -> {
  114. phone2.sendSms();
  115. }, "b").start();
  116. }

}

class Phone1 {

  1. public synchronized void sendSms() {
  2. System.out.println(Thread.currentThread().getName() + "打电话===");
  3. }
  4. public synchronized void sendEmail() {
  5. try {
  6. TimeUnit.SECONDS.sleep(3);
  7. } catch (InterruptedException e) {
  8. e.printStackTrace();
  9. }
  10. System.out.println(Thread.currentThread().getName() + "发邮件===");
  11. }
  12. public synchronized static void sendEmail_() {
  13. try {
  14. TimeUnit.SECONDS.sleep(3);
  15. } catch (InterruptedException e) {
  16. e.printStackTrace();
  17. }
  18. System.out.println(Thread.currentThread().getName() + "发邮件===");
  19. }
  20. public synchronized static void sendSms_() {
  21. System.out.println(Thread.currentThread().getName() + "打电话===");
  22. }
  23. public void hello() {
  24. System.out.println("hello===");
  25. }

}

  1. - 从字节码角度分析synchronized实现
  2. > javap -c ***.class文件反编译
  3. > 假如你需要更多信息:
  4. > javap -v ***.class文件反编译
  5. > -v -verbose 输出附加信息(包括行号、本地变量表,反汇编等详细信息)
  6. - synchronized同步代码块
  7. ```java
  8. // javap -c ***.class文件反编译
  9. public class TestDemo {
  10. public static void main(String[] args) {
  11. Object o = new Object();
  12. synchronized (o) {
  13. System.out.println("o = " + o);
  14. }
  15. }
  16. }
  17. // 一定是一个enter两个exit吗?
  18. // 从下面可以看到 , 出现两次monitorexit退出 , 第一次是因为synchronized自己释放锁
  19. // 第二次确保能够正确释放在finally里面释放锁,由JVM控制

image.png

  • synchronized普通同步方法 ```java

public class TestDemo {

  1. public static void main(String[] args) {
  2. new TestDemo().test();
  3. }
  4. public synchronized void test() {
  5. Object o = new Object();
  6. System.out.println("o = " + o);
  7. }

}

// 调用指令将会检查方法的ACC_SYNCHRONIZED访问标志是否被设置。 // 如果设置了,执行线程会将先持有monitor然后再执行方法, // 最后在方法完成(无论是正常完成还是非正常完成)时释放 monitor

  1. ![image.png](https://cdn.nlark.com/yuque/0/2022/png/25783451/1641230127952-86fdabfb-e94a-43fc-8ef6-a3e2c8710166.png#clientId=ucc18ef2e-aa4f-4&crop=0&crop=0&crop=1&crop=1&from=paste&height=558&id=u3b64141a&margin=%5Bobject%20Object%5D&name=image.png&originHeight=1116&originWidth=2078&originalType=binary&ratio=1&rotation=0&showTitle=false&size=207594&status=done&style=none&taskId=udb0a8514-761d-42d0-8595-509ff7b79be&title=&width=1039)
  2. - synchronized静态同步方法
  3. ```java
  4. // ACC_STATIC, ACC_SYNCHRONIZED访问标志区分该方法是否静态同步方法
  5. public class TestDemo {
  6. public static void main(String[] args) {
  7. test();
  8. }
  9. public static synchronized void test() {
  10. Object o = new Object();
  11. System.out.println("o = " + o);
  12. }
  13. }

image.png

3. 公平锁和非公平锁

  1. // 非公平锁
  2. import java.util.concurrent.locks.Lock;
  3. import java.util.concurrent.locks.ReentrantLock;
  4. class Ticket {
  5. private int number = 20;
  6. private static Lock lock = new ReentrantLock(false);
  7. public void scalc() {
  8. lock.lock();
  9. try {
  10. if (number > 0) {
  11. System.out.println(Thread.currentThread().getName() + " 卖出第: \t " + (number--) + " \t 还剩下 :" + number);
  12. }
  13. } finally {
  14. lock.unlock();
  15. }
  16. }
  17. }
  18. public class FairAndNoFairDemo {
  19. public static void main(String[] args) {
  20. Ticket ticket = new Ticket();
  21. new Thread(() -> {
  22. for (int i = 0; i < 55; i++) {
  23. ticket.scalc();
  24. }
  25. }, "a").start();
  26. new Thread(() -> {
  27. for (int i = 0; i < 55; i++) {
  28. ticket.scalc();
  29. }
  30. }, "b").start();
  31. new Thread(() -> {
  32. for (int i = 0; i < 55; i++) {
  33. ticket.scalc();
  34. }
  35. }, "c").start();
  36. new Thread(() -> {
  37. for (int i = 0; i < 55; i++) {
  38. ticket.scalc();
  39. }
  40. }, "d").start();
  41. new Thread(() -> {
  42. for (int i = 0; i < 55; i++) {
  43. ticket.scalc();
  44. }
  45. }, "e").start();
  46. }
  47. }

image.png
这样抢票全部都是线程a去抢票了, 线程b,c,d,e线程没有机会抢到嫖

  1. // 公平锁
  2. class Ticket {
  3. private int number = 20;
  4. private static Lock lock = new ReentrantLock(true);
  5. public void scalc() {
  6. lock.lock();
  7. try {
  8. if (number > 0) {
  9. System.out.println(Thread.currentThread().getName() + " 卖出第: \t " + (number--) + " \t 还剩下 :" + number);
  10. }
  11. } finally {
  12. lock.unlock();
  13. }
  14. }
  15. }
  16. public class FairAndNoFairDemo {
  17. public static void main(String[] args) {
  18. Ticket ticket = new Ticket();
  19. new Thread(() -> {
  20. for (int i = 0; i < 55; i++) {
  21. ticket.scalc();
  22. }
  23. }, "a").start();
  24. new Thread(() -> {
  25. for (int i = 0; i < 55; i++) {
  26. ticket.scalc();
  27. }
  28. }, "b").start();
  29. new Thread(() -> {
  30. for (int i = 0; i < 55; i++) {
  31. ticket.scalc();
  32. }
  33. }, "c").start();
  34. new Thread(() -> {
  35. for (int i = 0; i < 55; i++) {
  36. ticket.scalc();
  37. }
  38. }, "d").start();
  39. new Thread(() -> {
  40. for (int i = 0; i < 55; i++) {
  41. ticket.scalc();
  42. }
  43. }, "e").start();
  44. }
  45. }

image.png

源码解读

  1. // hasQueuePredecessors 判断当前队列是否有线程在排队, 如果有就执行当前线程
  2. // 公平锁底层实现
  3. protected final boolean tryAcquire(int acquires) {
  4. final Thread current = Thread.currentThread();
  5. int c = getState();
  6. if (c == 0) {
  7. if (!hasQueuedPredecessors() &&
  8. compareAndSetState(0, acquires)) {
  9. setExclusiveOwnerThread(current);
  10. return true;
  11. }
  12. }
  13. else if (current == getExclusiveOwnerThread()) {
  14. int nextc = c + acquires;
  15. if (nextc < 0)
  16. throw new Error("Maximum lock count exceeded");
  17. setState(nextc);
  18. return true;
  19. }
  20. return false;
  21. }
  22. // 非公平锁底层实现
  23. final boolean nonfairTryAcquire(int acquires) {
  24. final Thread current = Thread.currentThread();
  25. int c = getState();
  26. if (c == 0) {
  27. if (compareAndSetState(0, acquires)) {
  28. setExclusiveOwnerThread(current);
  29. return true;
  30. }
  31. }
  32. else if (current == getExclusiveOwnerThread()) {
  33. int nextc = c + acquires;
  34. if (nextc < 0) // overflow
  35. throw new Error("Maximum lock count exceeded");
  36. setState(nextc);
  37. return true;
  38. }
  39. return false;
  40. }

为什么会有公平锁/非公平锁的设计为什么默认非公平? 1 恢复挂起的线程到真正锁的获取还是有时间差的,从开发人员来看这个时间微乎其微,但是从CPU的角度来看,这个时间差存在的还是很明显的。所以非公平锁能更充分的利用CPU 的时间片,尽量减少 CPU 空闲状态时间。 2 使用多线程很重要的考量点是线程切换的开销,当采用非公平锁时, 当1个线程请求锁获取同步状态,然后释放同步状态,因为不需要考虑是否还有前驱节点,所以刚释放锁的线程在此刻再次获取同步状态的概率就变得非常大,所以就减少了线程的开销 。 使⽤公平锁会有什么问题? 公平保证了线程之间排队的顺序性, 并且按照排队顺序去执行,而非公平锁则不需要理会管理排队的顺序性. 非公平锁会出现锁的饥饿性 什么时候用公平?什么时候用非公平? 非公平: 如果增加系统的吞吐量,建议使用非公平锁,这样就会减少很多线程之间的上下文切换 公平: 业务需求考虑,比如按照顺序去执行的流程

4. 可重入锁

隐式锁(即synchronized关键字使用的锁)默认是可重入锁

  1. public class RestartLock {
  2. public static void main(String[] args) throws Exception {
  3. // reset_synchronized();
  4. reset_synchronized_method();
  5. }
  6. /**
  7. * sync 可重入锁 , 方法
  8. */
  9. private static void reset_synchronized_method() {
  10. RestartLock restartLock = new RestartLock();
  11. restartLock.reset_1();
  12. }
  13. public synchronized void reset_1() {
  14. System.out.println("第一次进入");
  15. reset_2();
  16. }
  17. public synchronized void reset_2() {
  18. System.out.println("第二次进入");
  19. reset_3();
  20. }
  21. public synchronized void reset_3() {
  22. System.out.println("第三次进入");
  23. }
  24. /**
  25. * 可重入锁, 代码块
  26. */
  27. public static void reset_synchronized() {
  28. Object finalObject = new Object();
  29. new Thread(() -> {
  30. synchronized (finalObject) {
  31. System.out.println("进入一次锁方法");
  32. synchronized (finalObject) {
  33. System.out.println("进入二次锁方法");
  34. synchronized (finalObject) {
  35. System.out.println("进入三次锁方法");
  36. }
  37. }
  38. }
  39. }).start();
  40. }
  41. }

每个锁对象拥有一个锁计数器和一个指向持有该锁的线程的指针。

当执行monitorenter时,如果目标锁对象的计数器为零,那么说明它没有被其他线程所持有,Java虚拟机会将该锁对象的持有线程设置为当前线程,并且将其计数器加1。

在目标锁对象的计数器不为零的情况下,如果锁对象的持有线程是当前线程,那么 Java 虚拟机可以将其计数器加1,否则需要等待,直至持有线程释放该锁。

当执行monitorexit时,Java虚拟机则需将锁对象的计数器减1。计数器为零代表锁已被释放。

显式锁(即Lock)也有ReentrantLock这样的可重入锁。

  1. import java.util.concurrent.locks.ReentrantLock;
  2. public class ResetLock_Lock {
  3. public final static ReentrantLock lock = new ReentrantLock(false);
  4. public static void main(String[] args) {
  5. try {
  6. System.out.println("当前获取了几把锁: " + lock.getHoldCount());
  7. lock.lock();
  8. System.out.println("当前获取了几把锁: " + lock.getHoldCount());
  9. lock.lock();
  10. System.out.println("当前获取了几把锁: " + lock.getHoldCount());
  11. lock.lock();
  12. System.out.println("当前获取了几把锁: " + lock.getHoldCount());
  13. } finally {
  14. lock.unlock();
  15. System.out.println("释放锁之后还剩下几把锁: " + lock.getHoldCount());
  16. lock.unlock();
  17. System.out.println("释放锁之后还剩下几把锁: " + lock.getHoldCount());
  18. lock.unlock();
  19. System.out.println("释放锁之后还剩下几把锁: " + lock.getHoldCount());
  20. }
  21. }
  22. }

image.png

5. 死锁

  1. 死锁是什么

死锁是指两个或两个以上的线程在执行过程中,因争夺资源而造成的一种互相等待的现象,若无外力干涉那它们都将无法推进下去,如果系统资源充足,进程的资源请求都能够得到满足,死锁出现的可能性就很低,否则就会因争夺有限的资源而陷入死锁。

  1. 产生死锁主要原因
    1. 系统资源不足
    2. 进程运行推进的顺序不合适
    3. 资源分配不当
  2. 案例 ```java

import java.util.concurrent.TimeUnit; // 死锁案例 public class DeadLockDemo_Dead {

  1. public static void main(String[] args) {
  2. final Object finalA = new Object();
  3. final Object finalB = new Object();
  4. new Thread(() -> {
  5. synchronized (finalA) {
  6. try {
  7. TimeUnit.SECONDS.sleep(2);
  8. } catch (InterruptedException e) {
  9. e.printStackTrace();
  10. }
  11. System.out.println(Thread.currentThread().getName() + "====已经获取到A锁啦, 但是还想要获取B锁 ==========");
  12. synchronized (finalB) {
  13. System.out.println(Thread.currentThread().getName() + "====成功获取到B锁了");
  14. }
  15. }
  16. }, "FIRST_ONE").start();
  17. new Thread(() -> {
  18. synchronized (finalB) {
  19. try {
  20. TimeUnit.SECONDS.sleep(2);
  21. } catch (InterruptedException e) {
  22. e.printStackTrace();
  23. }
  24. System.out.println(Thread.currentThread().getName() + "====已经获取到B锁啦, 但是还是想要获取到A锁 ==========");
  25. synchronized (finalA) {
  26. System.out.println(Thread.currentThread().getName() + "====成功获取到A锁了");
  27. }
  28. }
  29. }, "SECOND_TWO").start();
  30. }

}

  1. 4. 如何排查死锁
  2. 1. jps -l && jstack 进程编号
  3. ![image.png](https://cdn.nlark.com/yuque/0/2022/png/25783451/1641299803459-999af689-f5ae-4f48-b655-8cccd4dc4840.png#clientId=u5ba7769f-1952-4&crop=0&crop=0&crop=1&crop=1&from=paste&height=157&id=u539bd9f2&margin=%5Bobject%20Object%5D&name=image.png&originHeight=314&originWidth=966&originalType=binary&ratio=1&rotation=0&showTitle=false&size=51340&status=done&style=none&taskId=u606948b4-8dde-45a9-9310-fc7691ad63c&title=&width=483)<br />![image.png](https://cdn.nlark.com/yuque/0/2022/png/25783451/1641299851102-e2decdf2-0ea6-4589-a809-0ab4b1ba744b.png#clientId=u5ba7769f-1952-4&crop=0&crop=0&crop=1&crop=1&from=paste&height=541&id=u0b8c8d0f&margin=%5Bobject%20Object%5D&name=image.png&originHeight=1082&originWidth=1732&originalType=binary&ratio=1&rotation=0&showTitle=false&size=205567&status=done&style=none&taskId=u476c73d1-219b-4cf4-ba8c-a949bf7d672&title=&width=866)
  4. 2. jconsole
  5. <a name="xpi4f"></a>
  6. # 3. **LockSupport与线程中断**
  7. <a name="Tac5W"></a>
  8. ## 1.线程中断
  9. 1. 什么是中断?
  10. **首先** <br />一个线程不应该由其他线程来强制中断或停止,而是应该由线程自己自行停止。 <br />所以,Thread.stop, Thread.suspend, Thread.resume 都已经被废弃了。 <br /> <br />**其次 **<br />在Java中没有办法立即停止一条线程,然而停止线程却显得尤为重要,如取消一个耗时操作。 <br />因此,Java提供了一种用于停止线程的机制——中断。 <br /> <br />中断只是一种协作机制,Java没有给中断增加任何语法,中断的过程完全需要程序员自己实现。 <br />若要中断一个线程,你需要手动调用该线程的interrupt方法,该方法也仅仅是将线程对象的中断标识设成true <br />接着你需要自己写代码不断地检测当前线程的标识位,如果为true,表示别的线程要求这条线程中断, <br />此时究竟该做什么需要你自己写代码实现。 <br /> <br />每个线程对象中都有一个标识,用于表示线程是否被中断;该标识位为true表示中断,为false表示未中断; <br />通过调用线程对象的interrupt方法将该线程的标识位设为true;可以在别的线程中调用,也可以在自己的线程中调用。
  11. 2. 中断的相关API方法
  12. | public void interrupt() | 实例方法,<br />实例方法interrupt()仅仅是设置线程的中断状态为true,不会停止线程 |
  13. | --- | --- |
  14. | **public static boolean interrupted() ** | 静态方法,Thread.interrupted(); <br />**判断线程是否被中断,并清除当前中断状态 **<br />**这个方法做了两件事: **<br />**1 返回当前线程的中断状态 **<br />**2 将当前线程的中断状态设为false **<br />** **<br />**这个方法有点不好理解,因为连续调用两次的结果可能不一样。** |
  15. | public boolean isInterrupted() | 实例方法,<br />判断当前线程是否被中断(通过检查中断标志位) |
  16. 3. 如何使用中断标识停止线程?
  17. 1. volatile变量实现
  18. ```java
  19. import java.util.concurrent.TimeUnit;
  20. public class VolatileDemo {
  21. private static volatile boolean isStop = false;
  22. public static void main(String[] args) throws InterruptedException {
  23. new Thread(() -> {
  24. while (true) {
  25. if (isStop) {
  26. System.out.println(Thread.currentThread().getName() + "当isStop为true的时候已经退出了");
  27. break;
  28. }
  29. System.out.println("-------hello interrupt");
  30. }
  31. }).start();
  32. TimeUnit.SECONDS.sleep(3);
  33. new Thread(() -> {
  34. isStop = true;
  35. }).start();
  36. }
  37. }
  1. AtomicBoolean实现 ```java public class AtomicBooleanDemo {

    private static AtomicBoolean atomicBoolean = new AtomicBoolean(false);

    public static void main(String[] args) throws InterruptedException { new Thread(() -> {

    1. while (true) {
    2. if (atomicBoolean.get()) {
    3. System.out.println(Thread.currentThread().getName() + "=====当前的atomicBoolean值已经为true了,准备退出");
    4. break;
    5. }
    6. System.out.println("AtomicBoolean=======");
    7. }

    }).start();

    TimeUnit.SECONDS.sleep(3); new Thread(() -> {

    1. atomicBoolean.set(true);

    }).start(); } }

  1. 3. Thread类自带的中断api方法实现
  2. ```java
  3. import java.util.concurrent.TimeUnit;
  4. public class InterrupDemo_Ed {
  5. public static void main(String[] args) throws InterruptedException {
  6. Thread a = new Thread(() -> {
  7. while (true) {
  8. if (Thread.currentThread().isInterrupted()) {
  9. System.out.println(Thread.currentThread().getName() + "当前线程是已经终止了=============");
  10. break;
  11. }
  12. System.out.println("running==============");
  13. }
  14. }, "A");
  15. a.start();
  16. TimeUnit.SECONDS.sleep(3);
  17. new Thread(a::interrupt, "B").start();
  18. }
  19. }
  1. 1. 实例方法interrupt(),没有返回值

image.png

  1. 2. 实例方法isInterrupted,返回布尔值

image.png

  1. 当前线程的中断标识为true,是不是就立刻停止?
    1. 具体来说,当对一个线程,调用 interrupt() 时:
      1. 如果线程处于正常活动状态,那么会将该线程的中断标志设置为 true,仅此而已。

被设置中断标志的线程将继续正常运行,不受影响。所以, interrupt() 并不能真正的中断线程,需要被调用的线程自己进行配合才行。

  1. 2. 如果线程处于被阻塞状态(例如处于sleep, wait, join 等状态),在别的线程中调用当前线程对象的interrupt方法, 那么线程将立即退出被阻塞状态,并抛出一个InterruptedException异常。
  1. 案例1 ```java import java.util.concurrent.TimeUnit;

public class InterruptDemo_ {

  1. public static void main(String[] args) throws InterruptedException {
  2. Thread t1 = new Thread(
  3. () -> {
  4. for (int i = 0; i <= 300; i++) {
  5. System.out.println(Thread.currentThread().getName() + "执行到的数字是: " + i);
  6. }
  7. // 还处于活动状态
  8. System.out.println("中断状态后的第二次查询======" + Thread.currentThread().isInterrupted());
  9. }
  10. );
  11. t1.start();
  12. System.out.println("暂停前查询一次状态=========" + t1.isInterrupted()); // false
  13. // interrupt 仅仅只是把中断状态设置为true,并不会中断线程
  14. t1.interrupt();
  15. //活动状态现在还在执行
  16. System.out.println("中断状态后的第一次查询======" + t1.isInterrupted());
  17. TimeUnit.SECONDS.sleep(3);
  18. // 非活动状态,表示t1线程不在执行中,已经结束执行
  19. System.out.println("中断状态后的第三次查询======" + t1.isInterrupted());
  20. }

}

  1. 3. 案例2
  2. ```java
  3. import java.util.concurrent.TimeUnit;
  4. public class Interrupt_LockDemo {
  5. public static void main(String[] args) throws InterruptedException {
  6. Thread t1 = new Thread(() -> {
  7. while (true) {
  8. if (Thread.currentThread().isInterrupted()) {
  9. System.out.println(Thread.currentThread().getName() + "=========已经被中断了");
  10. break;
  11. }
  12. try {
  13. Thread.sleep(200);
  14. } catch (InterruptedException e) {
  15. Thread.currentThread().interrupt();
  16. /**
  17. * sleep方法抛出InterruptedException,中断标识也会被清空,
  18. * 没有通过 在catch里面使用 Thread.currentThread().interrupt() 将状态复位
  19. */
  20. e.printStackTrace();
  21. }
  22. System.out.println("Hello=========");
  23. }
  24. }, "t1");
  25. t1.start();
  26. TimeUnit.SECONDS.sleep(1);
  27. t1.interrupt(); // 将中断状态设置为 true
  28. }
  29. }
  30. /**
  31. * Causes the currently executing thread to sleep (temporarily cease
  32. * execution) for the specified number of milliseconds, subject to
  33. * the precision and accuracy of system timers and schedulers. The thread
  34. * does not lose ownership of any monitors.
  35. *
  36. * @param millis
  37. * the length of time to sleep in milliseconds
  38. *
  39. * @throws IllegalArgumentException
  40. * if the value of {@code millis} is negative
  41. *
  42. * @throws InterruptedException
  43. * if any thread has interrupted the current thread. The
  44. * <i>interrupted status</i> of the current thread is
  45. * cleared when this exception is thrown.
  46. */
  47. // Thread.sleep的时候 , 会抛出InterruptedException异常, 会将中断线程里面的中断线程标记清理掉,将true-->false,
  48. // 这个时候我们需要在catch里面将中断标记进行复位
  49. public static native void sleep(long millis) throws InterruptedException;
  1. 静态方法Thread.interrupted()
    1. 静态方法Thread.interrupted() ```java public class Interrupted_Demo_Lock { public static void main(String[] args) { System.out.println(Thread.currentThread().getName() + “—-“ + Thread.interrupted()); System.out.println(Thread.currentThread().getName() + “—-“ + Thread.interrupted()); System.out.println(“111111”); Thread.currentThread().interrupt(); System.out.println(“222222”); System.out.println(Thread.currentThread().getName() + “—-“ + Thread.interrupted()); System.out.println(Thread.currentThread().getName() + “—-“ + Thread.interrupted()); } }
  1. 2. 都会返回中断状态,两者对比
  2. ```java
  3. public static boolean interrupted() {
  4. return currentThread().isInterrupted(true);
  5. } // 静态方法 , 设置为true 就是为了将中断之后自动复位
  6. public boolean isInterrupted() {
  7. return isInterrupted(false); // 实例方法 , false, 不会复位,也就是清除标志位
  8. }
  1. 结论 ```java 方法的注释也清晰的表达了“中断状态将会根据传入的ClearInterrupted参数值确定是否重置”。

所以, 静态方法interrupted将 会清除中断状态(传入的参数ClearInterrupted为true),

实例方法isInterrupted则不会(传入的参数ClearInterrupted为false)。

  1. 5. 总结
  2. **interrupt()方法是一个实例方法 **<br />它通知目标线程中断,也就是设置目标线程的中断标志位为true,中断标志位表示当前线程已经被中断了。
  3. **isInterrupted()方法也是一个实例方法 **<br />它判断当前线程是否被中断(通过检查中断标志位)并获取中断标志
  4. **Thread类的静态方法interrupted() **<br />返回当前线程的中断状态(boolean类型)且将当前线程的中断状态设为false,此方法调用之后会清除当前线程的中断标志位的状态(将中断标志置为false了),返回当前值并清零置false
  5. <a name="Kg97g"></a>
  6. ## 2.LockSupport是什么
  7. 1. LockSupport是用来创建锁和其他同步类的基本线程阻塞原语, LockSupport中的park() unpark() 的作用分别是阻塞线程和解除阻塞线程
  8. 1. 线程等待和唤醒的方法
  9. 1. 方式1:使用Object中的wait()方法让线程等待,使用Object中的notify()方法唤醒线程
  10. 1. 案例1
  11. ```java
  12. import java.util.concurrent.TimeUnit;
  13. public class Object_wait_notify {
  14. public static void main(String[] args) throws InterruptedException {
  15. Object objectLock = new Object();
  16. new Thread(() -> {
  17. synchronized (objectLock) {
  18. try {
  19. System.out.println(Thread.currentThread().getName() + "========" + "come in====");
  20. objectLock.wait();
  21. } catch (InterruptedException e) {
  22. e.printStackTrace();
  23. }
  24. }
  25. System.out.println(Thread.currentThread().getName() + "线程被唤醒了\t");
  26. }, "t1").start();
  27. TimeUnit.SECONDS.sleep(3);
  28. new Thread(() -> {
  29. synchronized (objectLock) {
  30. objectLock.notify();
  31. System.out.println("发出通知了");
  32. }
  33. }, "t2").start();
  34. }
  35. }
  1. 2. (异常)案例2
  1. import java.util.concurrent.TimeUnit;
  2. public class Object_wait_notify {
  3. public static void main(String[] args) throws InterruptedException {
  4. Object objectLock = new Object();
  5. new Thread(() -> {
  6. try {
  7. TimeUnit.SECONDS.sleep(3);
  8. } catch (InterruptedException e) {
  9. e.printStackTrace();
  10. }
  11. /**
  12. * 此时就会一直形成阻塞状态, 因为objectLock已经发出过了通知, 这个时候t1还在睡眠, 等待t1睡眠完成, 通知已经发过了
  13. * 就会一直阻塞
  14. */
  15. synchronized (objectLock) {
  16. try {
  17. System.out.println(Thread.currentThread().getName() + "========" + "come in====");
  18. objectLock.wait();
  19. } catch (InterruptedException e) {
  20. e.printStackTrace();
  21. }
  22. }
  23. System.out.println(Thread.currentThread().getName() + "线程被唤醒了\t");
  24. }, "t1").start();
  25. new Thread(() -> {
  26. synchronized (objectLock) {
  27. objectLock.notify();
  28. System.out.println("发出通知了");
  29. }
  30. }, "t2").start();
  31. }
  32. }
  1. 3. (异常)案例3
  1. public class Object_wait_notify {
  2. public static void main(String[] args) throws InterruptedException {
  3. Object objectLock = new Object();
  4. new Thread(() -> {
  5. synchronized (objectLock) {
  6. objectLock.notify();
  7. System.out.println("发出通知了");
  8. }
  9. }, "t2").start();
  10. new Thread(() -> {
  11. /**
  12. * 此时就会一直形成阻塞状态, 因为objectLock notify在上面就已经发过通知了
  13. *
  14. */
  15. synchronized (objectLock) {
  16. try {
  17. System.out.println(Thread.currentThread().getName() + "========" + "come in====");
  18. objectLock.wait();
  19. } catch (InterruptedException e) {
  20. e.printStackTrace();
  21. }
  22. }
  23. System.out.println(Thread.currentThread().getName() + "线程被唤醒了\t");
  24. }, "t1").start();
  25. }
  26. }
  1. 4. 总结:
  2. 1. waitnotify方法必须要在同步块或者方法里面,且成对出现使用
  3. 1. waitnotifyOK
  1. 方式2:使用JUC包中Condition的await()方法让线程等待,使用signal()方法唤醒线程
    1. 案例1 ```java

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

public class Lock_await_signal {

  1. static ReentrantLock reentrantLock = new ReentrantLock();
  2. static Condition condition = reentrantLock.newCondition();
  3. public static void main(String[] args) {
  4. new Thread(() -> {
  5. reentrantLock.lock();
  6. try {
  7. condition.await();
  8. System.out.println(Thread.currentThread().getName() + "已经被唤醒了===========");
  9. } catch (InterruptedException e) {
  10. e.printStackTrace();
  11. } finally {
  12. reentrantLock.unlock();
  13. }
  14. }, "lock_1").start();
  15. new Thread(() -> {
  16. reentrantLock.lock();
  17. try {
  18. condition.signal();
  19. System.out.println(Thread.currentThread().getName() + "发出通知了==========");
  20. } finally {
  21. reentrantLock.unlock();
  22. }
  23. }, "lock_2").start();
  24. }

}

  1. 2. 案例2(异常)
  2. ```java
  3. import java.util.concurrent.TimeUnit;
  4. import java.util.concurrent.locks.Condition;
  5. import java.util.concurrent.locks.ReentrantLock;
  6. public class Lock_await_signal {
  7. static ReentrantLock reentrantLock = new ReentrantLock();
  8. static Condition condition = reentrantLock.newCondition();
  9. public static void main(String[] args) {
  10. new Thread(() -> {
  11. try {
  12. TimeUnit.SECONDS.sleep(3);
  13. } catch (InterruptedException e) {
  14. e.printStackTrace();
  15. }
  16. /**
  17. * lock_1线程先沉睡3秒 , 让lock_2线程发出通知, 结果就会造成阻塞
  18. */
  19. reentrantLock.lock();
  20. try {
  21. System.out.println("come in================");
  22. condition.await();
  23. System.out.println(Thread.currentThread().getName() + "已经被唤醒了===========");
  24. } catch (InterruptedException e) {
  25. e.printStackTrace();
  26. } finally {
  27. reentrantLock.unlock();
  28. }
  29. }, "lock_1").start();
  30. new Thread(() -> {
  31. reentrantLock.lock();
  32. try {
  33. condition.signal();
  34. System.out.println(Thread.currentThread().getName() + "发出通知了==========");
  35. } finally {
  36. reentrantLock.unlock();
  37. }
  38. }, "lock_2").start();
  39. }
  40. }
  1. 3. 案例3(异常)
  1. import java.util.concurrent.locks.Condition;
  2. import java.util.concurrent.locks.ReentrantLock;
  3. public class Lock_await_signal {
  4. static ReentrantLock reentrantLock = new ReentrantLock();
  5. static Condition condition = reentrantLock.newCondition();
  6. public static void main(String[] args) {
  7. new Thread(() -> {
  8. reentrantLock.lock();
  9. try {
  10. condition.signal();
  11. System.out.println(Thread.currentThread().getName() + "发出通知了==========");
  12. } finally {
  13. reentrantLock.unlock();
  14. }
  15. }, "lock_2").start();
  16. new Thread(() -> {
  17. /**
  18. * 线才能lock_2先发出通知, 之后lock_1进入等待, 结果就会造成阻塞
  19. */
  20. reentrantLock.lock();
  21. try {
  22. System.out.println("come in================");
  23. condition.await();
  24. System.out.println(Thread.currentThread().getName() + "已经被唤醒了===========");
  25. } catch (InterruptedException e) {
  26. e.printStackTrace();
  27. } finally {
  28. reentrantLock.unlock();
  29. }
  30. }, "lock_1").start();
  31. }
  32. }
  1. 4. 总结:
  2. 1. Condtion中的线程等待和唤醒方法之前,需要先获取锁
  3. 1. 一定要先awaitsignal,不要反了
  1. 方式3:LockSupport类可以阻塞当前线程以及唤醒指定被阻塞的线程
    1. Object和Condition使用的限制条件
  2. 线程先要获得并持有锁,必须在锁块(synchronized或lock)中
  3. 必须要先等待后唤醒,线程才能够被唤醒
    1. LockSupport类中的park等待和unpark唤醒
  4. 是什么
    1. 通过park()和unpark(thread)方法来实现阻塞和唤醒线程的操作
    2. LockSupport类使用了一种名为Permit(许可)的概念来做到阻塞和唤醒线程的功能,每个线程都有一个许可(permit), permit只有两个值1和零,默认是零。 可以把许可看成是一种(0,1)信号量(Semaphore),但与 Semaphore 不同的是,许可的累加上限是1。
  5. 主要方法
    1. park() /park(Object blocker)
    2. 唤醒处于阻塞状态的指定线程
  6. 案例 ```java import java.util.concurrent.TimeUnit; import java.util.concurrent.locks.LockSupport;

public class LockSupper_park_no_park {

  1. public static void main(String[] args) {
  2. Thread t1 = new Thread(() -> {
  3. try {
  4. TimeUnit.SECONDS.sleep(4);
  5. } catch (InterruptedException e) {
  6. e.printStackTrace();
  7. }
  8. System.out.println("lock park in");
  9. LockSupport.park();
  10. System.out.println("lock park out");
  11. });
  12. t1.start();
  13. new Thread(() -> {
  14. System.out.println("unpark park in");
  15. LockSupport.unpark(t1);
  16. System.out.println("unpark park out");
  17. }).start();
  18. }

}

  1. <a name="jlUv8"></a>
  2. # 4. CAS
  3. <a name="Yq1bJ"></a>
  4. ## 1. 没有CAS之前
  5. - 1. 多线程环境不使用原子类保证线程安全(基本数据类型):
  6. - 案例
  7. ```java
  8. import java.util.concurrent.BrokenBarrierException;
  9. import java.util.concurrent.CountDownLatch;
  10. import java.util.concurrent.CyclicBarrier;
  11. class Volatile_Demo {
  12. volatile int number = 0;
  13. public int getNumber() {
  14. return number;
  15. }
  16. public synchronized void setNumber() {
  17. number++;
  18. }
  19. }
  20. public class Volatile_Synchronized_Demo {
  21. public static void main(String[] args) throws InterruptedException, BrokenBarrierException {
  22. CyclicBarrier cyclicBarrier = new CyclicBarrier(50);
  23. CountDownLatch countDownLatch = new CountDownLatch(50);
  24. Volatile_Demo volatile_demo = new Volatile_Demo();
  25. for (int i = 1; i <= 50; i++) { // 50 * 10
  26. cyclicBarrier.await();
  27. new Thread(
  28. () -> {
  29. try {
  30. for (int j = 0; j < 10; j++) {
  31. volatile_demo.setNumber();
  32. }
  33. } finally {
  34. countDownLatch.countDown();
  35. }
  36. }
  37. ).start();
  38. }
  39. countDownLatch.await();
  40. int number = volatile_demo.getNumber();
  41. System.out.println("number = " + number);
  42. }
  43. }
    1. 多线程环境 使用原子类保证线程安全(基本数据类型)
      • 案例 ```java import java.util.concurrent.CountDownLatch; import java.util.concurrent.atomic.AtomicInteger;

public class AtomicIntegerDemo {

  1. final static AtomicInteger A = new AtomicInteger();
  2. public static void main(String[] args) throws InterruptedException {
  3. CountDownLatch countDownLatch = new CountDownLatch(50);
  4. for (int i = 0; i < 50; i++) {
  5. new Thread(
  6. () -> {
  7. try {
  8. for (int j = 0; j < 50; j++) {
  9. A.incrementAndGet();
  10. }
  11. } finally {
  12. countDownLatch.countDown();
  13. }
  14. }
  15. ).start();
  16. }
  17. countDownLatch.await();
  18. System.out.println(A.get());
  19. }

}

  1. <a name="q5gHy"></a>
  2. ## 2. CAS是什么
  3. - 1. 说明
  4. compare and swap的缩写,中文翻译成比较并交换,实现并发算法时常用到的一种技术。它包含三个操作数——内存位置、预期原值及更新值。 <br />执行CAS操作的时候,将内存位置的值与预期原值比较: <br />如果相匹配,那么处理器会自动将该位置值更新为新值, <br />如果不匹配,处理器不做任何操作,多个线程同时执行CAS操作只有一个会成功。
  5. - 2. 原理
  6. CAS有三个操作数, 内存值V, 预期值A,更新值B,仅仅只有内存值V和旧值预期值A相等, 才会将更新值B,否则什么都不处理<br />![image.png](https://cdn.nlark.com/yuque/0/2022/png/25783451/1641387261505-102b4550-1f4c-4d90-9eef-46011bd77a8d.png#clientId=u5ba7769f-1952-4&crop=0&crop=0&crop=1&crop=1&from=paste&height=267&id=u41c9e53f&margin=%5Bobject%20Object%5D&name=image.png&originHeight=534&originWidth=1232&originalType=binary&ratio=1&rotation=0&showTitle=false&size=50836&status=done&style=none&taskId=u6bfe325d-610d-4050-af0b-20af78772ae&title=&width=616)
  7. - 3.案例
  8. ```java
  9. import java.util.concurrent.atomic.AtomicInteger;
  10. public class CASDemo {
  11. public static void main(String[] args) {
  12. AtomicInteger atomicInteger = new AtomicInteger(5);
  13. System.out.println(atomicInteger.compareAndSet(5, 2021) + "\t" + atomicInteger.get()); // true 2021
  14. System.out.println(atomicInteger.compareAndSet(5, 1024) + "\t" + atomicInteger.get()); // false 2021
  15. }
  16. }

3.CAS底层原理?如果知道,谈谈你对UnSafe的理解

  • 1.UnSafe
    • 是CAS的核心类,由于Java方法无法直接访问底层系统,需要通过本地(native)方法来访问,Unsafe相当于一个后门,基于该类可以直接操作特定内存的数据。Unsafe类存在于sun.misc包中,其内部方法操作可以像C的指针一样直接操作内存,因为Java中CAS操作的执行依赖于Unsafe类的方法。

注意Unsafe类中的所有方法都是native修饰的,也就是说Unsafe类中的方法都直接调用操作系统底层资源执行相应任务

  • 变量valueOffset,表示该变量值在内存中的偏移地址,因为Unsafe就是根据内存偏移地址获取数据的。
  • 变量value用volatile修饰,保证了多线程之间的内存可见性。
    • 2.源码分析

new AtomicInteger().getAndIncrement();
image.png
假设A,B两个线程去同时去执行getAndIncrement()方法,底层才有getAndAddInt()方法.

1 AtomicInteger里面的value原始值为3,即主内存中AtomicInteger的value为3,根据JMM模型,线程A和线程B各自持有一份值为3的value的副本分别到各自的工作内存。
2 A线程通过getIntVolatile获取到数据之后被挂起
3 之后B线程也通过getIntVolatile方法获取到value值3,此时线程没有被挂起 , 并且执行了compareAndSwapInt之后将数据更新到4了
4 此时线程A恢复过来了, 执行compareAndSwapInt方法发现自己携带的值和原主内存中的值不一样了 , 变成4了,此时就会更新失败,只能重新再次读取一次,然后更新
5 A线程又重新获取了一次, 因为变量被volatile修饰 , 所以变量是对多个线程是可见的,也就是对A是可见的,
此时A线程再次执行compareAndSwapInt就会将4更新为5

4.自旋锁,借鉴CAS思想

  • 1.是什么

是指尝试获取锁的线程不会立即阻塞,而是采用循环的方式去尝试获取锁,
当线程发现锁被占用时,会不断循环判断锁的状态,直到获取。这样的好处是减少线程上下文切换的消耗,缺点是循环会消耗CPU

  • 2.案例 ```java public class SpinLockDemo {

    AtomicReference threadAtomicReference = new AtomicReference<>();

    public void myLock() {

    1. Thread thread = Thread.currentThread();
    2. System.out.println(thread.getName() + "\t" + "lock");
    3. while (!threadAtomicReference.compareAndSet(null, thread)) { // 当第一个线程进入的时候回获取成功
    4. }

    }

    public void myUnLock() {

    1. Thread thread = Thread.currentThread();
    2. System.out.println(thread.getName() + "\t" + "unlock");
    3. threadAtomicReference.compareAndSet(thread, null);

    }

  1. public static void main(String[] args) {
  2. SpinLockDemo spinLockDemo = new SpinLockDemo();
  3. new Thread(() -> {
  4. spinLockDemo.myLock();
  5. try {
  6. Thread.sleep(3000);
  7. } catch (InterruptedException e) {
  8. e.printStackTrace();
  9. }
  10. spinLockDemo.myUnLock();
  11. }, "A").start();
  12. try {
  13. Thread.sleep(3);
  14. } catch (InterruptedException e) {
  15. e.printStackTrace();
  16. }
  17. new Thread(() -> {
  18. spinLockDemo.myLock();
  19. spinLockDemo.myUnLock();
  20. }, "B").start();
  21. }

}

  1. <a name="N9pK6"></a>
  2. ## 5.CAS缺点
  3. - 1. ABA问题
  4. 就是当一个线程A从内存中取出内存值V时,比如取出的值是"LL",这个时候B线程也从内存中取出值"LL",<br />但是这个时候B线程把内存中的V值修改成"KK"了,之后A线程又把值修改为"LL",这个时候虽然A线程操作是成功的, **尽管线程one的CAS操作成功,但是不代表这个过程就是没有问题的。 **
  5. - 版本号时间戳原子引用 - AtomicStampedReference
  6. - 案例
  7. ```java
  8. public class ABADemo {
  9. static AtomicInteger atomicInteger = new AtomicInteger(100);
  10. public static void main(String[] args) {
  11. new Thread(() -> {
  12. atomicInteger.compareAndSet(100, 101);
  13. atomicInteger.compareAndSet(101, 100);
  14. }).start();
  15. new Thread(() -> {
  16. try {
  17. TimeUnit.SECONDS.sleep(1);
  18. } catch (InterruptedException e) {
  19. e.printStackTrace();
  20. }
  21. System.out.println(atomicInteger.compareAndSet(100, 2019) + "\t" + atomicInteger.get());
  22. }).start();
  23. }
  24. }
  • 解决案例 ```java

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

public class ABAStampedDemo {

  1. static AtomicStampedReference<Integer> atomicStampedReference
  2. = new AtomicStampedReference<>(100, 1);
  3. public static void main(String[] args) throws InterruptedException {
  4. new Thread(() -> {
  5. int stamp = atomicStampedReference.getStamp();
  6. try {
  7. TimeUnit.SECONDS.sleep(1);
  8. } catch (InterruptedException e) {
  9. e.printStackTrace();
  10. }
  11. System.out.println(Thread.currentThread().getName() + "\t" + "首次获取版本号" + stamp);
  12. atomicStampedReference.compareAndSet(100, 101,
  13. stamp, stamp + 1);
  14. System.out.println(Thread.currentThread().getName() + "\t" + "第二次获取版本号" + atomicStampedReference.getStamp());
  15. atomicStampedReference.compareAndSet(101, 100,
  16. atomicStampedReference.getStamp(), atomicStampedReference.getStamp() + 1);
  17. System.out.println(Thread.currentThread().getName() + "\t" + "第三次获取版本号" + atomicStampedReference.getStamp());
  18. }).start();
  19. new Thread(() -> {
  20. int stamp = atomicStampedReference.getStamp();
  21. System.out.println(Thread.currentThread().getName() + "\t" + "首次获取版本号" + stamp);
  22. try {
  23. TimeUnit.SECONDS.sleep(3);
  24. } catch (InterruptedException e) {
  25. e.printStackTrace();
  26. }
  27. boolean result = atomicStampedReference.compareAndSet(100, 1002,
  28. stamp, stamp + 1);
  29. System.out.println(Thread.currentThread().getName() + "\t" + "第二次获取版本号" + atomicStampedReference.getStamp());
  30. System.out.println("执行状态: " + result);
  31. }).start();
  32. }

}

  1. ![image.png](https://cdn.nlark.com/yuque/0/2022/png/25783451/1641392627307-c7a9bab8-8e7b-4670-ad85-45a3ec394bf0.png#clientId=u5ba7769f-1952-4&crop=0&crop=0&crop=1&crop=1&from=paste&height=152&id=ueb28c10b&margin=%5Bobject%20Object%5D&name=image.png&originHeight=304&originWidth=1594&originalType=binary&ratio=1&rotation=0&showTitle=false&size=64706&status=done&style=none&taskId=u6456f786-31d4-4cb9-9572-60c058ce2bd&title=&width=797)
  2. - 扩展:AtomicMarkableReference
  3. <a name="YZaNS"></a>
  4. # 5.原子类操作
  5. <a name="rsoQl"></a>
  6. ## 1.基本类型原子类
  7. - AtomicInteger
  8. ```java
  9. import java.util.concurrent.CountDownLatch;
  10. import java.util.concurrent.atomic.AtomicInteger;
  11. class MyNumber {
  12. private AtomicInteger atomicInteger = new AtomicInteger();
  13. public void add() {
  14. atomicInteger.getAndIncrement();
  15. }
  16. public Integer get() {
  17. return atomicInteger.get();
  18. }
  19. }
  20. public class AtomicInteger_Demo_ {
  21. public static void main(String[] args) throws InterruptedException {
  22. CountDownLatch countDownLatch = new CountDownLatch(50);
  23. MyNumber myNumber = new MyNumber();
  24. for (int i = 1; i <= 50; i++) {
  25. new Thread(() -> {
  26. try {
  27. for (int j = 1; j <= 500; j++) {
  28. myNumber.add();
  29. }
  30. } finally {
  31. countDownLatch.countDown();
  32. }
  33. }).start();
  34. }
  35. countDownLatch.await();
  36. System.out.println("myNumber = " + myNumber.get());
  37. }
  38. }
  • AtomicBoolean ```java import java.util.concurrent.atomic.AtomicBoolean;

public class AtomicBooleanDemo {

  1. private static AtomicBoolean atomicBoolean = new AtomicBoolean(false);
  2. public static void main(String[] args) {
  3. atomicBoolean.set(true);
  4. boolean b = atomicBoolean.get();
  5. System.out.println("b = " + b);
  6. }

}

  1. - AtomicLong
  2. ```java
  3. import java.util.concurrent.CountDownLatch;
  4. import java.util.concurrent.atomic.AtomicLong;
  5. class MyNumber_ {
  6. private AtomicLong atomicLong = new AtomicLong();
  7. public void add() {
  8. atomicLong.getAndIncrement();
  9. }
  10. public Long get() {
  11. return atomicLong.get();
  12. }
  13. }
  14. public class AtomicLong_Demo_ {
  15. public static void main(String[] args) throws InterruptedException {
  16. CountDownLatch countDownLatch = new CountDownLatch(500);
  17. MyNumber_ myNumber_ = new MyNumber_();
  18. for (int i = 1; i <= 500 ; i++) {
  19. new Thread(()->{
  20. try {
  21. for (int j = 1; j <= 5000; j++) {
  22. myNumber_.add();
  23. }
  24. }finally {
  25. countDownLatch.countDown();
  26. }
  27. }).start();
  28. }
  29. countDownLatch.await();
  30. System.out.println(myNumber_.get());
  31. }
  32. }
  • 常用API:

    • public final int get() //获取当前的值
    • public final int getAndSet(int newValue)//获取当前的值,并设置新的值
    • public final int getAndIncrement()//获取当前的值,并自增
    • public final int getAndDecrement() //获取当前的值,并自减
    • public final int getAndAdd(int delta) //获取当前的值,并加上预期的值
    • boolean compareAndSet(int expect, int update) //如果输入的数值等于预期值,则以原子方式将该值设置为输入值(update)

      2.数组类型原子类

  • AtomicIntegerArray ```java import java.util.concurrent.atomic.AtomicIntegerArray;

public class AtomicIntegerArrayDemo {

  1. private static AtomicIntegerArray atomicIntegerArray = new AtomicIntegerArray(10);
  2. public static void main(String[] args) {
  3. atomicIntegerArray.set(1, 1);
  4. atomicIntegerArray.set(2, 1);
  5. atomicIntegerArray.set(3, 1);
  6. System.out.println(atomicIntegerArray.get(1));
  7. System.out.println(atomicIntegerArray.incrementAndGet(1));
  8. System.out.println(atomicIntegerArray.get(1));
  9. }

}

  1. - AtomicLongArray
  2. ```java
  3. import java.util.concurrent.atomic.AtomicLongArray;
  4. public class AtomicLongArray_Demo_1 {
  5. private static AtomicLongArray atomicLongArray = new AtomicLongArray(10);
  6. public static void main(String[] args) {
  7. atomicLongArray.set(0, 1);
  8. atomicLongArray.set(1, 2);
  9. atomicLongArray.set(2, 3);
  10. atomicLongArray.set(3, 4);
  11. atomicLongArray.set(4, 5);
  12. System.out.println(atomicLongArray.get(0));
  13. System.out.println(atomicLongArray.incrementAndGet(0));
  14. }
  15. }
  • AtomicReferenceArray ```java

import java.util.concurrent.atomic.AtomicReferenceArray;

public class AtomicReferenceArray_Demo {

  1. private static AtomicReferenceArray<String> atomicReferenceArray = new AtomicReferenceArray<>(10);
  2. public static void main(String[] args) {
  3. atomicReferenceArray.set(0, "A");
  4. atomicReferenceArray.set(1, "B");
  5. atomicReferenceArray.set(2, "C");
  6. atomicReferenceArray.set(3, "D");
  7. String c = atomicReferenceArray.getAndAccumulate(0, "A",
  8. (v1, v2) -> v1 + v2);
  9. System.out.println("c = " + atomicReferenceArray.get(0));
  10. }

}

  1. <a name="B9bbn"></a>
  2. ## 3.引用类型原子类
  3. - AtomicReference
  4. ```java
  5. @ToString
  6. @AllArgsConstructor
  7. @Getter
  8. @Setter
  9. class User_ {
  10. private Integer age;
  11. private String username;
  12. }
  13. public class AtomicReference_Demo_ {
  14. private static AtomicReference<User_> atomicReference = new AtomicReference<>();
  15. public static void main(String[] args) throws InterruptedException {
  16. User_ anda_1 = new User_(12, "ANDA");
  17. User_ anda_2 = new User_(12, "ANDA");
  18. atomicReference.set(anda_1);
  19. boolean b = atomicReference.compareAndSet(anda_1, anda_2);
  20. System.out.println("b = " + b);
  21. atomicReference.compareAndSet(anda_2, anda_1);
  22. TimeUnit.SECONDS.sleep(3);
  23. boolean b1 = atomicReference.compareAndSet(anda_1, anda_2);
  24. System.out.println("b1 = " + b1);
  25. }
  26. }
  • AtomicStampedReference ```java

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

public class AtomicStampedReferenceDemo {

  1. private static AtomicStampedReference<Long> atomicStampedReference = new AtomicStampedReference<>(100L, 1);
  2. public static void main(String[] args) {
  3. new Thread(() -> {
  4. int stamp = atomicStampedReference.getStamp();
  5. try {
  6. TimeUnit.SECONDS.sleep(1);
  7. } catch (InterruptedException e) {
  8. e.printStackTrace();
  9. }
  10. boolean b = atomicStampedReference.compareAndSet(100L, 101L, stamp, stamp + 1);
  11. System.out.println(Thread.currentThread().getName() + "====>" + b);
  12. boolean b1 = atomicStampedReference.compareAndSet(101L, 100L, atomicStampedReference.getStamp(), atomicStampedReference.getStamp() + 1);
  13. System.out.println(Thread.currentThread().getName() + "====>" + b1);
  14. }).start();
  15. new Thread(() -> {
  16. int stamp = atomicStampedReference.getStamp();
  17. try {
  18. TimeUnit.SECONDS.sleep(3);
  19. } catch (InterruptedException e) {
  20. e.printStackTrace();
  21. }
  22. boolean b1 = atomicStampedReference.compareAndSet(100L, 1000L, stamp, stamp + 1);
  23. System.out.println("b1 = " + b1);
  24. }).start();
  25. }

}

  1. - AtomicMarkableReference
  2. ```java
  3. import java.util.concurrent.atomic.AtomicMarkableReference;
  4. public class AtomicMarkableReference_Demo_ {
  5. private static AtomicMarkableReference<Integer> atomicMarkableReference = new AtomicMarkableReference(1, false);
  6. public static void main(String[] args) {
  7. atomicMarkableReference.set(1, false);
  8. Integer integer = atomicMarkableReference.get(new boolean[]{true});
  9. System.out.println("integer = " + integer);
  10. }
  11. }

4.对象的属性修改原子类

  • 使用目的
    • 以一种线程安全的方式操作非线程安全对象内的某些字段
  • 使用要求
    • 更新的对象属性必须使用 public volatile 修饰符。
    • 因为对象的属性修改类型原子类都是抽象类,所以每次使用都必须

使用静态方法newUpdater()创建一个更新器,并且需要设置想要更新的类和属性

  • AtomicIntegerFieldUpdater ```java

import java.util.concurrent.CountDownLatch; import java.util.concurrent.atomic.AtomicIntegerFieldUpdater;

@Getter @Setter @ToString @AllArgsConstructor @NoArgsConstructor class Demo_ {

  1. private static AtomicIntegerFieldUpdater<Demo_>
  2. atomicIntegerFieldUpdater = AtomicIntegerFieldUpdater.newUpdater(Demo_.class, "sum");
  3. private String username;
  4. private volatile int sum;
  5. public void add(Demo_ demo_) {
  6. atomicIntegerFieldUpdater.incrementAndGet(demo_);
  7. }

}

public class AtomicIntegerFieldUpdaterDemo { public static void main(String[] args) throws InterruptedException { Demo anda = new Demo(“Anda”,100); CountDownLatch countDownLatch = new CountDownLatch(100); for (int i = 1; i <= 100; i++) { new Thread(() -> { try { anda.add(anda); } finally { countDownLatch.countDown(); } } ).start(); } countDownLatch.await(); int sum = anda.getSum(); System.out.println(“sum = “ + sum);

  1. }

}

  1. - AtomicLongFieldUpdater
  2. ```java
  3. import lombok.*;
  4. import java.util.concurrent.CountDownLatch;
  5. import java.util.concurrent.atomic.AtomicLongFieldUpdater;
  6. @Getter
  7. @Setter
  8. @ToString
  9. @AllArgsConstructor
  10. @NoArgsConstructor
  11. class _DEMO_ {
  12. private String username;
  13. private volatile long sum;
  14. private static AtomicLongFieldUpdater<_DEMO_> atomicLongFieldUpdater = AtomicLongFieldUpdater.newUpdater(_DEMO_.class, "sum");
  15. public void add(_DEMO_ demo_) {
  16. atomicLongFieldUpdater.incrementAndGet(demo_);
  17. }
  18. }
  19. public class AtomicLongReference_Demo_ {
  20. public static void main(String[] args) throws InterruptedException {
  21. CountDownLatch countDownLatch = new CountDownLatch(100);
  22. _DEMO_ demo_ = new _DEMO_("TESt", 0);
  23. for (int i = 1; i <= 100; i++) {
  24. new Thread(() -> {
  25. try {
  26. demo_.add(demo_);
  27. } finally {
  28. countDownLatch.countDown();
  29. }
  30. }).start();
  31. }
  32. countDownLatch.await();
  33. System.out.println(demo_.getSum());
  34. }
  35. }
  • AtomicReferenceFieldUpdater ```java

import java.util.concurrent.atomic.AtomicReferenceFieldUpdater;

class AtomicReference_Demo { private volatile Boolean status = Boolean.FALSE;

  1. private AtomicReferenceFieldUpdater<_AtomicReference_Demo_, Boolean> atomicReferenceFieldUpdater = AtomicReferenceFieldUpdater.newUpdater(
  2. _AtomicReference_Demo_.class, Boolean.class, "status");
  3. public void get_status(_AtomicReference_Demo_ atomicReference_demo_) {
  4. if (atomicReferenceFieldUpdater.compareAndSet(atomicReference_demo_, Boolean.FALSE, Boolean.TRUE)) {
  5. System.out.println(Thread.currentThread().getName() + "获取到说了");
  6. } else {
  7. System.out.println(Thread.currentThread().getName() + "没有获取到");
  8. }
  9. }

}

public class AtomicReferenceFieldUpdaterDemo {

  1. public static void main(String[] args) {
  2. _AtomicReference_Demo_ atomicReference_demo_ = new _AtomicReference_Demo_();
  3. for (int i = 1; i <= 5; i++) {
  4. new Thread(() -> {
  5. atomicReference_demo_.get_status(atomicReference_demo_);
  6. }).start();
  7. }
  8. }

}

  1. <a name="Nxzj7"></a>
  2. ## 5.原子操作增强类
  3. > 注意DoubleAccumulator 和DoubleAdder 区别就是DoubleAdder只能增加数据, DoubleAccmulator可以增加,可以减少 , LongAccmulator和LongAdder同理
  4. - DoubleAccumulator
  5. - get() 获取当前累加的数量
  6. - accumulate(long x) 定义自增的数字
  7. - reset() 重置当前的内部累加的数据
  8. - getThenReset() 获取并且重置当前内部累加的数据
  9. ```java
  10. import java.util.concurrent.atomic.DoubleAccumulator;
  11. public class DoubleAccumulator_Demo {
  12. public static void main(String[] args) {
  13. DoubleAccumulator doubleAccumulator = new DoubleAccumulator(Double::sum, 1);
  14. doubleAccumulator.accumulate(12);
  15. doubleAccumulator.accumulate(12);
  16. double v = doubleAccumulator.doubleValue();
  17. System.out.println("v = " + v); // 25.0
  18. doubleAccumulator.reset(); // 1.0
  19. doubleAccumulator.accumulate(3);
  20. System.out.println("v = " + doubleAccumulator.get()); // 4.0
  21. }
  22. }
  • DoubleAdder ```java import java.util.concurrent.atomic.DoubleAdder;

public class DoubleAddr_Demo {

  1. public static void main(String[] args) {
  2. DoubleAdder doubleAdder = new DoubleAdder();
  3. doubleAdder.add(1);
  4. doubleAdder.add(1);
  5. doubleAdder.add(1);
  6. doubleAdder.add(1);
  7. double sum = doubleAdder.sum();
  8. System.out.println("sum = " + sum);
  9. doubleAdder.reset();
  10. System.out.println(doubleAdder.sum());
  11. }

}

  1. - LongAccumulator
  2. ```java
  3. import java.util.concurrent.atomic.LongAccumulator;
  4. public class LongAccumulator_Demo {
  5. public static void main(String[] args) {
  6. LongAccumulator accumulator = new LongAccumulator(Long::sum, 1);
  7. accumulator.accumulate(1);
  8. accumulator.accumulate(1);
  9. accumulator.accumulate(1);
  10. accumulator.accumulate(1);
  11. accumulator.accumulate(1);
  12. System.out.println(accumulator.get());
  13. accumulator.reset();
  14. System.out.println(accumulator.get());
  15. }
  16. }
  • LongAdder ```java import java.util.concurrent.atomic.LongAdder;

public class LongAdder_Demo {

  1. public static void main(String[] args) {
  2. LongAdder longAdder = new LongAdder();
  3. longAdder.increment();
  4. longAdder.increment();
  5. longAdder.increment();
  6. longAdder.increment();
  7. longAdder.increment();
  8. System.out.println(longAdder.sum());
  9. longAdder.reset();
  10. System.out.println(longAdder.sum());
  11. }

} ```

  1. 点赞计数器,看看性能