并发包下锁的一些应用

前言:在并发中,最熟悉的JUC编程,问的最多的也是下面四个包,随手写篇博客记录日常中的学习一下!

 一、读写锁

java.util.concurrent.locks.ReentrantReadWriteLock;

  ReadWriteLock管理一组锁,一个是只读的锁,一个是写锁。读锁可以在没有写锁的时候被多个线程同时持有,写锁是独占的。
所有读写锁的实现必须确保写操作对读操作的内存影响。换句话说,一个获得了读锁的线程必须能看到前一个释放的写锁所更新的内容。
读写锁比互斥锁允许对于共享数据更大程度的并发。每次只能有一个写线程,但是同时可以有多个线程并发地读数据。ReadWriteLock适用于读多写少的并发情况。

  举个例子,有六个线程进行读写操作,但是呢,我们在写入数据的时候是并不希望有别的线程来加塞,我们在读取的时候可以多个线程进行读取:

 1 import java.util.HashMap;
 2 import java.util.Map;
 3 import java.util.concurrent.TimeUnit;
 4 import java.util.concurrent.locks.ReadWriteLock;
 5 import java.util.concurrent.locks.ReentrantReadWriteLock;
 6 
 7 /**
 8  * @author zhangzhixi
 9  * @date 2021-4-18 22:25
10  */
11 public class Demo_01_读写锁 {
12     public static void main(String[] args) {
13         MyCache myCache = new MyCache();
14         // 六个写的线程
15         for (int i = 1; i <= 6; i++) {
16             final int temp = i;
17             new Thread(() -> {
18                 myCache.put(temp + "", temp + "");
19             }, String.valueOf(i)).start();
20         }
21         // 六个读的线程
22         for (int i = 1; i <= 6; i++) {
23             final int temp = i;
24             new Thread(() -> {
25                 myCache.get(temp + "");
26             }, String.valueOf(i)).start();
27         }
28     }
29 }
30 
31 /**
32  * 定义缓存类
33  */
34 class MyCache {
35     volatile Map<String, Object> map = new HashMap<>();
36 
37     public void put(String key, String value) {
38         System.out.println(Thread.currentThread().getName() + "\t 正在写入" + key);
39         // 模拟网络延迟
40         try {
41             TimeUnit.MICROSECONDS.sleep(300);
42         } catch (InterruptedException e) {
43             e.printStackTrace();
44         }
45         map.put(key, value);
46         System.out.println(Thread.currentThread().getName() + "\t 写入完成");
47     }
48 
49     public void get(String key) {
50             // 模拟网络延迟
51             try {
52                 TimeUnit.MICROSECONDS.sleep(200);
53             } catch (InterruptedException e) {
54                 e.printStackTrace();
55             }
56             Object value = map.get(key);
57             System.out.println(Thread.currentThread().getName() + "\t 读取完成->" + value);
58     }
59 }

通过上面的测试可以看到,线程在写入数据的时候,有其他线程过来加塞。

改进后的读写锁代码:

 1 import java.util.HashMap;
 2 import java.util.Map;
 3 import java.util.concurrent.TimeUnit;
 4 import java.util.concurrent.locks.ReadWriteLock;
 5 import java.util.concurrent.locks.ReentrantReadWriteLock;
 6 
 7 /**
 8  * @author zhangzhixi
 9  * @date 2021-4-18 22:25
10  */
11 public class Demo_01_读写锁 {
12     public static void main(String[] args) {
13         MyCache myCache = new MyCache();
14         // 六个写的线程
15         for (int i = 1; i <= 6; i++) {
16             final int temp = i;
17             new Thread(() -> {
18                 myCache.put(temp + "", temp + "");
19             }, String.valueOf(i)).start();
20         }
21         // 六个读的线程
22         for (int i = 1; i <= 6; i++) {
23             final int temp = i;
24             new Thread(() -> {
25                 myCache.get(temp + "");
26             }, String.valueOf(i)).start();
27         }
28     }
29 }
30 
31 /**
32  * 定义缓存类
33  */
34 class MyCache {
35     volatile Map<String, Object> map = new HashMap<>();
36     ReadWriteLock lock = new ReentrantReadWriteLock();
37     public void put(String key, String value) {
38         // 写入数据
39         lock.writeLock().lock();
40         try {
41             System.out.println(Thread.currentThread().getName() + "\t 正在写入" + key);
42             // 模拟网络延迟
43             try {
44                 TimeUnit.MICROSECONDS.sleep(300);
45             } catch (InterruptedException e) {
46                 e.printStackTrace();
47             }
48             map.put(key, value);
49             System.out.println(Thread.currentThread().getName() + "\t 写入完成");
50         } catch (Exception e) {
51             e.printStackTrace();
52         } finally {
53             lock.writeLock().unlock();
54         }
55     }
56 
57     public void get(String key) {
58         lock.readLock().lock();
59         try {
60             System.out.println(Thread.currentThread().getName() + "\t 正在读取" + key);
61             // 模拟网络延迟
62             try {
63                 TimeUnit.MICROSECONDS.sleep(200);
64             } catch (InterruptedException e) {
65                 e.printStackTrace();
66             }
67             Object value = map.get(key);
68             System.out.println(Thread.currentThread().getName() + "\t 读取完成->" + value);
69         } catch (Exception e) {
70             e.printStackTrace();
71         } finally {
72             lock.readLock().unlock();
73         }
74     }
75 }

 

 二、倒计时锁存器:秦一统六国实例

