1.采用synchronized解决线程并发问题

  1. public class SaleTickDemo01 {
  2. public static void main(String[] args) {
  3. Ticket ticket = new Ticket();
  4. new Thread(() -> {
  5. for (int i = 0; i < 60; i++) {
  6. ticket.sale();
  7. }
  8. }, "A").start();
  9. new Thread(() -> {
  10. for (int i = 0; i < 60; i++) {
  11. ticket.sale();
  12. }
  13. }, "B").start();
  14. new Thread(() -> {
  15. for (int i = 0; i < 60; i++) {
  16. ticket.sale();
  17. }
  18. }, "C").start();
  19. }
  20. }
  21. class Ticket {
  22. private int number = 50;
  23. // synchronized
  24. public void sale() {
  25. if (number > 0) {
  26. System.out.println(Thread.currentThread().getName() + "卖出了" + (number--) + "票,剩余:" + number);
  27. }
  28. }
  29. }

没有加synchronized执行的结果如下:
image.png
给sale方法添加synchronized后的结果
image.png

2.采用Lock解决线程并发问题

  1. public class SaleTickDemo02 {
  2. public static void main(String[] args) {
  3. // 并发:多线程操作同一个资源类,把资源类丢入线程
  4. Ticket2 ticket = new Ticket2();
  5. new Thread(() -> {for (int i = 0; i < 60; i++) ticket.sale();} , "A").start();
  6. new Thread(() -> {for (int i = 0; i < 60; i++) ticket.sale();} , "B").start();
  7. new Thread(() -> {for (int i = 0; i < 60; i++) ticket.sale();} , "C").start();
  8. }
  9. }
  10. // Lock三部曲
  11. // 1.Lock lock = new ReentrantLock();
  12. // 2.lock.lock(); 加锁
  13. // 3.lock.unlock(); 解锁
  14. class Ticket2 {
  15. private int number = 50;
  16. Lock lock = new ReentrantLock();
  17. public void sale() {
  18. lock.lock();
  19. try {
  20. if (number > 0) {
  21. System.out.println(Thread.currentThread().getName() + "卖出了" + (number--) + "票,剩余:" + number);
  22. }
  23. } catch (Exception e) {
  24. e.printStackTrace();
  25. } finally {
  26. lock.unlock();
  27. }
  28. }
  29. }

执行出来的效果与demo1一致

3.传统消费者生产者问题

3.1 synchronized

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

如上这段代码使用if的话会引发 虚假唤醒 ,等待应该总是出现在循环中
image.png
将if换成while后代码执行结果正常
image.png

3.2 Lock

  1. /**
  2. * 线程中间的通讯问题:生产者和消费者问题!
  3. * 线程交替执行 A V 操作同一个变量 num = 0
  4. * A num+1
  5. * B num-1
  6. */
  7. public class LockPc {
  8. public static void main(String[] args) {
  9. Data2 data = new Data2();
  10. new Thread(() -> {
  11. for (int i = 0; i < 10; i++) {
  12. try {
  13. data.increment();
  14. } catch (InterruptedException e) {
  15. e.printStackTrace();
  16. }
  17. }
  18. }, "A").start();
  19. new Thread(() -> {
  20. for (int i = 0; i < 10; i++) {
  21. try {
  22. data.decrement();
  23. } catch (InterruptedException e) {
  24. e.printStackTrace();
  25. }
  26. }
  27. }, "B").start();
  28. new Thread(() -> {
  29. for (int i = 0; i < 10; i++) {
  30. try {
  31. data.increment();
  32. } catch (InterruptedException e) {
  33. e.printStackTrace();
  34. }
  35. }
  36. }, "C").start();
  37. new Thread(() -> {
  38. for (int i = 0; i < 10; i++) {
  39. try {
  40. data.decrement();
  41. } catch (InterruptedException e) {
  42. e.printStackTrace();
  43. }
  44. }
  45. }, "D").start();
  46. }
  47. }
  48. class Data2 {
  49. private int number = 0;
  50. Lock lock = new ReentrantLock();
  51. Condition condition = lock.newCondition();
  52. //+1
  53. public void increment() throws InterruptedException {
  54. lock.lock();
  55. try {
  56. while (number != 0) {
  57. condition.await();
  58. }
  59. number++;
  60. System.out.println(Thread.currentThread().getName() + "=>" + number);
  61. // 通知其它线程,我+1完毕了
  62. condition.signalAll();
  63. } catch (InterruptedException e) {
  64. e.printStackTrace();
  65. } finally {
  66. lock.unlock();
  67. }
  68. }
  69. //-1
  70. public void decrement() throws InterruptedException {
  71. lock.lock();
  72. try {
  73. while (number == 0) {
  74. condition.await();
  75. }
  76. number--;
  77. System.out.println(Thread.currentThread().getName() + "=>" + number);
  78. // 通知其它线程,我-1完毕了
  79. condition.signalAll();
  80. } catch (Exception e) {
  81. e.printStackTrace();
  82. } finally {
  83. lock.unlock();
  84. }
  85. }
  86. }

