公平锁

是指多个线程按照申请的顺序来获取锁,类似于排队买车票;先来后到的原则。

非公平锁

是指多个线程获取锁的顺序并不是按照申请锁的顺序,有可能是后申请的线程比先申请的线程优先获取到锁,在高并发的情况下,有可能会造成优先级反转或者饥饿现象

可重入锁(也叫递归锁)

指的是同一线程外层函数获得所之后,内层递归函数任然能获取该锁的代码,在同一个线程外层方法获取的时候,在进入内层方法会自动获取锁

也就是说线程可以进入任何一个它已经拥有的锁所同步着的代码块(ReentrantLock和Synchronize 就是一个典型的非公平重入锁)

 

ReentrantLock和Synchronize 就是一个典型的非公平重入锁,上代码:

  1 package com.company.lock.demo;
  2 
  3 import java.util.concurrent.TimeUnit;
  4 import java.util.concurrent.locks.Lock;
  5 import java.util.concurrent.locks.ReentrantLock;
  6 
  7 /**
  8  * (ReentrantLock和 Synchronize 就是一个典型的非公平重入锁)
  9  * <p>
 10  * Synchronize 就是一个典型的非公平重入锁
 11  * <p>
 12  * t1    发送短信
 13  * t1    发送邮件(发送邮件的方法也是加锁的,但 t1 线程直接获取,而且是同一把锁,说明 Synchronize 就是一个典型的重入锁 )
 14  * t2    发送短信
 15  * t2    发送邮件(发送邮件的方法也是加锁的,但 t2 线程直接获取,而且是同一把锁,说明 Synchronize 就是一个典型的重入锁 )
 16  * <p>
 17  * <p>
 18  * ReentrantLock 也是一个典型的非公平重入锁
 19  * <p>
 20  * t3    get() 发送短信
 21  * t3    set() 发送邮件(发送邮件的方法也是加锁的,但 t3 线程直接获取,而且是同一把锁,说明 ReentrantLock 就是一个典型的重入锁 )
 22  * t4    get() 发送短信
 23  * t4    set() 发送邮件(发送邮件的方法也是加锁的,但 t4 线程直接获取,而且是同一把锁,说明 ReentrantLock 就是一个典型的重入锁 )
 24  * <p>
 25  * <p>
 26  * 可重入锁的最大作用: 避免死锁
 27  */
 28 
 29 class Phone implements Runnable {
 30     /**
 31      * Synchronize 就是一个典型的非公平重入锁
 32      *
 33      * @throws Exception
 34      */
 35     public synchronized void sendMessages() throws Exception {
 36         System.out.println(Thread.currentThread().getName() + "\t" + "发送短信");
 37         sendEmail();
 38     }
 39 
 40     public synchronized void sendEmail() throws Exception {
 41         System.out.println(Thread.currentThread().getName() + "\t" + "发送邮件");
 42     }
 43 
 44 
 45     /**
 46      * ReentrantLock 也是一个典型的非公平重入锁
 47      */
 48 
 49     Lock lock = new ReentrantLock();//默认参数为 false ,即非公平重入锁
 50 
 51     @Override
 52     public void run() {
 53         get();
 54     }
 55 
 56     private void get() {
 57         lock.lock();
 58         try {
 59             System.out.println(Thread.currentThread().getName() + "\t" + "get() 发送短信");
 60             set();
 61         } catch (Exception e) {
 62             e.printStackTrace();
 63         } finally {
 64             lock.unlock();
 65         }
 66     }
 67 
 68     private void set() {
 69         lock.lock();
 70         try {
 71             System.out.println(Thread.currentThread().getName() + "\t" + "set() 发送邮件");
 72             call();
 73         } catch (Exception e) {
 74             e.printStackTrace();
 75         } finally {
 76             lock.unlock();
 77         }
 78     }
 79 
 80     private void call() {
 81         lock.lock();
 82         try {
 83             System.out.println(Thread.currentThread().getName() + "\t" + "call() 打个电话");
 84         } catch (Exception e) {
 85             e.printStackTrace();
 86         } finally {
 87             lock.unlock();
 88         }
 89     }
 90 }
 91 
 92 public class ReentrantLockDemo {
 93     public static void main(String[] args) {
 94         Phone phone = new Phone();
 95         new Thread(() -> {
 96             try {
 97                 phone.sendMessages();
 98             } catch (Exception e) {
 99                 e.printStackTrace();
100             }
101         }, "t1").start();
102         new Thread(() -> {
103             try {
104                 phone.sendMessages();
105             } catch (Exception e) {
106                 e.printStackTrace();
107             }
108         }, "t2").start();
109 
110         new Thread(() -> {
111             try {
112                 phone.sendMessages();
113             } catch (Exception e) {
114                 e.printStackTrace();
115             } finally {
116             }
117         }, "t5").start();
118         try {
119             TimeUnit.SECONDS.sleep(1);
120         } catch (InterruptedException e) {
121             e.printStackTrace();
122         }
123 
124         System.out.println("-------------------------------");
125         System.out.println("-------------------------------");
126         System.out.println("-------------------------------");
127         System.out.println("-------------------------------");
128 
129         Thread t3 = new Thread(phone, "t3");
130         Thread t4 = new Thread(phone, "t4");
131         t3.start();
132         t4.start();
133     }
134 }
View Code

