当前位置:   article > 正文

JUC并发编程学习笔记(简单易懂)_juc笔记

juc笔记

目录

1:回顾多线程

1.1 wait/sleep 的区别

1.2 synchronized锁 

1.3 Lock 锁

​编辑 1.3.1 什么是公平锁,非公平锁?

1.4  synchronized 和 Lock 区别

1.5 synchronized 版的生产者和消费者 

1.6 防止虚假唤醒问题

 1.7 JUC 版的生产者消费者问题 Lock Condition

1.8 Condition 实现精准通知唤醒

1.9 八锁现象问题理解

总结:

2:不安全的集合类

2.1 常见的不安全集合类:ArrayList

2.1.1 解决方法:

为啥这个是安全的?(未完善)

2.2 常见的不安全集合类:HashSet()

2.2.1 解决方法:

2.3 常见的不安全集合类:HashMap()

2.3.1 报错信息

2.3.2 解决方法

3:Callable

3.1 怎么启动Callable?

4:常用的辅助类

4.1 CountDownLatch

4.1.1 介绍(减法计数器)

 4.1.2 简单使用

4.2 CyclicBarrier

4.2.1 介绍(加法计数器)

4.3 Semaphore

4.3.1 介绍(信号量)

4.3.2 简单使用

5:读写锁(ReadWriteLock)

 5.1 使用

6:阻塞队列

6.1:队列

6.2 :四组API

6.2.1:抛出异常

6.2.2:有返回值,不会抛出异常

6.2.3:阻塞等待

6.2.4:超时等待

6.2.5 总结

6.3: SynchronousQueue(同步队列)

7:线程池

7.1 线程池的好处

7.1.1  3大方法

 7.1.2  7大参数

  7.1.2  四种拒绝策略

7.1.2.1  代码示例:

8:了解CPU密集型和IO密集型(池的大小)

9:四大函数式接口

9.1 Function 函数式接口

9.2 Predicate 断定型接口

9.3 Consumer 消费性接口

9.4 Supplier 供给型接口

 10 Stream流式计算

 11 ForkJoin(分支合并)

12 异步回调

13 Volatile(可见性)

不保证原子性

不加锁保证原子性

 14 单例模式

饿汉式单例

懒汉式单例

为什么要用两个if判断呢?用一个if加上synchronized不行吗?下面我们来分析为什么不能用一个if+synchronized。

静态内部类

15  CAS

ABA

16 可重入锁

17 自旋锁


1:回顾多线程

进程和线程是什么
进程是操作系统分配资源的最小单元,而线程是cpu调度的最小单元。

  • java默认有几个线程
    2个,main线程和GC线程(GC垃圾回收机制)

  • java可以开启线程么
    不能

  • 并发和并行
    并发,多线程操作同一个资源,cpu单核,模拟多条线程,快速交替
    并行,多人一起走,cpu多核,多个线程可以同时执行,线程池

  1. package main;
  2. public class Demo1 {
  3. public static void main(String[] args) {
  4. //获取cpu的核数
  5. //cpu密集型,io密集型
  6. System.out.println(Runtime.getRuntime().availableProcessors());
  7. }
  8. }

示例:

线程有几个状态:

Thread.State

  1. public enum State {
  2. /**
  3. * 新建状态
  4. */
  5. NEW,
  6. /**
  7. * 运行状态
  8. */
  9. RUNNABLE,
  10. /**
  11. * 堵塞状态
  12. */
  13. BLOCKED,
  14. /**
  15. * 等待状态
  16. */
  17. WAITING,
  18. /**
  19. * 超时等待
  20. */
  21. TIMED_WAITING,
  22. /**
  23. * 终止状态
  24. */
  25. TERMINATED;
  26. }

1.1 wait/sleep 的区别

1.来自不同类,wait->Object,sleep->Thread
2.锁的释放,wait->释放锁,sleep->不释放锁
3.使用范围,wait->同步代码块,sleep->任何地方

1.2 synchronized锁 

  1. package main;
  2. /*
  3. * 真正的多线程开发,公司中的开发,降低耦合型
  4. * 线程就是一个单独的资源类,没有任何附属的操作!
  5. * 1. 属性 方法
  6. * */
  7. public class TicketSale {
  8. public static void main(String[] args) {
  9. //并发:多线程操作同一个资源类,把资源丢入线程
  10. Ticket ticket = new Ticket();
  11. //@FunctionalInterface 函数式接口,jkd1.8 lambda 表达式(参数)->{代码}
  12. new Thread(()->{
  13. for (int i = 0; i < 30; i++) {
  14. ticket.sale();
  15. }
  16. },"A").start();
  17. new Thread(()->{
  18. for (int i = 0; i < 30; i++) {
  19. ticket.sale();
  20. }
  21. },"B").start();
  22. new Thread(()->{
  23. for (int i = 0; i < 30; i++) {
  24. ticket.sale();
  25. }
  26. },"C").start();
  27. }
  28. }
  29. //资源类OOP
  30. class Ticket{
  31. //属性 方法
  32. private int number = 30;
  33. //卖票的方式
  34. public synchronized void sale(){
  35. if (number>0){
  36. System.out.println(Thread.currentThread().getName()+"卖出了"+(number--)+"票,剩余"+number);
  37. }
  38. }
  39. }

1.3 Lock 锁

Class ReentrantLock 构造方法
public ReentrantLock()创建一个ReentrantLock的实例。 这相当于使用ReentrantLock(false)
public ReentrantLock(boolean fair)根据给定的公平政策创建一个 ReentrantLock的实例. fair - true如果此锁应使用合理的订购策略

1.3.1 什么是公平锁,非公平锁?

  • 非公平锁:可以插队(无参构造方法默认为非公平锁)
  • 公平锁:先来后到(有参构造方法传值true时为公平锁)
  1. package main;
  2. import java.util.concurrent.locks.Lock;
  3. import java.util.concurrent.locks.ReentrantLock;
  4. /*
  5. * 真正的多线程开发,公司中的开发,降低耦合型
  6. * 线程就是一个单独的资源类,没有任何附属的操作!
  7. * 1. 属性 方法
  8. * */
  9. public class TicketSale2 {
  10. public static void main(String[] args) {
  11. //并发:多线程操作同一个资源类,把资源丢入线程
  12. Ticket2 ticket = new Ticket2();
  13. //@FunctionalInterface 函数式接口,jkd1.8 lambda 表达式(参数)->{代码}
  14. new Thread(()->{
  15. for (int i = 0; i < 30; i++) {
  16. ticket.sale();
  17. }
  18. },"A").start();
  19. new Thread(()->{
  20. for (int i = 0; i < 30; i++) {
  21. ticket.sale();
  22. }
  23. },"B").start();
  24. new Thread(()->{
  25. for (int i = 0; i < 30; i++) {
  26. ticket.sale();
  27. }
  28. },"C").start();
  29. }
  30. }
  31. /*
  32. * lock三部曲
  33. * 1.new ReentrantLock()
  34. * 2.lock.lock();//加锁
  35. * 3.finally-> lock.unlock() //解锁
  36. * */
  37. class Ticket2{
  38. //属性 方法
  39. private int number = 30;
  40. //卖票的方式
  41. Lock lock = new ReentrantLock();
  42. public void sale(){
  43. lock.lock();//加锁
  44. try {
  45. //业务代码
  46. if (number>0){
  47. System.out.println(Thread.currentThread().getName()+"卖出了"+(number--)+"票,剩余"+number);
  48. }
  49. } catch (Exception e) {
  50. e.printStackTrace();
  51. } finally {
  52. lock.unlock();//解锁
  53. }
  54. }
  55. }
  56. /*
  57. 公平锁
  58. 线程1购买了第10张票
  59. 线程2购买了第9张票
  60. 线程3购买了第8张票
  61. 线程1购买了第7张票
  62. 线程2购买了第6张票
  63. 线程3购买了第5张票
  64. 线程1购买了第4张票
  65. 线程2购买了第3张票
  66. 线程3购买了第2张票
  67. 线程1购买了第1张票
  68. 非公平锁
  69. 线程1购买了第10张票
  70. 线程1购买了第9张票
  71. 线程1购买了第8张票
  72. 线程1购买了第7张票
  73. 线程1购买了第6张票
  74. 线程1购买了第5张票
  75. 线程1购买了第4张票
  76. 线程1购买了第3张票
  77. 线程1购买了第2张票
  78. 线程1购买了第1张票
  79. */

1.4  synchronized 和 Lock 区别

  1. lock是一个接口,而synchronized是java的一个关键字。
  2. synchronized在发生异常时会自动释放占有的锁,因此不会出现死锁;而lock发生异常时,不会主动释放占有的锁,必须手动来释放锁,可能引起死锁的发生。
  3. Synchronized 可重入锁,不可以中断的,非公平; Lock ,可重入锁,可以判断锁,非公平(可以自己设置);
  4. Synchronized适合锁少量的代码同步问题,Lock适合锁大量的同步代码!
  5. Synchronized 线程1(获得锁,阻塞)、绩程2(等待,傻傻的等) ; Lock锁就不一定会等待下去;
  6. Synchronized无法判断获取锁的状态,Lock 可以判断是否获取到了锁

