1.公平锁和非公平锁
-
是什么?
公平锁:是指多个线程按照申请锁的顺序来获取锁,先来后到。非公平锁:是指多个线程获取锁的顺序并不是按照申请锁的顺序,有可能后申请的线程比现申请的线程优先获得锁,在高并发的情况下,有可能会造成优先级反战或者饥饿现象
-
两者区别
公平锁:就是很公平,在并发环境中,每个线程获取锁时会查看此锁维护的等待队列,如果为空,或者当前线程是等待队列的第一个,就占有锁,否则加入等待队列,以后会按照FIFO的规则从队列中取到自己
非公平锁:比较粗鲁,上来就尝试占有锁,如果尝试失败,在采用类似公平锁的方式(非公平锁的优点在于吞吐量比公平锁大)
synchronized是非公平锁;ReentrantLock的创建可以指定构造函数的boolean类型来得到公平锁或非公平锁,默认是非公平锁。
2.可重入锁(即递归锁)
- 是什么?
指的是同一线程外层函数获得锁之后,内层递归函数仍然能获取该锁的代码。在同一个线程在外层方法获取锁的时候,在进入内层方法会自动获取锁
也即是锁,线程可以进入任何一个它已经拥有的锁同步着的代码块。
ReentrantLock/synchronized就是典型的可重入锁。
最大作用:避免死锁。 - 代码举例
package com.magic.juc0117;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
class Phone implements Runnable{
public static synchronized void sendEmail() throws Exception {
System.out.println(Thread.currentThread().getName()+"sendEmail----------");
}
public synchronized void sendSMS() throws Exception{
System.out.println(Thread.currentThread().getName()+"sendSMS-------------");
sendEmail();
}
@Override
public void run() {
get();
}
Lock lock = new ReentrantLock();
public void get(){
// lock.lock(); **几把锁都可以,必须有对应unlock**
lock.lock();
try{
System.out.println(Thread.currentThread().getName()+"====get-------------");
set();
} catch (Exception e){
e.printStackTrace();
} finally {
lock.unlock();
// lock.unlock();
}
}
public void set(){
lock.lock();
try{
System.out.println(Thread.currentThread().getName()+"=====set-------------");
} catch (Exception e){
e.printStackTrace();
} finally {
lock.unlock();
}
}
}
public class ReentrantLockDemo {
public static void main(String[] args) {
Phone phone = new Phone();
new Thread(() -> {
try{
phone.sendSMS();
} catch (Exception e){
throw new RuntimeException(e);
}
},"t1").start();
new Thread(() -> {
try {
phone.sendSMS();
} catch (Exception e) {
e.printStackTrace();
}
},"t2").start();
try{
TimeUnit.SECONDS.sleep(2);
} catch (Exception e){
throw new RuntimeException(e);
}
Thread t3 = new Thread(phone,"t3");
Thread t4 = new Thread(phone,"t4");
t3.start();
t4.start();
}
}
3.自旋锁
- 是什么?
spinlock
尝试获取锁的线程不会立即阻塞,而是采取循环的方式去获取锁,这样的好处是减少线程上下文切换的消耗,缺点是循环会消耗CPU - 自己手写自旋锁
package com.magic.juc0117;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicReference;
public class SpinLockDemo {
//原子引用线程
AtomicReference<Thread> atomicReference = new AtomicReference<>();
public void myLock(){
Thread thread = Thread.currentThread();
System.out.println(thread.getName()+"fffff");
while (!atomicReference.compareAndSet(null,thread)){
}
}
public void myUnlock(){
Thread thread = Thread.currentThread();
System.out.println(thread.getName()+"unlock");
atomicReference.compareAndSet(thread,null);
}
public static void main(String[] args) throws InterruptedException {
SpinLockDemo spinLockDemo = new SpinLockDemo();
new Thread(() -> {
spinLockDemo.myLock();
try {
TimeUnit.SECONDS.sleep(5);
} catch (InterruptedException e) {
e.printStackTrace();
}
spinLockDemo.myUnlock();
},"AA").start();
TimeUnit.SECONDS.sleep(1);
new Thread(() -> {
spinLockDemo.myLock();
spinLockDemo.myUnlock();
},"BB").start();
}
}
4.独占锁(写锁)/共享锁(读锁)
- 是什么
独占:该锁一次只能被一个线程持有,ReentrantLock和synchronized都是独占锁
共享:该锁可以被多个线程持有
ReentrantReadWriteLock其读锁是共享锁,其写锁是独占锁。
读锁的共享锁可保证并发读是非常高效的,读写、写读、写写过程是互斥的 - 代码实现
package com.magic.juc0117;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
/**
* @Description 读读能共存 读写不能共存 写写不能共存
* 写操作:原子+独占,不应该被打断
* @Author Chelsea
* @Date 2020/1/28 18:40
*/
class MyCache{
//缓存用volatile修饰
private volatile Map<String,Object> map = new HashMap<>();
// private Lock lock = new ReentrantLock();
private ReadWriteLock rwLock = new ReentrantReadWriteLock();
public void put(String key,Object value){
rwLock.writeLock().lock();
try {
System.out.println(Thread.currentThread().getName()+"\t正在写入:"+key);
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
map.put(key,value);
System.out.println(Thread.currentThread().getName()+"\t写入完成:"+key);
}catch (Exception e){
e.printStackTrace();
}finally {
rwLock.writeLock().unlock();
}
}
public void get(String key){
rwLock.readLock().lock();
try {
System.out.println(Thread.currentThread().getName()+"\t正在读取:"+key);
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
Object result = map.get(key);
System.out.println(Thread.currentThread().getName()+"\t读取完成:"+result);
}catch (Exception e){
e.printStackTrace();
}finally {
rwLock.readLock().unlock();
}
}
}
public class ReadWriteLockDemo {
public static void main(String[] args) {
MyCache myCache = new MyCache();
for (int i = 0; i < 5; i++) {
final int tempInt = i;
new Thread(() -> {
myCache.put(tempInt+"",tempInt+"");
},String.valueOf(i)).start();
}
for (int i = 0; i < 5; i++) {
final int tempInt = i;
new Thread(() -> {
myCache.get(tempInt+"");
},String.valueOf(i)).start();
}
}
}
学习整理于面试题.