java.util.concurrent.CountDownLatch;

  CountDownLatch主要有两个方法,当一个或多个线程调用await方法时,调用线程会被阻塞。
其它线程调用countDown方法会将计数器减1(调用countDown方法的线程不会阻塞),
当计数器的值变为零时,因调用await方法被阻塞的线程会被唤醒,继续执行。

  例子:班级放学后班长最后一个走人,班长锁门

import java.util.concurrent.CountDownLatch;

/**
 * @author zhangzhixi
 * @date 2021-4-18 23:15
 */
public class Demo_02_倒计时锁存器 {
    public static void main(String[] args) throws InterruptedException {
        CountDownLatch countDownLatch = new CountDownLatch(6);
        for (int i = 1; i <= 6; i++) {
            new Thread(()->{
                System.out.println(Thread.currentThread().getName() + "\t 上完自习离开教室");
                countDownLatch.countDown();
            },String.valueOf(i)).start();
        }
        countDownLatch.await();
        System.out.println("班长最后离开锁门~");
    }
}


CountDownLatch实例之枚举的应用

秦国一统天下,歼灭各国怎么用代码表示呢?

 

 下面使用的是枚举的方式定义线程的名称:

 1 import java.util.concurrent.CountDownLatch;
 2 
 3 /**
 4  * @author zhangzhixi
 5  * @date 2021-4-19 9:19
 6  */
 7 public class Demo_03_倒计时锁存器之枚举的应用 {
 8     public static void main(String[] args) throws InterruptedException {
 9         // 倒计时计数器
10         CountDownLatch countDownLatch = new CountDownLatch(6);
11 
12         for (int i = 1; i <= 6; i++) {
13             new Thread(() -> {
14                 // 减少寄存器的数量
15                 countDownLatch.countDown();
16                 System.out.println(Thread.currentThread().getName() + "国,被灭了");
17             }, CountEnum.forEachEnum(i).getName()).start();
18         }
19 
20         // 导致当前线程等待,直到锁存器递减计数到零为止,除非该线程被中断
21         countDownLatch.await();
22         System.out.println("秦国一统天下~");
23     }
24 }
25 
26 enum CountEnum {
27     ONE(1, "齐"), TWO(2, "楚"), THREE(3, "燕"), FORE(4, "韩"), FIVE(5, "赵"), SIX(6, "魏");
28 
29     private Integer id;
30     private String name;
31 
32     CountEnum(Integer id, String name) {
33         this.id = id;
34         this.name = name;
35     }
36 
37     public Integer getId() {
38         return id;
39     }
40 
41     public String getName() {
42         return name;
43     }
44 
45     public static CountEnum forEachEnum(int index) {
46         // 以声明顺序返回包含此枚举类型的常量的数组
47         CountEnum[] values = CountEnum.values();
48         for (CountEnum element : values) {
49             if (index == element.getId()) {
50                 return element;
51             }
52         }
53         return null;
54     }
55 }

 

 三、循环屏障:七龙珠实例

 1 import java.util.concurrent.BrokenBarrierException;
 2 import java.util.concurrent.CyclicBarrier;
 3 
 4 /**
 5  * @author zhangzhixi
 6  * @date 2021-4-19 22:28
 7  */
 8 public class Demo_04_循环屏障之七龙珠 {
 9     public static void main(String[] args) {
10         // 集齐七颗龙珠召唤神龙
11         CyclicBarrier barrier = new CyclicBarrier(7, () -> {
12             System.out.println("=========收集到了神龙===========");
13         });
14         for (int i = 1; i <= 7; i++) {
15             final int emp = i;
16             new Thread(() -> {
17                 System.out.println("集齐到了第" + emp + "颗龙珠");
18                 try {
19                     // 等到所有各方都在此障碍上调用await 
20                     barrier.await();
21                 } catch (InterruptedException e) {
22                     e.printStackTrace();
23                 } catch (BrokenBarrierException e) {
24                     e.printStackTrace();
25                 }
26             }, String.valueOf(i)).start();
27         }
28 
29     }
30 }

 

 四、信号灯Semaphore:抢车位实例

 1 import java.util.concurrent.Semaphore;
 2 import java.util.concurrent.TimeUnit;
 3 
 4 /**
 5  * @author zhangzhixi
 6  * @date 2021-4-19 22:38
 7  */
 8 public class Demo05_信号量_抢车位 {
 9     public static void main(String[] args) {
10         // 给了三个车位
11         Semaphore semaphore = new Semaphore(3);
12 
13         // 模拟6个车子
14         for (int i = 1; i <= 6; i++) {
15             new Thread(() -> {
16                 // 抢到车位(抢占)
17                 try {
18                     semaphore.acquire();
19                     System.out.println(Thread.currentThread().getName() + "\t 号车子抢到抢到了");
20                     // 模拟停车时间
21                     try {
22                         TimeUnit.SECONDS.sleep(3);
23                     } catch (InterruptedException e) {
24                         e.printStackTrace();
25                     }
26                     System.out.println(Thread.currentThread().getName() + "\t 号车子停车2S后离开车位");
27                 } catch (InterruptedException e) {
28                     e.printStackTrace();
29                 } finally {
30                     // 车子离开车位,
31                     semaphore.release();
32                 }
33             }, String.valueOf(i)).start();
34         }
35     }
36 }

 

posted @ 2021-04-19 11:18  Java小白的搬砖路  阅读(88)  评论(0编辑  收藏  举报