synchronized

Lock

1.5 synchronized 版的生产者和消费者 

  1. package pc;
  2. /*
  3. * 线程之间的通信问题:生产者和消费者问题 等待唤醒 ,通知唤醒
  4. * 线程交替执行 A B 操作同一个变量 num = 0;
  5. * A num+1
  6. * B num-1
  7. * */
  8. public class A {
  9. public static void main(String[] args) {
  10. Data data = new Data();
  11. new Thread(()->{
  12. for (int i = 0; i < 10; i++) {
  13. try {
  14. data.increment();
  15. } catch (InterruptedException e) {
  16. e.printStackTrace();
  17. }
  18. }
  19. },"A").start();
  20. new Thread(()->{
  21. for (int i = 0; i < 10; i++) {
  22. try {
  23. data.decrement();
  24. } catch (InterruptedException e) {
  25. e.printStackTrace();
  26. }
  27. }
  28. },"B").start();
  29. }
  30. }
  31. //等待 业务 通知
  32. class Data{//数字 资源类
  33. private int number = 0;
  34. public synchronized void increment() throws InterruptedException {
  35. if (number != 0) {
  36. //等待
  37. this.wait();
  38. }
  39. number++;
  40. //通知其他线程,我+1完毕了
  41. System.out.println(Thread.currentThread().getName()+"-->"+number);
  42. this.notifyAll();
  43. }
  44. public synchronized void decrement() throws InterruptedException {
  45. if (number == 0) {
  46. this.wait();
  47. }
  48. number--;
  49. System.out.println(Thread.currentThread().getName()+"-->"+number);
  50. this.notifyAll();
  51. }
  52. }

1.6 防止虚假唤醒问题

四个线程时(两个生产者两个消费者)出现虚假唤醒问题,需要使用while来替换if来判断唤醒后是否可以继续执行
理解 : 拿两个加法线程A、B来说,比如A先执行,执行时调用了wait方法,那它会等待,此时会释放锁,那么线程B获得锁并且也会执行wait方法,两个加线程一起等待被唤醒。此时减线程中的某一个线程执行完毕并且唤醒了这俩加线程,那么这俩加线程不会一起执行,其中A获取了锁并且加1,执行完毕之后B再执行。如果是if的话,那么A修改完num后,B不会再去判断num的值,直接会给num+1。如果是while的话,A执行完之后,B还会去判断num的值,因此就不会执行

  1. package pc;
  2. /*
  3. * 线程之间的通信问题:生产者和消费者问题 等待唤醒 ,通知唤醒
  4. * 线程交替执行 A B 操作同一个变量 num = 0;
  5. * A num+1
  6. * B num-1
  7. * */
  8. public class A {
  9. public static void main(String[] args) {
  10. Data data = new Data();
  11. new Thread(()->{
  12. for (int i = 0; i < 10; i++) {
  13. try {
  14. data.increment();
  15. } catch (InterruptedException e) {
  16. e.printStackTrace();
  17. }
  18. }
  19. },"A").start();
  20. new Thread(()->{
  21. for (int i = 0; i < 10; i++) {
  22. try {
  23. data.decrement();
  24. } catch (InterruptedException e) {
  25. e.printStackTrace();
  26. }
  27. }
  28. },"B").start();
  29. new Thread(()->{
  30. for (int i = 0; i < 10; i++) {
  31. try {
  32. data.increment();
  33. } catch (InterruptedException e) {
  34. e.printStackTrace();
  35. }
  36. }
  37. },"C").start();
  38. new Thread(()->{
  39. for (int i = 0; i < 10; i++) {
  40. try {
  41. data.decrement();
  42. } catch (InterruptedException e) {
  43. e.printStackTrace();
  44. }
  45. }
  46. },"D").start();
  47. }
  48. }
  49. //等待 业务 通知
  50. class Data{//数字 资源类
  51. private int number = 0;
  52. public synchronized void increment() throws InterruptedException {
  53. while (number != 0) {
  54. //等待
  55. this.wait();
  56. }
  57. number++;
  58. //通知其他线程,我+1完毕了
  59. System.out.println(Thread.currentThread().getName()+"-->"+number);
  60. this.notifyAll();
  61. }
  62. public synchronized void decrement() throws InterruptedException {
  63. while (number == 0) {
  64. this.wait();
  65. }
  66. number--;
  67. System.out.println(Thread.currentThread().getName()+"-->"+number);
  68. this.notifyAll();
  69. }
  70. }

 1.7 JUC 版的生产者消费者问题 Lock Condition

await:

 signalAll:

  1. package pc;
  2. import java.util.concurrent.locks.Condition;
  3. import java.util.concurrent.locks.Lock;
  4. import java.util.concurrent.locks.ReentrantLock;
  5. public class B {
  6. public static void main(String[] args) {
  7. Data2 data = new Data2();
  8. new Thread(()->{
  9. for (int i = 0; i < 10; i++) {
  10. try {
  11. data.increment();
  12. } catch (InterruptedException e) {
  13. e.printStackTrace();
  14. }
  15. }
  16. },"A").start();
  17. new Thread(()->{
  18. for (int i = 0; i < 10; i++) {
  19. try {
  20. data.decrement();
  21. } catch (InterruptedException e) {
  22. e.printStackTrace();
  23. }
  24. }
  25. },"B").start();
  26. new Thread(()->{
  27. for (int i = 0; i < 10; i++) {
  28. try {
  29. data.increment();
  30. } catch (InterruptedException e) {
  31. e.printStackTrace();
  32. }
  33. }
  34. },"C").start();
  35. new Thread(()->{
  36. for (int i = 0; i < 10; i++) {
  37. try {
  38. data.decrement();
  39. } catch (InterruptedException e) {
  40. e.printStackTrace();
  41. }
  42. }
  43. },"D").start();
  44. }
  45. }
  46. //等待 业务 通知
  47. class Data2 {//数字 资源类
  48. private int number = 0;
  49. Lock lock = new ReentrantLock();
  50. Condition condition = lock.newCondition();
  51. // condition.await();//等待
  52. // condition.signalAll();//唤醒全部
  53. public void increment() throws InterruptedException {
  54. lock.lock();
  55. try {
  56. //业务代码
  57. while (number != 0) {
  58. //等待
  59. condition.await();
  60. }
  61. number++;
  62. //通知其他线程,我+1完毕了
  63. System.out.println(Thread.currentThread().getName() + "-->" + number);
  64. condition.signalAll();
  65. } catch (InterruptedException e) {
  66. e.printStackTrace();
  67. } finally {
  68. lock.unlock();
  69. }
  70. }
  71. public void decrement() throws InterruptedException {
  72. lock.lock();
  73. try {
  74. while (number == 0) {
  75. condition.await();
  76. }
  77. number--;
  78. System.out.println(Thread.currentThread().getName() + "-->" + number);
  79. condition.signalAll();
  80. } catch (Exception e) {
  81. e.printStackTrace();
  82. } finally {
  83. lock.unlock();
  84. }
  85. }
  86. }

1.8 Condition 实现精准通知唤醒

await:

 signal:

  1. package pc;
  2. import java.util.concurrent.locks.Condition;
  3. import java.util.concurrent.locks.Lock;
  4. import java.util.concurrent.locks.ReentrantLock;
  5. public class C {
  6. public static void main(String[] args) {
  7. Data3 data = new Data3();
  8. new Thread(() -> {
  9. for (int i = 0; i < 10; i++) {
  10. data.printA();
  11. }
  12. }, "A").start();
  13. new Thread(() -> {
  14. for (int i = 0; i < 10; i++) {
  15. data.printB();
  16. }
  17. }, "B").start();
  18. new Thread(() -> {
  19. for (int i = 0; i < 10; i++) {
  20. data.printC();
  21. }
  22. }, "C").start();
  23. }
  24. }
  25. class Data3 {// 资源类 Lock
  26. private Lock lock = new ReentrantLock();
  27. private Condition condition1 = lock.newCondition();
  28. private Condition condition2 = lock.newCondition();
  29. private Condition condition3 = lock.newCondition();
  30. private int number = 1;
  31. public void printA() {
  32. lock.lock();
  33. try {
  34. //业务,判断->执行->通知
  35. while (number != 1) {
  36. //等待
  37. condition1.await();
  38. }
  39. System.out.println(Thread.currentThread().getName() + "--->A");
  40. //唤醒指定的人:B
  41. number = 2;
  42. condition2.signal();
  43. } catch (Exception e) {
  44. e.printStackTrace();
  45. } finally {
  46. lock.unlock();
  47. }
  48. }
  49. public void printB() {
  50. lock.lock();
  51. try {
  52. //业务,判断->执行->通知
  53. while (number != 2) {
  54. //等待
  55. condition2.await();
  56. }
  57. System.out.println(Thread.currentThread().getName() + "--->B");
  58. //唤醒指定的人:C
  59. number = 3;
  60. condition3.signal();
  61. } catch (Exception e) {
  62. e.printStackTrace();
  63. } finally {
  64. lock.unlock();
  65. }
  66. }
  67. public void printC() {
  68. lock.lock();
  69. try {
  70. //业务,判断->执行->通知
  71. while (number!=3){
  72. //等待
  73. condition3.await();
  74. }
  75. System.out.println(Thread.currentThread().getName()+"--->C");
  76. number=1;
  77. condition1.signal();
  78. } catch (Exception e) {
  79. e.printStackTrace();
  80. } finally {
  81. lock.unlock();
  82. }
  83. }
  84. }