执行的结果与sync一致
image.png

3.3 Lock 精准通知实现一条生产线

A->B->C->A 形成一个循环的通知

  1. /**
  2. * 线程中间的通讯问题:生产者和消费者问题!
  3. * 线程交替执行 A B 操作同一个变量 num = 0
  4. * A->B->C->A 形成一个循环的通知
  5. */
  6. public class LockPcOrder {
  7. public static void main(String[] args) {
  8. Data3 data3 = new Data3();
  9. new Thread(() -> {
  10. for (int i = 0; i < 10; i++) {
  11. data3.printA();
  12. }
  13. }, "A").start();
  14. new Thread(() -> {
  15. for (int i = 0; i < 10; i++) {
  16. data3.printB();
  17. }
  18. }, "B").start();
  19. new Thread(() -> {
  20. for (int i = 0; i < 10; i++) {
  21. data3.printC();
  22. }
  23. }, "C").start();
  24. }
  25. }
  26. class Data3 {
  27. private int number = 1;
  28. Lock lock = new ReentrantLock();
  29. Condition condition1 = lock.newCondition();
  30. Condition condition2 = lock.newCondition();
  31. Condition condition3 = lock.newCondition();
  32. Condition condition4 = lock.newCondition();
  33. public void printA() {
  34. lock.lock();
  35. try {
  36. // 业务,判断->执行->通知
  37. while (number != 1) {
  38. // 等待
  39. condition1.await();
  40. }
  41. System.out.println(Thread.currentThread().getName() + "=>AAAAAAAA");
  42. // 唤醒指定的人,B
  43. number = 2;
  44. condition2.signal();
  45. } catch (Exception e) {
  46. e.printStackTrace();
  47. } finally {
  48. lock.unlock();
  49. }
  50. }
  51. public void printB() {
  52. lock.lock();
  53. try {
  54. // 业务,判断->执行->通知
  55. while (number != 2) {
  56. // 等待
  57. condition2.await();
  58. }
  59. System.out.println(Thread.currentThread().getName() + "=>BBBBBBB");
  60. // 唤醒指定的人,C
  61. number = 3;
  62. condition3.signal();
  63. } catch (Exception e) {
  64. e.printStackTrace();
  65. } finally {
  66. lock.unlock();
  67. }
  68. }
  69. public void printC() {
  70. lock.lock();
  71. try {
  72. // 业务,判断->执行->通知
  73. while (number != 3) {
  74. // 等待
  75. condition3.await();
  76. }
  77. System.out.println(Thread.currentThread().getName() + "=>CCCCCCC");
  78. // 唤醒指定的人,A
  79. number = 1;
  80. condition1.signal();
  81. } catch (Exception e) {
  82. e.printStackTrace();
  83. } finally {
  84. lock.unlock();
  85. }
  86. }
  87. }

4.八大锁现象