自旋锁

是指尝试获取锁的线程不会立即阻塞,而是采用循环的方式去尝试获取锁,这样的好处是减少线程上下文切换的消耗,缺点是循环会消耗 CPU

 1 package com.company.lock.demo;
 2 
 3 import java.util.concurrent.TimeUnit;
 4 import java.util.concurrent.atomic.AtomicReference;
 5 
 6 /**
 7  * 自旋锁:
 8  * <p>
 9  * 是指尝试获取锁的线程不会立即阻塞,而是采用循环的方式去尝试获取锁,这样的好处是减少线程上下文切换的消耗,缺点是循环会消耗 CPU
10  * <p>
11  * 通过CAS操作完成自旋锁,A线程先进来调用myLock()方法,自己持有锁5秒钟,B线程随后进来发现当前有线程持有锁,不是null,所以只能通过自旋等待,直到A线程释放锁后
12  * B线程随后抢到锁。
13  */
14 public class SpinLockDemo {
15     AtomicReference<Thread> atomicReference = new AtomicReference<>();//引用类型,默认参数为null
16 
17     public void myLock() {
18         //当前线程
19         Thread thread = Thread.currentThread();
20         System.out.println(Thread.currentThread().getName() + "\t com in O(∩_∩)O~" + "我进去啦!");
21         while (!atomicReference.compareAndSet(null, thread)) {//CAS自旋,若当前值和主内存值一致,才能更换主内存值,为 true 取反结束循环。
22         }
23     }
24 
25     public void myUnLock() {
26         Thread thread = Thread.currentThread();
27         atomicReference.compareAndSet(thread, null);
28         System.out.println(Thread.currentThread().getName() + "\t come out O(∩_∩)O ~" + "我出来啦!");
29     }
30 
31     public static void main(String[] args) {
32         SpinLockDemo spinLockDemo = new SpinLockDemo();
33         new Thread(() -> {
34             spinLockDemo.myLock();
35             try {
36                 TimeUnit.SECONDS.sleep(5);
37             } catch (InterruptedException e) {
38                 e.printStackTrace();
39             }
40             spinLockDemo.myUnLock();
41         }, "AA").start();
42 
43         try {
44             TimeUnit.SECONDS.sleep(1);
45         } catch (InterruptedException e) {
46             e.printStackTrace();
47         }
48 
49         new Thread(() -> {
50             spinLockDemo.myLock();
51             try {
52                 TimeUnit.SECONDS.sleep(2);
53             } catch (InterruptedException e) {
54                 e.printStackTrace();
55             }
56             spinLockDemo.myUnLock();
57         }, "BB").start();
58 
59         while (Thread.activeCount() > 2) {
60             Thread.yield();
61         }
62         System.out.println("自旋锁....小case");
63 
64     }
65 }
View Code

 

多线程并发环境下读写锁资源同时进行的方案设计

多线程同时读一个资源没有任何问题,为了满足并发量,读取共享资源是可以同时进行的,但如果有一个线程想要修改共享资源,就不应该在有其它线程可以对该资源进行读或写。即:

读 --  读  能共存 

读 -- 写   不能共存