1.9 八锁现象问题理解

 同一把锁:synchronized 锁的对象是方法的调用者(phone)

  1. package lock;
  2. import java.util.concurrent.TimeUnit;
  3. /*
  4. * 1.标准情况下,两个线程先打印发短信还是打电话?1.发短信﹑2.打电话
  5. * 2.sendSms延迟4秒,两个线程先打印 发短信还是 打电话?1.发短信 2.打电话
  6. * */
  7. public class Demo1 {
  8. public static void main(String[] args) throws InterruptedException {
  9. Phone phone = new Phone();
  10. //锁的存在
  11. new Thread(()->{
  12. phone.sendSms();
  13. },"A").start();
  14. TimeUnit.SECONDS.sleep(1);
  15. new Thread(()->{
  16. phone.call();
  17. },"B").start();
  18. }
  19. }
  20. class Phone{
  21. //synchronized 锁的对象是方法的调用者
  22. //两个方法是同一个锁,谁先拿到谁先执行
  23. public synchronized void sendSms(){
  24. try {
  25. TimeUnit.SECONDS.sleep(4);
  26. } catch (InterruptedException e) {
  27. e.printStackTrace();
  28. }
  29. System.out.println("发短信");
  30. }
  31. public synchronized void call(){
  32. System.out.println("打电话");
  33. }
  34. }

结果:


 两个对象不是用一把锁:phone,phone2

  1. package com.example.juc.test;
  2. import java.util.concurrent.TimeUnit;
  3. /*
  4. * 3.增加了一个普通方法,先发短信还是Hello
  5. * 4.两个对象,两个同步方法,先发短信还是先打电话
  6. * */
  7. public class Demo1 {
  8. public static void main(String[] args) throws InterruptedException {
  9. //两个对象,两个调用者,两把锁!
  10. Phone phone = new Phone();
  11. Phone phone2 = new Phone();
  12. //锁的存在
  13. new Thread(()->{
  14. System.out.println(Thread.currentThread().getName());
  15. phone.hello();
  16. phone.sendSms();
  17. },"A").start();
  18. TimeUnit.SECONDS.sleep(1);
  19. new Thread(()->{
  20. System.out.println(Thread.currentThread().getName());
  21. phone2.hello();
  22. phone2.call();
  23. },"B").start();
  24. }
  25. }
  26. class Phone{
  27. //synchronized 锁的对象是方法的调用者
  28. //两个方法是同一个锁,谁先拿到谁先执行
  29. public synchronized void sendSms(){
  30. try {
  31. TimeUnit.SECONDS.sleep(4);
  32. } catch (InterruptedException e) {
  33. e.printStackTrace();
  34. }
  35. System.out.println("发短信");
  36. }
  37. public synchronized void call(){
  38. System.out.println("打电话");
  39. }
  40. //这里没有锁,不是同步方法,不受锁的影响
  41. public void hello(){
  42. System.out.println("hello");
  43. }
  44. }

结果:


static静态方法两把锁锁的都是class模板:class phone

Phone唯一的一个Class对象

  1. package com.example.juc.test;
  2. import java.util.concurrent.TimeUnit;
  3. /*
  4. * 5.增加两个静态的同步方法,只有一个对象,先打印 发短信还是打电话
  5. * 6.两个对象!增加两个静态的同步方法, 先打印 发短信还是打电话
  6. * */
  7. public class Demo1 {
  8. public static void main(String[] args) throws InterruptedException {
  9. //两个对象的Class类模板只有一个,static,锁的是Class
  10. Phone phone = new Phone();
  11. Phone phone2 = new Phone();
  12. //锁的存在
  13. new Thread(()->{
  14. phone.sendSms();
  15. },"A").start();
  16. TimeUnit.SECONDS.sleep(1);
  17. new Thread(()->{
  18. phone2.call();
  19. },"B").start();
  20. }
  21. }
  22. //Phone唯一的一个Class对象
  23. class Phone{
  24. //synchronized 锁的对象是方法的调用者
  25. //static 静态方法
  26. //类一加载就有了!锁的是Class
  27. public static synchronized void sendSms(){
  28. try {
  29. TimeUnit.SECONDS.sleep(4);
  30. } catch (InterruptedException e) {
  31. e.printStackTrace();
  32. }
  33. System.out.println("发短信");
  34. }
  35. public static synchronized void call(){
  36. System.out.println("打电话");
  37. }
  38. }

结果:


不是同一把锁,A线程的锁是phone类,B线程的锁是phone2

  1. package com.example.juc.test;
  2. import java.util.concurrent.TimeUnit;
  3. /*
  4. * 7.1个静态的同步方法,1个普通的同步方法,1个对象,先打印谁
  5. * 8.1个静态的同步方法,1个普通的同步方法,2个对象,先打印谁
  6. * */
  7. public class Demo1 {
  8. public static void main(String[] args) throws InterruptedException {
  9. //两个对象的Class类模板只有一个,static,锁的是Class
  10. Phone phone = new Phone();
  11. Phone phone2 = new Phone();
  12. //锁的存在
  13. new Thread(() -> {
  14. phone.sendSms();
  15. }, "A").start();
  16. TimeUnit.SECONDS.sleep(1);
  17. new Thread(() -> {
  18. phone2.call();
  19. }, "B").start();
  20. }
  21. }
  22. //Phone唯一的一个Class对象
  23. class Phone {
  24. //静态的同步方法 锁的是Class类模板
  25. public static synchronized void sendSms() {
  26. try {
  27. TimeUnit.SECONDS.sleep(4);
  28. } catch (InterruptedException e) {
  29. e.printStackTrace();
  30. }
  31. System.out.println("发短信");
  32. }
  33. //普通的同步方法 锁的调用者
  34. public synchronized void call() {
  35. System.out.println("打电话");
  36. }
  37. }

结果:

总结:

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

2:不安全的集合类

2.1 常见的不安全集合类:ArrayList

  1. /**
  2. * .ConcurrentModificationException 并发修改异常
  3. */
  4. public class test {
  5. public static void main(String[] args) {
  6. //并发情况下 ArrayList是不安全的
  7. List<String> list = new ArrayList<>();
  8. //Vector<String> list = new Vector<>();
  9. for (int i = 1; i <= 10 ; i++) {
  10. new Thread(()->{
  11. list.add(UUID.randomUUID().toString().substring(0,5));
  12. System.out.println(list);
  13. },String.valueOf(i)).start();
  14. }
  15. }
  16. }

在并发情况下报错:

  1. Exception in thread "5" java.util.ConcurrentModificationException
  2. at java.util.ArrayList$Itr.checkForComodification(ArrayList.java:907)
  3. at java.util.ArrayList$Itr.next(ArrayList.java:857)
  4. at java.util.AbstractCollection.toString(AbstractCollection.java:461)
  5. at java.lang.String.valueOf(String.java:2994)
  6. at java.io.PrintStream.println(PrintStream.java:821)

2.1.1 解决方法:

1:new Vector()

  1. public class test {
  2. public static void main(String[] args) {
  3. Vector<String> list = new Vector<>();
  4. for (int i = 1; i <= 10 ; i++) {
  5. new Thread(()->{
  6. list .add(UUID.randomUUID().toString().substring(0,5));
  7. System.out.println(list );
  8. },String.valueOf(i)).start();
  9. }
  10. }
  11. }

2:Collections.synchronizedList()这个重点

  1. /**
  2. * .ConcurrentModificationException 并发修改异常
  3. */
  4. public class test {
  5. public static void main(String[] args) {
  6. List<String> list = new ArrayList<>();
  7. List<String> list2 = Collections.synchronizedList(list);
  8. for (int i = 1; i <= 10 ; i++) {
  9. new Thread(()->{
  10. list2.add(UUID.randomUUID().toString().substring(0,5));
  11. System.out.println(list2);
  12. },String.valueOf(i)).start();
  13. }
  14. }
  15. }

 3:CopyOnWriteArrayList<E>

  1. public class test {
  2. public static void main(String[] args) {
  3. List<String> list2 = new CopyOnWriteArrayList();
  4. for (int i = 1; i <= 10 ; i++) {
  5. new Thread(()->{
  6. list2.add(UUID.randomUUID().toString().substring(0,5));
  7. System.out.println(list2);
  8. },String.valueOf(i)).start();
  9. }
  10. }
  11. }