问题1:请问先打印 发短信还是打电话
答:两个方法用的是同一个锁,谁先拿到执行谁
问题2:sendSms加了4秒睡眠后 先打印 发短信还是打电话
答:两个方法用的是同一个锁,谁先拿到执行谁

  1. public class Test1 {
  2. public static void main(String[] args) {
  3. Phone phone = new Phone();
  4. new Thread(phone::sendSms, "A").start();
  5. try {
  6. TimeUnit.SECONDS.sleep(1);
  7. } catch (InterruptedException e) {
  8. e.printStackTrace();
  9. }
  10. new Thread(phone::call, "B").start();
  11. }
  12. }
  13. class Phone {
  14. // synchronize 锁的对象是方法的调用者
  15. // 两个方法用的是同一个锁,谁先拿到执行谁
  16. synchronized void sendSms() {
  17. try {
  18. TimeUnit.SECONDS.sleep(4);
  19. } catch (InterruptedException e) {
  20. e.printStackTrace();
  21. }
  22. System.out.println("发短信");
  23. }
  24. synchronized void call() {
  25. System.out.println("打电话");
  26. }
  27. }

问题3:先执行hello还是 打短信?hello是普通方法
答:先打印hello,因为它不是同步方法,不受锁的影响

  1. public class Test2 {
  2. public static void main(String[] args) {
  3. Phone2 phone = new Phone2();
  4. new Thread(phone::sendSms, "A").start();
  5. try {
  6. TimeUnit.SECONDS.sleep(1);
  7. } catch (InterruptedException e) {
  8. e.printStackTrace();
  9. }
  10. new Thread(phone::hello, "B").start();
  11. }
  12. }
  13. class Phone2 {
  14. // synchronize 锁的对象是方法的调用者
  15. // 两个方法用的是同一个锁,谁先拿到执行谁
  16. synchronized void sendSms() {
  17. try {
  18. TimeUnit.SECONDS.sleep(4);
  19. } catch (InterruptedException e) {
  20. e.printStackTrace();
  21. }
  22. System.out.println("发短信");
  23. }
  24. synchronized void call() {
  25. System.out.println("打电话");
  26. }
  27. // 这里没有锁!不是同步方法,不受锁的影响
  28. void hello(){
  29. System.out.println("hello");
  30. }
  31. }

问题4:两个Phone的情况下先执行sendSms还是call
答:两把锁根据时间来所以先调用call,因为phone的sendSms等待了4秒,所以phone1先获得锁

  1. public class Test2 {
  2. public static void main(String[] args) {
  3. // 两个对象,两个调用者,两把锁!
  4. Phone2 phone = new Phone2();
  5. Phone2 phone1 = new Phone2();
  6. new Thread(phone::sendSms, "A").start();
  7. try {
  8. TimeUnit.SECONDS.sleep(1);
  9. } catch (InterruptedException e) {
  10. e.printStackTrace();
  11. }
  12. new Thread(phone1::call, "B").start();
  13. }
  14. }
  15. class Phone2 {
  16. // synchronize 锁的对象是方法的调用者
  17. // 两个方法用的是同一个锁,谁先拿到执行谁
  18. synchronized void sendSms() {
  19. try {
  20. TimeUnit.SECONDS.sleep(4);
  21. } catch (InterruptedException e) {
  22. e.printStackTrace();
  23. }
  24. System.out.println("发短信");
  25. }
  26. synchronized void call() {
  27. System.out.println("打电话");
  28. }
  29. // 这里没有锁!不是同步方法,不受锁的影响
  30. void hello(){
  31. System.out.println("hello");
  32. }
  33. }

问题5:增加两个静态的同步方法,只有一个对象,先打印 发短信?打电话?
答:Phone2只会有一个class对象,所以谁先调用就给谁锁,sendSms->call

  1. public class Test2 {
  2. public static void main(String[] args) {
  3. Phone2 phone = new Phone2();
  4. new Thread(()->{
  5. phone.sendSms();
  6. } ,"A").start();
  7. try {
  8. TimeUnit.SECONDS.sleep(1);
  9. } catch (InterruptedException e) {
  10. e.printStackTrace();
  11. }
  12. new Thread(()->{
  13. phone.call();
  14. }, "B").start();
  15. }
  16. }
  17. class Phone2 {
  18. // synchronize 锁的对象是方法的调用者
  19. // static 静态方法
  20. // 嘞一加载就有了!锁的是class
  21. static synchronized void sendSms() {
  22. try {
  23. TimeUnit.SECONDS.sleep(4);
  24. } catch (InterruptedException e) {
  25. e.printStackTrace();
  26. }
  27. System.out.println("发短信");
  28. }
  29. static synchronized void call() {
  30. System.out.println("打电话");
  31. }
  32. // 这里没有锁!不是同步方法,不受锁的影响
  33. void hello(){
  34. System.out.println("hello");
  35. }
  36. }

