标签:nbsp null ati 维护 线程池 code onclick alt 阻塞
一、公平锁/非公平锁/可重入锁/递归锁/自旋锁谈谈你的理解?请手写一个自旋锁
1. 公平锁和非公平锁:
1.1 是什么?
公平锁:是指多个线程按照申请锁的顺序来获取锁,类似排队打饭,先来后到。
非公平锁:是指多个线程获取锁的顺序并不是按照申请锁的顺序,有可能后申请的线程比先申请的线程优先获得锁;在高并发的情况下,有可能会造成优先级反转或者饥饿现象;
1.2 两者区别?
公平锁/非公平锁:
并发包ReentrantLock的创建可以指定构造函数的boolean类型来得到公平锁或非公平锁,默认是非公平锁;
关于两者的区别:
公平锁,就是很公平,在并发环境中,每个线程在获取锁时会先查看此锁维护的等待队列,如果为空,或者当前线程是等待队列的第一个,就占有锁,否则就会加入到等待队列中,以后会按照FIFO的规则从从队列中取到自己;
非公平锁比较粗鲁,上来就直接尝试占有锁,如果尝试失败,就在采用类似公平锁的那种方式;
1.3 题外话?
Java ReentrantLock而言,通过构造函数指定该锁是否是公平锁,默认是非公平锁。非公平锁的优点在于吞吐量大比公平锁大;对于Sysnchronized而言,也是一种非公平锁;
2. 可重入锁(又名递归锁):
2.1 是什么?
可重入锁也叫递归锁,指的是同一线程外层函数获得锁之后,内层递归函数仍然能获取该锁的代码,同一个线程在外层方法获取锁的时候,在进入内层方法的时候会自动获取锁。也就是说,线程可以进入任何一个它已经拥有锁所同步着的代码块。
2.2 ReentrantLock/Synchronized就是一个典型的可重入锁
2.3 可重入锁最大的作用是避免死锁
参考代码1:Synchronized
package com.example.code.lock; class Phone { public synchronized void sendSMS() { System.out.println(Thread.currentThread().getName() + "\t invoked sendSMS()"); this.sendEmail(); } public synchronized void sendEmail() { System.out.println(Thread.currentThread().getName() + "\t invoked sendEmail()"); } } /** * 同步方法可以调用另一个同步方法 * 0 invoked sendSMS() //0线程在外层方法获取锁的时候 * 0 invoked sendEmail() //0在进入内层方法的时候会自动获取锁 * 1 invoked sendSMS() * 1 invoked sendEmail() */ public class LockDemo { public static void main(String[] args) { Phone phone = new Phone(); for (int i = 0; i < 2; i++) { new Thread(() -> {phone.sendSMS();},String.valueOf(i)).start(); } } }
参考代码2:ReentrantLock
package com.example.code.lock; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; class Phone { public void sendSMS() { System.out.println(Thread.currentThread().getName() + "\t invoked sendSMS()"); this.sendEmail(); } public void sendEmail() { System.out.println(Thread.currentThread().getName() + "\t invoked sendEmail()"); } } /** * ReentrantLock是典型的可重入锁 * 注意:锁几次释放几次 */ public class LockDemo { public static void main(String[] args) { Phone phone = new Phone(); Lock lock = new ReentrantLock(); for (int i = 0; i < 2; i++) { new Thread(() -> { lock.lock(); lock.lock(); try { phone.sendSMS(); }finally { lock.unlock(); lock.unlock(); } }, String.valueOf(i)).start(); } } }
3. 自旋锁:
是指尝试获取锁的线程不会立即阻塞,而是采用循环的方式尝试去获取锁,这样的好处是减少线程上下文切换的消耗,缺点是循环会消耗CPU;
案例:手写自旋锁
package com.example.code.lock; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicReference; /** * 题目:实现一个自旋锁 * 自旋锁的好处:循环比较获取直到成功为止,没有类似wait的阻塞。 * * 通过CAS完成自旋锁,A线程先进来调用myLock方法自己持有5秒钟,B随后进来后发现当前线程拥有锁,表示null,所以只能通过自选等待,直到A释放锁后B随后抢到 */ public class SpinLockDemo { AtomicReference<Thread> atomicReference = new AtomicReference<>(); public void myLock() { Thread thread = Thread.currentThread(); System.out.println(thread.getName() + "\t come in o(n_n)o"); while (!atomicReference.compareAndSet(null, thread)) {} } public void myUnlock() { Thread thread = Thread.currentThread(); atomicReference.compareAndSet(thread, null); System.out.println(thread.getName() + "\t invoked myUnlock()"); } public static void main(String[] args) { SpinLockDemo spinLockDemo = new SpinLockDemo(); for (int i = 0; i < 2; i++) { new Thread(() -> { spinLockDemo.myLock(); try { TimeUnit.SECONDS.sleep(2); } catch (InterruptedException e) { e.printStackTrace(); } spinLockDemo.myUnlock(); },String.valueOf(i)).start(); } } }
4. 独占锁/共享锁:
二、CountDownLatch/CyclicBarrier/Semaphore使用过吗?
三、阻塞队列知道吗?
四、线程池用过吗?ThreadPoolExecutor谈谈你的理解?
五、线程池用过吗?生产上你如何设置合理参数?
六、死锁编码及定位分析?
标签:nbsp null ati 维护 线程池 code onclick alt 阻塞
原文地址:https://www.cnblogs.com/luliang888/p/12117454.html