为啥这个是安全的?(未完善)

  1. /*
  2. * CopyOnWrite 写入时赋值 COW 计算机程序设计领域的一种优化策略
  3. * 多个线程调用的时候 , list , 读取的时候 , 固定的, 写入(覆盖)
  4. * 在写入的时候避免覆盖 , 造成数据问题!
  5. * 读写分离
  6. */
  1. 1:
  2. public CopyOnWriteArrayList() {
  3. setArray(new Object[0]);
  4. }
  5. 2:
  6. final void setArray(Object[] a) {
  7. array = a;
  8. }
  9. 3:
  10. private transient volatile Object[] array;

为啥不用:Vector 而是选择用 :CopyOnWriteArrayList

1:Vector  synchronized  修饰,效率相对低。

2:CopyOnWriteArrayList  的方法 都没被 synchronized   修饰,效率相对较高。

2.2 常见的不安全集合类:HashSet()

  1. public class set集合 {
  2. public static void main(String[] args) {
  3. Set<String> set = new HashSet<>();
  4. for (int i = 1; i <= 30 ; i++) {
  5. new Thread(()->{
  6. set.add(UUID.randomUUID().toString().substring(0,5));
  7. System.out.println(set);
  8. },String.valueOf(i)).start();
  9. }
  10. }
  11. }

报错信息:

  1. Exception in thread "11" Exception in thread "16" java.util.ConcurrentModificationException
  2. at java.util.HashMap$HashIterator.nextNode(HashMap.java:1442)
  3. at java.util.HashMap$KeyIterator.next(HashMap.java:1466)
  4. at java.util.AbstractCollection.toString(AbstractCollection.java:461)
  5. at java.lang.String.valueOf(String.java:2994)
  6. at java.io.PrintStream.println(PrintStream.java:821)

2.2.1 解决方法:

1:Collections.synchronizedSet(new HashSet<>());

  1. public class set集合 {
  2. public static void main(String[] args) {
  3. //Set<String> seobjectst = new HashSet<>();
  4. Set<String> set = Collections.synchronizedSet(new HashSet<>());
  5. for (int i = 1; i <= 30 ; i++) {
  6. new Thread(()->{
  7. set.add(UUID.randomUUID().toString().substring(0,5));
  8. System.out.println(set);
  9. },String.valueOf(i)).start();
  10. }
  11. }
  12. }

1.2  new CopyOnWriteArraySet<>();

  1. public class set集合 {
  2. public static void main(String[] args) {
  3. Set<String> set = new CopyOnWriteArraySet<>();
  4. for (int i = 1; i <= 30 ; i++) {
  5. new Thread(()->{
  6. set.add(UUID.randomUUID().toString().substring(0,5));
  7. System.out.println(set);
  8. },String.valueOf(i)).start();
  9. }
  10. }
  11. }

HashSet的底层就是HashMap

 map的key不允许重复

2.3 常见的不安全集合类:HashMap()

  1. public class map {
  2. public static void main(String[] args) {
  3. Map<String,String> map2 = new HashMap<>();
  4. for (int i = 0; i < 30; i++) {
  5. new Thread(()->{
  6. map2.put(Thread.currentThread().getName(), UUID.randomUUID().toString().substring(0,5));
  7. System.out.println(map2);
  8. },String.valueOf(i)).start();
  9. }
  10. }
  11. }

2.3.1 报错信息

  1. Exception in thread "21" Exception in thread "25" java.util.ConcurrentModificationException
  2. at java.util.HashMap$HashIterator.nextNode(HashMap.java:1442)
  3. at java.util.HashMap$EntryIterator.next(HashMap.java:1476)
  4. at java.util.HashMap$EntryIterator.next(HashMap.java:1474)
  5. at java.util.AbstractMap.toString(AbstractMap.java:554)
  6. at java.lang.String.valueOf(String.java:2994)

2.3.2 解决方法

ConcurrentHashMap<>()

  1. Map<String,String> map2 = new ConcurrentHashMap<>();
  2. for (int i = 0; i < 30; i++) {
  3. new Thread(()->{
  4. map2.put(Thread.currentThread().getName(), UUID.randomUUID().toString().substring(0,5));
  5. System.out.println(map2);
  6. },String.valueOf(i)).start();
  7. }

 Collections.synchronizedMap();

  1. Map<String,String> map = new HashMap<>();
  2. Map<String, String> map2 = Collections.synchronizedMap(map);
  3. for (int i = 0; i < 30; i++) {
  4. new Thread(()->{
  5. map2.put(Thread.currentThread().getName(), UUID.randomUUID().toString().substring(0,5));
  6. System.out.println(map2);
  7. },String.valueOf(i)).start();
  8. }

3:Callable

3.1 怎么启动Callable?

  1. public class test {
  2. public static void main(String[] args) throws ExecutionException, InterruptedException {
  3. TestA test = new TestA();
  4. FutureTask task = new FutureTask(test); //适配类
  5. new Thread(task,"A").start();
  6. new Thread(task,"B").start(); //结果会被缓存,效率高
  7. /获取callable的返回结果,get方法可能产生阻塞!把他放到最后或者使用异步通信来处理
  8. String o = String.valueOf(task.get()); //获取callable 的返回结果
  9. System.out.println(o);
  10. }
  11. }
  12. class TestA implements Callable<String>{
  13. @Override
  14. public String call() throws Exception {
  15. System.out.println("我是好人");
  16. return "100";
  17. }
  18. }
  19. 我是好人
  20. 100
  21. 进程已结束,退出代码0

 Runable ---->  实现类:FutureTask--->  Callable

4:常用的辅助类

4.1 CountDownLatch

4.1.1 介绍(减法计数器)

允许一个或多个线程等待直到在其他线程中执行的一组操作完成的同步辅助。

 4.1.2 简单使用

源码:

  1. /**
  2. * Constructs a {@code CountDownLatch} initialized with the given count.
  3. *
  4. * @param count the number of times {@link #countDown} must be invoked
  5. * before threads can pass through {@link #await}
  6. * @throws IllegalArgumentException if {@code count} is negative
  7. */
  8. public CountDownLatch(int count) {
  9. if (count < 0) throw new IllegalArgumentException("count < 0");
  10. this.sync = new Sync(count);
  11. }
  1. //计数器
  2. public class CountDownLatchTestA {
  3. public static void main(String[] args) throws InterruptedException {
  4. //总数是6,必须要执行任务的时候再使用
  5. CountDownLatch countDownLatch = new CountDownLatch(6);
  6. for (int i = 0; i < 6; i++) {
  7. new Thread(()->{
  8. System.out.println(Thread.currentThread().getName()+"出去了");
  9. countDownLatch.countDown();//数量-1
  10. },String.valueOf(i)).start();
  11. }
  12. countDownLatch.await();//等待计数器归零,然后再往下执行
  13. System.out.println("人都出去完了,我要关门了");
  14. }
  15. }
  16. 打印结果:
  17. 0出去了
  18. 2出去了
  19. 5出去了
  20. 1出去了
  21. 4出去了
  22. 3出去了
  23. 人都出去完了,我要关门了

原理:

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

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

每次有线程调用countDown()方法,计数器减1, 假设计算机变为0 ,await()方法就会被唤醒,继续执行!

4.2 CyclicBarrier

4.2.1 介绍(加法计数器)

源码:

  1. //接收一个计数,一个线程参数
  2. public CyclicBarrier(int parties, Runnable barrierAction) {
  3. if (parties <= 0) throw new IllegalArgumentException();
  4. this.parties = parties;
  5. this.count = parties;
  6. this.barrierCommand = barrierAction;
  7. }
  1. public class CyclicBarrierTestA {
  2. public static void main(String[] args) {
  3. CyclicBarrier barrier = new CyclicBarrier(7,new Thread(()->{
  4. System.out.println("成功召唤神龙!!!");
  5. }));
  6. for (int i = 1; i <= 7 ; i++) {
  7. final int temp = i;
  8. new Thread(()->{
  9. System.out.println("第"+temp+"颗龙珠收集成功!");
  10. try {
  11. barrier.await();
  12. } catch (InterruptedException e) {
  13. throw new RuntimeException(e);
  14. } catch (BrokenBarrierException e) {
  15. throw new RuntimeException(e);
  16. }
  17. }).start();
  18. }
  19. }
  20. }
  21. 打印:
  22. 1颗龙珠收集成功!
  23. 7颗龙珠收集成功!
  24. 2颗龙珠收集成功!
  25. 3颗龙珠收集成功!
  26. 6颗龙珠收集成功!
  27. 4颗龙珠收集成功!
  28. 5颗龙珠收集成功!
  29. 成功召唤神龙!!!

4.3 Semaphore

4.3.1 介绍(信号量)

源码:

默认是非公平锁,但是可以设置为公平锁

acquire获取资源