问题6:两个静态的同步方法,两个对象,先打印 发短信?打电话?
答:Phone2只会有一个class对象,所以谁先调用就给谁锁,sendSms->call

  1. public class Test3 {
  2. public static void main(String[] args) {
  3. Phone3 phone = new Phone3();
  4. Phone3 phone1 = new Phone3();
  5. new Thread(()->{
  6. phone.sendSms();
  7. } ,"A").start();
  8. try {
  9. TimeUnit.SECONDS.sleep(1);
  10. } catch (InterruptedException e) {
  11. e.printStackTrace();
  12. }
  13. new Thread(()->{
  14. phone1.call();
  15. }, "B").start();
  16. }
  17. }
  18. // Phone3 唯一的一个class对象
  19. class Phone3 {
  20. // synchronize 锁的对象是方法的调用者
  21. // static 静态方法
  22. // 类一加载就有了!锁的是class
  23. static synchronized void sendSms() {
  24. try {
  25. TimeUnit.SECONDS.sleep(4);
  26. } catch (InterruptedException e) {
  27. e.printStackTrace();
  28. }
  29. System.out.println("发短信");
  30. }
  31. static synchronized void call() {
  32. System.out.println("打电话");
  33. }
  34. // 这里没有锁!不是同步方法,不受锁的影响
  35. void hello(){
  36. System.out.println("hello");
  37. }
  38. }

问题7:一个静态同步方法,一个普通同步方法,一个对象,发短信?打电话
答:静态的同步方法锁的是class的模板,普通的同步方法,锁的调用者 call->sendSms
问题8:一个静态同步方法,一个普通同步方法,两个个对象,发短信?打电话
答:静态的同步方法锁的是class的模板,普通的同步方法,锁的调用者 call->sendSms

  1. public class Test4 {
  2. public static void main(String[] args) {
  3. Phone4 phone = new Phone4();
  4. new Thread(()->{
  5. phone.sendSms();
  6. } ,"A").start();
  7. try {
  8. TimeUnit.SECONDS.sleep(1);
  9. } catch (InterruptedException e) {
  10. e.printStackTrace();
  11. }
  12. new Thread(()->{
  13. phone.call();
  14. }, "B").start();
  15. }
  16. }
  17. // Phone4 唯一的一个class对象
  18. class Phone4 {
  19. // 静态的同步方法锁的是class的模板
  20. static synchronized void sendSms() {
  21. try {
  22. TimeUnit.SECONDS.sleep(4);
  23. } catch (InterruptedException e) {
  24. e.printStackTrace();
  25. }
  26. System.out.println("发短信");
  27. }
  28. // 普通的同步方法,锁的调用者
  29. static void call() {
  30. System.out.println("打电话");
  31. }
  32. }

5.集合类不安全

5.1 ArrayList

执行如下这段代码会发生java.util.ConcurrentModificationException(并发异常)

  1. public class ListTest {
  2. public static void main(String[] args) {
  3. // 并发下 ArrayList 不安全的吗,Synchronized;
  4. /*
  5. * 解决方案;
  6. * 1、List<String> list = new Vector<>();
  7. * 2、List<String> list = Collections.synchronizedList(new ArrayList<>());
  8. * 3、List<String> list = new CopyOnWriteArrayList<>();
  9. */
  10. List<String> list = new ArrayList<>();
  11. for (int i = 0; i < 20; i++) {
  12. new Thread(() -> {
  13. list.add(UUID.randomUUID().toString().substring(0, 5));
  14. System.out.println(list);
  15. }, String.valueOf(i)).start();
  16. }
  17. }
  18. }

5.2 HashSet

执行如下这段代码会发生java.util.ConcurrentModificationException(并发异常)
HashSet底层是调用HashMap,add set本质就是map key是无法重复的

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

5.3 HashMap