写 -- 写  不能共存

写操作:原子+独占,整个过程必须是一个完整的统一体,中间不能被分割或者被打断

代码举例:

  1 package com.company.lock.demo;
  2 
  3 import java.util.HashMap;
  4 import java.util.Map;
  5 import java.util.concurrent.TimeUnit;
  6 import java.util.concurrent.locks.ReentrantReadWriteLock;
  7 
  8 /**
  9  * 多个线程同时读一个资源类没有任何问题,所以为了满足并发量,读取共享资源应该可以同时进行。
 10  * 但是
 11  * 如果有一个线程想去写共享资源,就不应该再有其它线程可以对该资源进行读或写
 12  * <p>
 13  * <p>
 14  * 小总结:
 15  * <p>
 16  * 读-读  能共存
 17  * 读-写  不能共存
 18  * 写-写  不能共存
 19  * <p>
 20  * 写操作: 原子+独占,整个过程必须是一个完整的统一体,中间不许被分割,被打断
 21  */
 22 
 23 //手写一个缓存类
 24 class MyCache {
 25     //加 volatile 高并发环境下保证可见性
 26     private volatile Map<String, Object> map = new HashMap<>();//不加锁的情况下会出现加塞、打断等结果
 27     //读写分离锁,既可以保证高并发的读,又可以保证写操作的原子性
 28     private final ReentrantReadWriteLock readWriteLock = new ReentrantReadWriteLock();
 29 
 30     public void setMap(String key, Object value) {
 31         readWriteLock.writeLock().lock();
 32         try {
 33             System.out.println(Thread.currentThread().getName() + "\t 开始写入缓存" + "\t" + key);
 34             try {
 35                 TimeUnit.MICROSECONDS.sleep(300);
 36             } catch (InterruptedException e) {
 37                 e.printStackTrace();
 38             }
 39             map.put(key, value);
 40             System.out.println(Thread.currentThread().getName() + "\t 写入完成.....");
 41         } catch (Exception e) {
 42             e.printStackTrace();
 43         } finally {
 44             readWriteLock.writeLock().unlock();
 45         }
 46     }
 47 
 48     public void get(String key) {
 49         readWriteLock.readLock().lock();
 50         try {
 51             System.out.println(Thread.currentThread().getName() + "\t 开始读取资源");
 52             try {
 53                 TimeUnit.MICROSECONDS.sleep(300);
 54             } catch (InterruptedException e) {
 55                 e.printStackTrace();
 56             }
 57             Object result = map.get(key);
 58             System.out.println(Thread.currentThread().getName() + "\t 读取完成....." + result);
 59         } catch (Exception e) {
 60             e.printStackTrace();
 61         } finally {
 62             readWriteLock.readLock().unlock();
 63         }
 64     }
 65 
 66   /*  public void clean() {//清空缓存
 67         readWriteLock.writeLock().lock();
 68         try {
 69             map.clear();
 70         } catch (Exception e) {
 71             e.printStackTrace();
 72         } finally {
 73             readWriteLock.writeLock().unlock();
 74             System.out.println("清空完成.......");
 75         }
 76     }*/
 77 }
 78 
 79 public class ReentrantReadWriteLockDemo {
 80     public static void main(String[] args) {
 81         MyCache myCache = new MyCache();
 82         for (int i = 1; i <= 5; i++) {
 83             final int tempInt = i;
 84             new Thread(() -> {
 85                 myCache.setMap(tempInt + "", tempInt + "");
 86             }, String.valueOf(i)).start();
 87         }
 88         for (int i = 1; i <= 5; i++) {
 89             final int tempInt = i;
 90             new Thread(() -> {
 91                 myCache.get(tempInt + "");
 92             }, String.valueOf(i)).start();
 93         }
 94         /*for (int i = 1; i <= 5; i++) {//清空缓存的线程
 95             new Thread(() -> {
 96                 try {
 97                     TimeUnit.SECONDS.sleep(5);
 98                 } catch (InterruptedException e) {
 99                     e.printStackTrace();
100                 }
101                 myCache.clean();
102             }, String.valueOf(i)).start();
103         }*/
104     }
105 }
View Code

 

博客园  ©  2004-2026
浙公网安备 33010602011771号 浙ICP备2021040463号-3