release 释放资源

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

  1. public Semaphore(int permits) {
  2. sync = new NonfairSync(permits);
  3. }
  4. /**
  5. * Creates a {@code Semaphore} with the given number of
  6. * permits and the given fairness setting.
  7. *
  8. * @param permits the initial number of permits available.
  9. * This value may be negative, in which case releases
  10. * must occur before any acquires will be granted.
  11. * @param fair {@code true} if this semaphore will guarantee
  12. * first-in first-out granting of permits under contention,
  13. * else {@code false}
  14. */
  15. public Semaphore(int permits, boolean fair) {
  16. sync = fair ? new FairSync(permits) : new NonfairSync(permits);
  17. }

4.3.2 简单使用

  1. public class tWWW {
  2. public static void main(String[] args) {
  3. //线程数量,停车位,限流
  4. Semaphore semaphore = new Semaphore(3);
  5. for (int i = 0; i < 6; i++) {
  6. new Thread(()->{
  7. try {
  8. semaphore.acquire();//获得,假设如果满了,等待,等待被释放为止!
  9. System.out.println(Thread.currentThread().getName()+"抢到车位");
  10. TimeUnit.SECONDS.sleep(2);
  11. System.out.println(Thread.currentThread().getName()+"离开车位");
  12. } catch (InterruptedException e) {
  13. e.printStackTrace();
  14. } finally {
  15. semaphore.release();//释放,会将当前的信号量释放+1,然后唤醒等待的线程!
  16. }
  17. },String.valueOf(i)).start();
  18. }
  19. }
  20. }
  21. 打印结果如下:
  22. 2抢到车位
  23. 0抢到车位
  24. 1抢到车位
  25. 2离开车位
  26. 0离开车位
  27. 1离开车位
  28. 4抢到车位
  29. 5抢到车位
  30. 3抢到车位
  31. 4离开车位
  32. 5离开车位
  33. 3离开车位

5:读写锁(ReadWriteLock)

 5.1 使用

  1. package main;
  2. import java.util.HashMap;
  3. import java.util.Map;
  4. import java.util.concurrent.locks.Lock;
  5. import java.util.concurrent.locks.ReadWriteLock;
  6. import java.util.concurrent.locks.ReentrantReadWriteLock;
  7. /*
  8. * 独占锁(写锁) 一次只能被一个线程占有
  9. * 共享锁(读锁) 多个线程可以同时占有
  10. * ReadWriteLock
  11. * 读 - 读 可以共存
  12. * 读 - 写 不能共存
  13. * 写 - 写 不能共存
  14. * */
  15. public class ReadWriteLockDemo {
  16. public static void main(String[] args) {
  17. //MyCache myCache =new MyCache();
  18. MyCacheLock myCache =new MyCacheLock();
  19. //写入
  20. for (int i = 0; i < 5; i++) {
  21. final int temp = i;
  22. new Thread(()->{
  23. myCache.put(temp+"",temp+"");
  24. },String.valueOf(i)).start();
  25. }
  26. //读取
  27. for (int i = 0; i < 5; i++) {
  28. final int temp = i;
  29. new Thread(()->{
  30. myCache.get(temp+"");
  31. },String.valueOf(i)).start();
  32. }
  33. }
  34. }
  35. class MyCacheLock{
  36. private volatile Map<String,Object> map = new HashMap<>();
  37. //读写锁:更加细粒度的控制
  38. private ReadWriteLock readWriteLock = new ReentrantReadWriteLock();
  39. //存,写 的时候,只希望同时只有一个线程写
  40. public void put(String key,Object value){
  41. readWriteLock.writeLock().lock();
  42. try {
  43. System.out.println(Thread.currentThread().getName()+"写入"+key);
  44. map.put(key,value);
  45. System.out.println(Thread.currentThread().getName()+"写入OK");
  46. } catch (Exception e) {
  47. e.printStackTrace();
  48. } finally {
  49. readWriteLock.writeLock().unlock();
  50. }
  51. }
  52. //取,读 所有人都可以读
  53. public void get(String key){
  54. readWriteLock.readLock().lock();
  55. try {
  56. System.out.println(Thread.currentThread().getName()+"读取"+key);
  57. map.get(key);
  58. System.out.println(Thread.currentThread().getName()+"读取OK");
  59. } catch (Exception e) {
  60. e.printStackTrace();
  61. } finally {
  62. readWriteLock.readLock().unlock();
  63. }
  64. }
  65. }
  66. class MyCache{
  67. private volatile Map<String,Object> map = new HashMap<>();
  68. //存,写
  69. public void put(String key,Object value){
  70. System.out.println(Thread.currentThread().getName()+"写入"+key);
  71. map.put(key,value);
  72. System.out.println(Thread.currentThread().getName()+"写入OK");
  73. }
  74. //取,读
  75. public void get(String key){
  76. System.out.println(Thread.currentThread().getName()+"读取"+key);
  77. map.get(key);
  78. System.out.println(Thread.currentThread().getName()+"读取OK");
  79. }
  80. }
  81. 打印结果:
  82. 1写入1
  83. 1写入OK
  84. 2写入2
  85. 2写入OK
  86. 3写入3
  87. 3写入OK
  88. 4写入4
  89. 4写入OK
  90. 5写入5
  91. 5写入OK
  92. 2读取2
  93. 2读取OK
  94. 3读取3
  95. 3读取OK
  96. 5读取5
  97. 5读取OK
  98. 1读取1
  99. 4读取4
  100. 4读取OK
  101. 1读取OK

6:阻塞队列

6.1:队列

队列(FIFO)先进先出。

写入:如果队列满了,就必须阻塞等待。

读取:如果队列是空的,必须阻塞,等待生产,从而读取消息。

如下图所示:

6.2 :四组API

6.2.1:抛出异常

添加:add()

移除:remove()

判断队首:element()

  1. public class FIFO {
  2. public static void main(String[] args) {
  3. say();
  4. }
  5. public static void say(){
  6. ArrayBlockingQueue blockingQueue = new ArrayBlockingQueue<>(3);
  7. System.out.println(blockingQueue.add("a"));
  8. System.out.println(blockingQueue.add("b"));
  9. System.out.println(blockingQueue.add("c"));
  10. //Exception in thread "main" java.lang.IllegalStateException: Queue full
  11. //System.out.println(blockingQueue.add("d"));
  12. System.out.println(blockingQueue.remove());
  13. System.out.println(blockingQueue.remove());
  14. System.out.println(blockingQueue.remove());
  15. //Exception in thread "main" java.util.NoSuchElementException
  16. //System.out.println(blockingQueue.remove());
  17. }
  18. }

6.2.2:有返回值,不会抛出异常

添加:offer()

移除:poll()

判断队首:peek()

  1. public class FIFO {
  2. public static void main(String[] args) {
  3. say2();
  4. }
  5. public static void say2(){
  6. ArrayBlockingQueue blockingQueue = new ArrayBlockingQueue<>(3);
  7. //存
  8. System.out.println(blockingQueue.offer("a"));
  9. System.out.println(blockingQueue.offer("b"));
  10. System.out.println(blockingQueue.offer("c"));
  11. System.out.println(blockingQueue.offer("d"));
  12. //移除
  13. System.out.println(blockingQueue.poll());
  14. System.out.println(blockingQueue.poll());
  15. System.out.println(blockingQueue.poll());
  16. System.out.println(blockingQueue.poll());
  17. }
  18. }
  19. 打印:
  20. true
  21. true
  22. true
  23. false
  24. a
  25. b
  26. c
  27. null

6.2.3:阻塞等待

取:put()

移除:take()

  1. public class FIFO {
  2. public static void main(String[] args) throws InterruptedException {
  3. say3();
  4. }
  5. public static void say3() throws InterruptedException {
  6. //队列大小
  7. ArrayBlockingQueue blockingQueue = new ArrayBlockingQueue<>(3);
  8. blockingQueue.put("a");
  9. blockingQueue.put("b");
  10. blockingQueue.put("c");
  11. //队列没有位置了,第四个一直阻塞
  12. //blockingQueue.put("d");
  13. System.out.println(blockingQueue.take());
  14. System.out.println(blockingQueue.take());
  15. System.out.println(blockingQueue.take());
  16. // 队列中没有元素了,第四个一直阻塞
  17. //System.out.println(blockingQueue.take());
  18. }
  19. }

6.2.4:超时等待

  1. public class FIFO {
  2. public static void main(String[] args) throws InterruptedException {
  3. say4();
  4. }
  5. public static void say4() throws InterruptedException {
  6. ArrayBlockingQueue blockingQueue = new ArrayBlockingQueue<>(3);
  7. System.out.println(blockingQueue.offer("a"));
  8. System.out.println(blockingQueue.offer("b"));
  9. System.out.println(blockingQueue.offer("c"));
  10. System.out.println(blockingQueue.offer("d",2, TimeUnit.SECONDS));
  11. System.out.println(blockingQueue.poll());
  12. System.out.println(blockingQueue.poll());
  13. System.out.println(blockingQueue.poll());
  14. System.out.println(blockingQueue.poll(2,TimeUnit.SECONDS));
  15. }
  16. }