换成new HashMap<>()执行如下这段代码会发生java.util.ConcurrentModificationException(并发异常)

  1. public class MapTest {
  2. public static void main(String[] args) {
  3. // map 是这样用的吗? 不是,工作中不用 HashMap
  4. // 默认等价于什么? new HashMap<>(16,0.75);
  5. // Map<String, String> map = new HashMap<>();
  6. Map<String, String> map = new ConcurrentHashMap<>();
  7. for (int i = 1; i <=30; i++) {
  8. new Thread(()->{
  9. map.put(Thread.currentThread().getName(),UUID.randomUUID().toString().substring(0,5));
  10. System.out.println(map);
  11. },String.valueOf(i)).start();
  12. }
  13. }
  14. }

6.Callable 可获得执行任务返回值

  1. public class CallableTest {
  2. public static void main(String[] args) throws ExecutionException, InterruptedException {
  3. // new Thread(new Runnable()).start();
  4. // new Thread(new FutureTask<V>()).start();
  5. // new Thread(new FutureTask<V>( Callable )).start();
  6. new Thread().start(); // 怎么启动Callable
  7. MyThread thread = new MyThread();
  8. FutureTask<Integer> futureTask = new FutureTask<>(thread); // 适配类
  9. new Thread(futureTask,"A").start();
  10. new Thread(futureTask,"B").start(); // 结果会被缓存,效率高
  11. Integer o = futureTask.get(); //这个get 方法可能会产生阻塞!把他放到最后
  12. // 或者使用异步通信来处理!
  13. System.out.println(o);
  14. }
  15. }
  16. class MyThread implements Callable<Integer> {
  17. @Override
  18. public Integer call() {
  19. System.out.println("call()"); // 会打印几个call
  20. // 耗时的操作
  21. return 1024;
  22. }
  23. }

7.CountDownLatch 线程减法计数器

线程减法计数器,需要全部线程执行完后再输入出Close Door

  1. // 计数器
  2. public class CountDownLatchDemo {
  3. public static void main(String[] args) throws InterruptedException {
  4. // 总数是6,必须要执行任务的时候,再使用!
  5. CountDownLatch countDownLatch = new CountDownLatch(6);
  6. for (int i = 1; i <=6 ; i++) {
  7. new Thread(()->{
  8. System.out.println(Thread.currentThread().getName()+" Go out");
  9. countDownLatch.countDown(); // 数量-1
  10. },String.valueOf(i)).start();
  11. }
  12. countDownLatch.await(); // 等待计数器归零,然后再向下执行
  13. System.out.println("Close Door");
  14. }
  15. }

8.CyclicBarrier 线程加法计数器

线程加法计数器,线程数到8时才打印内容

  1. public class CyclicBarrierDemo {
  2. public static void main(String[] args) {
  3. /*
  4. * 集齐7颗龙珠召唤神龙
  5. */
  6. // 召唤龙珠的线程
  7. CyclicBarrier cyclicBarrier = new CyclicBarrier(8, () -> {
  8. System.out.println("召唤神龙成功!");
  9. });
  10. for (int i = 1; i <= 7; i++) {
  11. final int temp = i;
  12. // lambda能操作到 i 吗
  13. new Thread(() -> {
  14. System.out.println(Thread.currentThread().getName() + "收集" + temp + "个龙珠");
  15. try {
  16. cyclicBarrier.await(); // 等待
  17. } catch (InterruptedException | BrokenBarrierException e) {
  18. e.printStackTrace();
  19. }
  20. }).start();
  21. }
  22. }
  23. }

9.Semaphore 限流

  1. public class SemaphoreDemo {
  2. public static void main(String[] args) {
  3. // 线程数量:停车位! 限流!
  4. Semaphore semaphore = new Semaphore(3);
  5. for (int i = 1; i <=6 ; i++) {
  6. new Thread(()->{
  7. // acquire() 得到
  8. try {
  9. semaphore.acquire();
  10. System.out.println(Thread.currentThread().getName()+"抢到车位");
  11. TimeUnit.SECONDS.sleep(2);
  12. System.out.println(Thread.currentThread().getName()+"离开车位");
  13. } catch (InterruptedException e) {
  14. e.printStackTrace();
  15. } finally {
  16. semaphore.release(); // release() 释放
  17. }
  18. },String.valueOf(i)).start();
  19. }
  20. }
  21. }

10.ReadWriteLock 读写锁

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

加锁与没加锁效果对比:
image.png image.png

11.