6.2.5 总结

方法摘要
ArrayBlockingQueue(int capacity)创建具有给定(固定)容量和默认访问策略的 ArrayBlockingQueue
boolean add(E e)在插入此队列的尾部,如果有可能立即这样做不超过该队列的容量,返回指定的元素 true成功时与抛出 IllegalStateException如果此队列已满。
boolean remove(Object o)从该队列中删除指定元素的单个实例(如果存在)。
boolean offer(E e)如果可以在不超过队列容量的情况下立即将其指定的元素插入该队列的尾部,则在成功时 false如果该队列已满,则返回 true
boolean offer(E e, long timeout, TimeUnit unit)在该队列的尾部插入指定的元素,等待指定的等待时间,以使空间在队列已满时变为可用
E poll()检索并删除此队列的头,如果此队列为空,则返回 null 。
E poll(long timeout, TimeUnit unit)检索并删除此队列的头,等待指定的等待时间(如有必要)使元素变为可用
void put(E e)在该队列的尾部插入指定的元素,如果队列已满,则等待空间变为可用
E take()检索并删除此队列的头,如有必要,等待元素可用
E peek()检索但不删除此队列的头,如果此队列为空,则返回 null

6.3: SynchronousQueue(同步队列)

  1. public class 同步队列 {
  2. public static void main(String[] args) {
  3. BlockingQueue<String> queue = new SynchronousQueue<>();
  4. new Thread(()->{
  5. try {
  6. System.out.println(Thread.currentThread().getName()+"执行了a操作");
  7. queue.put("a");
  8. System.out.println(Thread.currentThread().getName()+"执行了b操作");
  9. queue.put("b");
  10. System.out.println(Thread.currentThread().getName()+"执行了c操作");
  11. queue.put("c");
  12. } catch (InterruptedException e) {
  13. throw new RuntimeException(e);
  14. }
  15. },"A").start();
  16. new Thread(()->{
  17. try {
  18. TimeUnit.SECONDS.sleep(3);
  19. System.out.println(Thread.currentThread().getName()+"读取了====a操作");
  20. queue.take();
  21. TimeUnit.SECONDS.sleep(3);
  22. System.out.println(Thread.currentThread().getName()+"读取了====b操作");
  23. queue.take();
  24. TimeUnit.SECONDS.sleep(3);
  25. System.out.println(Thread.currentThread().getName()+"读取了====c操作");
  26. queue.take();
  27. } catch (InterruptedException e) {
  28. throw new RuntimeException(e);
  29. }
  30. },"B").start();
  31. }
  32. }
  33. 打印结果:
  34. A执行了a操作
  35. B读取了====a操作
  36. A执行了b操作
  37. B读取了====b操作
  38. A执行了c操作
  39. B读取了====c操作

7:线程池

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

7.1 线程池的好处

1. 降低资源的消耗

2. 提高响应的速度

3. 方便管理

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

7.1.1  3大方法

  1. public class pool {
  2. public static void main(String[] args) {
  3. ExecutorService threadPool = Executors.newCachedThreadPool();// 缓存线程池(遇强则强)
  4. ExecutorService threadPool2 = Executors.newFixedThreadPool(6);//固定线程池大小
  5. ExecutorService threadPool3 = Executors.newSingleThreadExecutor();//单一线程
  6. try {
  7. for (int i = 0; i < 100; i++) {
  8. threadPool.execute(()->{
  9. System.out.println(Thread.currentThread().getName());
  10. });
  11. }
  12. } catch (Exception e) {
  13. throw new RuntimeException(e);
  14. } finally {
  15. threadPool.shutdown();
  16. }
  17. }
  18. }

 7.1.2  7大参数

ThreadPoolExecutor
构造方法 :

  1. public ThreadPoolExecutor(int corePoolSize,
  2. int maximumPoolSize,
  3. long keepAliveTime,
  4. TimeUnit unit,
  5. BlockingQueue<Runnable> workQueue,
  6. ThreadFactory threadFactory,
  7. RejectedExecutionHandler handler)

创建一个新的 ThreadPoolExecutor与给定的初始参数。

参数
corePoolSize- (核心线程数)即使空闲时仍保留在池中的线程数,除非设置 allowCoreThreadTimeOut
maximumPoolSize- 池中允许的最大线程数
keepAliveTime- 当线程数大于内核时,这是多余的空闲线程在终止前等待新任务的最大时间
unit- keepAliveTime参数的时间单位
workQueue- 用于在执行任务之前使用的队列。 这个队列将仅保存execute方法提交的Runnable任务。(阻塞队列
threadFactory- 执行程序创建新线程时使用的工厂(线程工厂
handler- 执行被阻止时使用的处理程序,因为达到线程限制和队列容量(拒绝策略

 

  7.1.2  四种拒绝策略

RejectedExecutionHandler
ThreadPoolExecutor.AbortPolicy被拒绝的任务的处理程序,抛出一个 RejectedExecutionException 。(银行满了,还有人进来,不处理这个人的,抛出异常
ThreadPoolExecutor.CallerRunsPolicy一个被拒绝的任务的处理程序,直接在 execute方法的调用线程中运行被拒绝的任务,除非执行程序已被关闭,否则这个任务被丢弃。(哪来回哪去
ThreadPoolExecutor.DiscardOldestPolicy被拒绝的任务的处理程序,丢弃最旧的未处理请求,然后重试 execute ,除非执行程序被关闭,在这种情况下,任务被丢弃。
ThreadPoolExecutor.DiscardPolicy被拒绝的任务的处理程序静默地丢弃被拒绝的任务
7.1.2.1  代码示例:
  1. /**
  2. * new ThreadPoolExecutor.AbortPolicy() 银行满了,还有人进来,不处理这个人的,抛出异常)
  3. * new ThreadPoolExecutor.CallerRunsPolicy() 哪来的回哪去
  4. * new ThreadPoolExecutor.DiscardOldestPolicy() 队列满了不会抛出异常
  5. * new ThreadPoolExecutor.DiscardOldestPolicy() 队列满了。尝试去跟第一个线程竞争。如果没竞争过,还是回丢弃任务
  6. * 不会抛出异常
  7. */
  8. public class newPool {
  9. public static void main(String[] args) {
  10. ThreadPoolExecutor poolExecutor = new ThreadPoolExecutor(2,5,3,
  11. TimeUnit.SECONDS,
  12. new LinkedBlockingQueue<>(3),
  13. Executors.defaultThreadFactory(),
  14. new ThreadPoolExecutor.DiscardOldestPolicy());
  15. try {
  16. //最大承载:Queue + max = 5 + 3 = 8
  17. //超过RejectedExecution
  18. for (int i = 0; i < 9 ; i++) {
  19. poolExecutor.execute(()->{
  20. System.out.println(Thread.currentThread().getName());
  21. });
  22. }
  23. } catch (Exception e) {
  24. throw new RuntimeException(e);
  25. } finally {
  26. poolExecutor.shutdown();
  27. }
  28. }
  29. }

8:了解CPU密集型和IO密集型(池的大小)

  1. package main;
  2. import java.util.concurrent.*;
  3. public class ExecutorServiceDemo {
  4. public static void main(String[] args) {
  5. /*
  6. * 最大线程到底该如何定义
  7. * 1.CPU密集型,几核,最大线程就是几,可以保持CPU的效率最高
  8. * 2.IO密集型 大于你程序中十分耗IO的线程
  9. * */
  10. ExecutorService threadPool = new ThreadPoolExecutor(
  11. 2,
  12. Runtime.getRuntime().availableProcessors(),
  13. 3,
  14. TimeUnit.SECONDS,
  15. new ArrayBlockingQueue<>(3),
  16. Executors.defaultThreadFactory(),
  17. new ThreadPoolExecutor.CallerRunsPolicy());
  18. try {
  19. //最大承载:Queue + max
  20. //超过RejectedExecution
  21. for (int i = 0; i < 9; i++) {
  22. //使用线程池之后,使用线程池来创建线程
  23. threadPool.execute(()->{
  24. System.out.println(Thread.currentThread().getName()+" : ok");
  25. });
  26. }
  27. } catch (Exception e) {
  28. e.printStackTrace();
  29. } finally {
  30. threadPool.shutdown();
  31. }
  32. }
  33. }

9:四大函数式接口

9.1 Function 函数式接口

注意:传入一个T类型的参数R 返回一个参数R

  1. /*
  2. * 函数式接口:有一个输入参数,有一个输出参数
  3. * */
  4. public class function {
  5. public static void main(String[] args) {
  6. Function<String, String> function = new Function<String, String>() {
  7. @Override
  8. public String apply(String o) {
  9. return o;
  10. }
  11. };
  12. Function<String, String> function2 = (str)->{return str;};
  13. System.out.println(function2.apply("adns"));
  14. }
  15. }

9.2 Predicate 断定型接口

注意:参入一个参数,返回一个布尔类型的  true / false

  1. package fourinterface;
  2. import java.util.function.Predicate;
  3. /*
  4. * 断定型接口:有一个输入参数,返回值只能是 布尔值
  5. * */
  6. public class PredicateDemo {
  7. public static void main(String[] args) {
  8. //判断字符串是否为空
  9. // Predicate<String> predicate = new Predicate<String>() {
  10. // @Override
  11. // public boolean test(String s) {
  12. // return s.isEmpty();
  13. // }
  14. // };
  15. Predicate<String> predicate = (str)->{return str.isEmpty();};
  16. System.out.println(predicate.test(" "));
  17. }
  18. }

9.3 Consumer 消费性接口

  1. package fourinterface;
  2. import java.util.function.Consumer;
  3. /*
  4. * Consumer 消费型接口: 只有输入,没有返回值
  5. * */
  6. public class ConsumerDemo {
  7. public static void main(String[] args) {
  8. // Consumer<String> consumer = new Consumer<String>() {
  9. // @Override
  10. // public void accept(String s) {
  11. // System.out.println(s);
  12. // }
  13. // };
  14. Consumer<String> consumer = (str)->{
  15. System.out.println(str);
  16. };
  17. consumer.accept("hello");
  18. }
  19. }

9.4 Supplier 供给型接口

  1. package fourinterface;
  2. import java.util.function.Supplier;
  3. /*
  4. * Supplier 供给型接口 :没有参数 , 只有返回值
  5. * */
  6. public class SupplierDemo {
  7. public static void main(String[] args) {
  8. // Supplier supplier =new Supplier() {
  9. // @Override
  10. // public Object get() {
  11. // return 1024;
  12. // }
  13. // };
  14. Supplier supplier = ()->{return 1024;};
  15. System.out.println(supplier.get());
  16. }
  17. }

 10 Stream流式计算

方法摘要
Stream< T > filter(Predicate< ? super T > predicate)返回由与此给定谓词匹配的此流的元素组成的流
Stream< T > sorted(Comparator< ? super T > comparator)返回由该流的元素组成的流,根据提供的 Comparator进行排序
< R > Stream< R > map(Function< ? super T,? extends R > mapper)返回由给定函数应用于此流的元素的结果组成的流
void forEach(Consumer< ? super T > action)对此流的每个元素执行操作
Stream< T > limit( long maxSize )返回由此流的元素组成的流,截短长度不能超过 maxSize
  1. package fourinterface;
  2. import java.util.Arrays;
  3. import java.util.List;
  4. public class StreamDemo {
  5. public static void main(String[] args) {
  6. User u1 = new User(1,"a",21);
  7. User u2 = new User(2,"b",22);
  8. User u3 = new User(3,"c",23);
  9. User u4 = new User(4,"d",24);
  10. User u5 = new User(6,"e",25);
  11. List<User> list = Arrays.asList(u1,u2,u3,u4,u5);
  12. //计算交给Stream流
  13. //lambda表达式,链式编程,函数式接口,Stream流式计算
  14. list.stream()
  15. .filter(u->{return u.getId()%2==0;})
  16. .filter(u->{return u.getAge()>23;})
  17. .map(u->{return u.getName().toUpperCase();})
  18. .sorted((uu1,uu2)->{return uu2.compareTo(uu1);})
  19. .limit(1)
  20. .forEach(System.out::println);
  21. }
  22. }
  23. @Data
  24. @NoArgsConstructor
  25. @AllArgsConstructor
  26. public class User {
  27. private int id ;
  28. private String name;
  29. private int age ;
  30. }

 11 ForkJoin(分支合并)

特点:工作窃取

ForkJoinTask

方法摘要
ForkJoinTask < V > fork()在当前任务正在运行的池中异步执行此任务(如果适用)
V get()等待计算完成,然后检索其结果

ForkJoinPool

方法摘要
< T > ForkJoinTask< T > submit(ForkJoinTask< T > task)提交一个ForkJoinTask来执行

LongStream

方法摘要
< T > ForkJoinTask< T > submit(ForkJoinTask< T > task)提交一个ForkJoinTask来执行
LongStream parallel()返回平行的等效流
static LongStream rangeClosed(long startInclusive, long endInclusive)返回有序顺序 LongStream从 startInclusive (含)至 endInclusive通过的递增步长(含) 1
long reduce(long identity, LongBinaryOperator op)使用提供的身份值和 associative累积功能对此流的元素执行 reduction ,并返回减小的值
  1. package forkjoin;
  2. import java.util.concurrent.RecursiveTask;
  3. public class ForkJoinDemo extends RecursiveTask<Long> {
  4. private Long start;
  5. private Long end;
  6. private Long temp = 10000L;
  7. public ForkJoinDemo(Long start, Long end) {
  8. this.start = start;
  9. this.end = end;
  10. }
  11. @Override
  12. protected Long compute() {
  13. if ((end-start) < temp ){
  14. Long sum = 0L;
  15. for (Long i = start; i <= end; i++) {
  16. sum += i;
  17. }
  18. return sum;
  19. }else{
  20. long middle = (start + end)/2;//中间值
  21. ForkJoinDemo task1 = new ForkJoinDemo(start,middle);
  22. task1.fork();//拆分任务,把任务压入线程队列
  23. ForkJoinDemo task2 = new ForkJoinDemo(middle+1,end);
  24. task2.fork();//拆分任务,把任务压入线程队列
  25. return task1.join()+task2.join();
  26. }
  27. }
  28. }
  1. package forkjoin;
  2. import java.util.concurrent.ExecutionException;
  3. import java.util.concurrent.ForkJoinPool;
  4. import java.util.concurrent.ForkJoinTask;
  5. import java.util.stream.LongStream;
  6. public class Test {
  7. public static void main(String[] args) throws ExecutionException, InterruptedException {
  8. //test1();//5533
  9. //test2();//3709
  10. test3();//222
  11. }
  12. public static void test1(){
  13. Long sum = 0L;
  14. long start = System.currentTimeMillis();
  15. for (Long i = 1L; i <= 10_0000_0000L; i++) {
  16. sum += i;
  17. }
  18. long end = System.currentTimeMillis();
  19. System.out.println("sum = "+sum+" 时间 : "+(end-start));
  20. }
  21. public static void test2() throws ExecutionException, InterruptedException {
  22. long start = System.currentTimeMillis();
  23. ForkJoinPool forkJoinPool = new ForkJoinPool();
  24. ForkJoinTask<Long> task = new ForkJoinDemo(0L,10_0000_0000L);
  25. ForkJoinTask<Long> submit = forkJoinPool.submit(task);
  26. Long sum = submit.get();
  27. long end = System.currentTimeMillis();
  28. System.out.println("sum = "+sum+" 时间 : "+(end-start));
  29. }
  30. public static void test3(){
  31. long start = System.currentTimeMillis();
  32. long sum = LongStream.rangeClosed(0L,10_0000_0000L).parallel().reduce(0,Long::sum);
  33. long end = System.currentTimeMillis();
  34. System.out.println("sum = "+sum+" 时间 : "+(end-start));
  35. }
  36. }

12 异步回调

  1. package main;
  2. import java.util.concurrent.CompletableFuture;
  3. import java.util.concurrent.TimeUnit;
  4. import java.util.function.Consumer;
  5. public class Demo2 {
  6. public static void main(String[] args) throws Exception {
  7. //没有返回值的runAsync 异步回调
  8. // CompletableFuture<Void> completableFuture = CompletableFuture.runAsync(()->{
  9. // try {
  10. // TimeUnit.SECONDS.sleep(2);
  11. // } catch (InterruptedException e) {
  12. // e.printStackTrace();
  13. // }
  14. // System.out.println(Thread.currentThread().getName()+"runAsync-->Void");
  15. // });
  16. // System.out.println("1111");
  17. // completableFuture.get();//获得阻塞执行结果
  18. //有返回值的 supplyAsync 异步回调
  19. //分为成功和失败的回调
  20. //失败返回的是错误信息
  21. CompletableFuture<Integer> completableFuture = CompletableFuture.supplyAsync(()->{
  22. System.out.println(Thread.currentThread().getName()+"supplyAsync-->Integer");
  23. int i = 10/0;
  24. return 1024;
  25. });
  26. System.out.println(completableFuture.whenComplete((t,u)->{
  27. System.out.println("t-->"+t);//正常的返回结果
  28. System.out.println("u-->"+u);//错误信息
  29. }).exceptionally((e)->{
  30. System.out.println(e.getMessage());//可获取到错误的返回结果
  31. return 2233;
  32. }).get());
  33. }
  34. }

13 Volatile(可见性)

  1. package main;
  2. import java.util.concurrent.TimeUnit;
  3. public class VolatileDemo {
  4. private static volatile int num = 0;
  5. public static void main(String[] args) throws Exception {
  6. new Thread(()->{
  7. while (num == 0) {//num不加volatile无法跳出循环
  8. }
  9. }).start();
  10. TimeUnit.SECONDS.sleep(1);
  11. num=1;
  12. System.out.println(num);
  13. }
  14. }

不保证原子性

  1. package main;
  2. import java.io.PrintWriter;
  3. //volatile不保证原子性
  4. public class VolatileDemo2 {
  5. private volatile static int num = 0;
  6. public static void add(){
  7. num++;//不是原子性操作,底层分好几步
  8. }
  9. public static void main(String[] args) {
  10. //理论上num结果为2万
  11. for (int i = 0; i < 20; i++) {
  12. new Thread(()->{
  13. for (int j = 0; j < 1000; j++) {
  14. add();
  15. }
  16. }).start();
  17. }
  18. while (Thread.activeCount() > 2) {//main gc
  19. Thread.yield();
  20. }
  21. System.out.println(Thread.currentThread().getName()+" "+num);
  22. }
  23. }

不加锁保证原子性

  1. package main;
  2. import java.util.concurrent.atomic.AtomicInteger;
  3. //volatile不保证原子性
  4. public class VolatileDemo2 {
  5. //原子类的Integer
  6. private volatile static AtomicInteger num = new AtomicInteger();
  7. public static void add(){
  8. //num++;//不是原子性操作,底层分好几步
  9. num.getAndIncrement();//AtomicInteger + 1 方法 ,CAS
  10. }
  11. public static void main(String[] args) {
  12. //理论上num结果为2万
  13. for (int i = 0; i < 20; i++) {
  14. new Thread(()->{
  15. for (int j = 0; j < 1000; j++) {
  16. add();
  17. }
  18. }).start();
  19. }
  20. while (Thread.activeCount() > 2) {
  21. Thread.yield();
  22. }
  23. System.out.println(Thread.currentThread().getName()+" "+num);
  24. }
  25. }

 14 单例模式

饿汉式单例

  1. package single;
  2. //饿汉式单例
  3. public class Hungry {
  4. //可能会浪费空间
  5. private byte[] data1 = new byte[1024*1024];
  6. private byte[] data2 = new byte[1024*1024];
  7. private byte[] data3 = new byte[1024*1024];
  8. private Hungry(){
  9. }
  10. private final static Hungry HUNGRY = new Hungry();
  11. public static Hungry getInstance(){
  12. return HUNGRY;
  13. }
  14. }

懒汉式单例

  1. package single;
  2. import java.lang.reflect.Constructor;
  3. import java.lang.reflect.Field;
  4. import java.lang.reflect.InvocationTargetException;
  5. //懒汉式单例
  6. //道高一尺魔高一丈
  7. public class LazyMan {
  8. private static boolean code = false;
  9. private LazyMan(){
  10. synchronized (LazyMan.class){
  11. if (code == false){
  12. code = true;
  13. }else{
  14. throw new RuntimeException("不要试图使用反射破坏异常");
  15. }
  16. }
  17. }
  18. private volatile static LazyMan lazyMan;
  19. //双重检测锁模式的 懒汉式单例 DCL懒汉式
  20. public static LazyMan getInstance(){
  21. if (lazyMan == null) {
  22. synchronized (LazyMan.class){
  23. if (lazyMan == null) {
  24. lazyMan = new LazyMan();//不是一个原子操作
  25. }
  26. }
  27. }
  28. return lazyMan;
  29. };
  30. //多线程并发
  31. public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException, NoSuchFieldException {
  32. // for (int i = 0; i < 10; i++) {
  33. // new Thread(()->{
  34. // LazyMan.getInstance();
  35. // }).start();
  36. // }
  37. //LazyMan instance = LazyMan.getInstance();
  38. Field code = LazyMan.class.getDeclaredField("code");
  39. code.setAccessible(true);
  40. Constructor<LazyMan> declaredConstructor = LazyMan.class.getDeclaredConstructor(null);
  41. declaredConstructor.setAccessible(true);
  42. LazyMan instance2 = declaredConstructor.newInstance();
  43. code.set(instance2,false);
  44. LazyMan instance3 = declaredConstructor.newInstance();
  45. System.out.println(instance2.hashCode());
  46. System.out.println(instance3.hashCode());
  47. }
  48. }
  49. /*
  50. * 1.分配内存空间
  51. * 2.执行构造方法,初始化对象
  52. * 3.把这个对象指向这个空间
  53. * 123
  54. * 132 A
  55. * B //此时lazyman还没有完成构造
  56. * */

为什么要用两个if判断呢?用一个if加上synchronized不行吗?下面我们来分析为什么不能用一个if+synchronized。

  1. public Singleton getSingleton(){
  2. if(singleton == null){
  3. synchronized(Singleton.class){
  4. singleton = new Singleton();
  5. }
  6. }
  7. return singleton;
  8. }

上面代码中,当多个线程在等待锁的时候,第一个线程抢到锁的线程先执行了 singleton = new Singleton();此时已经创建了一个实例,即singleton !=null。执行完后第一个线程释放了锁,后面的线程抢到了锁,又去执行 singleton = new Singleton(); 又创建了一个实例。这样就破坏了单例的性质,就不是单例模式了。所以抢到锁之后还要判断下singleton是否等于空,为空时才创建对象,不为空时就不创建对象。
所以,DCL懒汉式用了2个if+synchronized来保证线程安全。

静态内部类

  1. package single;
  2. //静态内部类
  3. public class Holder {
  4. private Holder(){
  5. }
  6. public static Holder getInstance(){
  7. return InnerClass.HOLDER;
  8. };
  9. public static class InnerClass{
  10. private static final Holder HOLDER = new Holder();
  11. }
  12. }

详情见:

单例模式的4种实现方式_聪颖之夏的博客-CSDN博客保证一个类只有一个实例,并且提供一个全局访问点重量级的对象,不需要多个实例,如线程池,数据库连接池。https://blog.csdn.net/XikYu/article/details/130790707?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522168802876916800225520150%2522%252C%2522scm%2522%253A%252220140713.130102334.pc%255Fblog.%2522%257D&request_id=168802876916800225520150&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2~blog~first_rank_ecpm_v1~rank_v31_ecpm-1-130790707-null-null.268%5Ev1%5Ekoosearch&utm_term=%E5%8D%95%E4%BE%8B%E6%A8%A1%E5%BC%8F&spm=1018.2226.3001.4450

15  CAS

AS通俗的解释就是:
比较当前工作内存中的值和主内存中的值,如果相同则执行规定操作,否则继续比较直到主内存和工作内存中的值一致为止.

ABA

CAS存在的问题

CAS虽然很高效的解决原子操作,但是CAS仍然存在三大问题。ABA问题,循环时间长开销大和只能保证一个共享变量的原子操作

ABA问题。因为CAS需要在操作值的时候检查下值有没有发生变化,如果没有发生变化则更新,但是如果一个值原来是A,变成了B,又变成了A,那么使用CAS进行检查时会发现它的值没有发生变化,但是实际上却变化了。ABA问题的解决思路就是使用版本号。在变量前面追加上版本号,每次变量更新的时候把版本号加一,那么A-B-A 就会变成1A - 2B-3A。

  1. public class CASDemo {
  2. //AtomicStampedReference 注意,如果泛型是一个包装类,注意对象的引用问题
  3. // 正常在业务操作,这里面比较的都是一个个对象
  4. static AtomicStampedReference<Integer> atomicStampedReference = new AtomicStampedReference<>(1,1);
  5. // CAS compareAndSet : 比较并交换!
  6. public static void main(String[] args) {
  7. new Thread(()->{
  8. int stamp = atomicStampedReference.getStamp(); // 获得版本号
  9. System.out.println("a1=>"+stamp);
  10. try {
  11. TimeUnit.SECONDS.sleep(1);
  12. } catch (InterruptedException e) {
  13. e.printStackTrace();
  14. }
  15. Lock lock = new ReentrantLock(true);
  16. atomicStampedReference.compareAndSet(1, 2,
  17. atomicStampedReference.getStamp(), atomicStampedReference.getStamp() + 1);
  18. System.out.println("a2=>"+atomicStampedReference.getStamp());
  19. System.out.println(atomicStampedReference.compareAndSet(2, 1,
  20. atomicStampedReference.getStamp(), atomicStampedReference.getStamp() + 1));
  21. System.out.println("a3=>"+atomicStampedReference.getStamp());
  22. },"a").start();
  23. // 乐观锁的原理相同!
  24. new Thread(()->{
  25. int stamp = atomicStampedReference.getStamp(); // 获得版本号
  26. System.out.println("b1=>"+stamp);
  27. try {
  28. TimeUnit.SECONDS.sleep(2);
  29. } catch (InterruptedException e) {
  30. e.printStackTrace();
  31. }
  32. System.out.println(atomicStampedReference.compareAndSet(1, 6,
  33. stamp, stamp + 1));
  34. System.out.println("b2=>"+atomicStampedReference.getStamp());
  35. },"b").start();
  36. }
  37. }

16 可重入锁

17 自旋锁

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/Li_阴宅/article/detail/966788
推荐阅读
相关标签
  

闽ICP备14008679号