你知道的Java锁有哪些? synchronized?Lock?它们又有什么区别?锁可分为哪些种类?锁是如何实现的?
公平与非公平锁
公平锁与非公平锁的区别体现在锁造成阻塞时的排队机制,公平锁按申请锁顺序排队等待获取锁,而非公平并不是按照申请顺序,有可能后申请的线程先获取到锁。
常用到的ReentrantLock 在创建的时候可以指定锁机制是公平还是非公平,默认非公平。synchronized也是非公平的
public ReentrantLock() {
sync = new NonfairSync();
}
public ReentrantLock(boolean fair) {
sync = fair ? new FairSync() : new NonfairSync();
}
可重入锁
synchronized,ReentrantLock,都是可重入锁,在同一线程在外层方法获取锁的时候,在进入内层方法会自动获取锁,也就是说线程可以进入任何一个它已经拥有的锁所同步的代码块,即表示可重新反复进入的锁,但仅限于当前线程;
// 线程可以进入任何一个它已经拥有的锁所同步的代码块
public synchronized void method1(){
// 访问另一个同步方法,自动获取锁
method2();
}
public synchronized void method2(){
}
Lock锁,可重入示例加几次锁,要记得释放几次
private ReentrantLock lock = new ReentrantLock();
public void method1(){
try {
lock.lock();
// 可重入,可以进入任何一个它已经拥有的锁所同步的代码
method2();
}finally {
lock.unlock();
}
}
public void method2(){
try {
lock.lock();
}finally {
lock.unlock();
}
}
自旋锁
什么是自旋锁,通过名字就可以猜到大概,通过unsafe+volatile自旋(while)的方式来"阻塞代码"
尝试获取锁的线程不会阻塞,而是采用循环的方式去尝试获取锁,这样的好处是可以减少线程上下文切换,缺点就是消耗cpu
// Unsafe 类中的一处自旋锁
public final int getAndAddInt(Object var1, long var2, int var4) {
int var5;
do {
// 拿到原有的值
var5 = this.getIntVolatile(var1, var2);
// 尝试进行替换,如果替换成功返回,否则一直尝试
} while(!this.compareAndSwapInt(var1, var2, var5, var5 + var4));
return var5;
}
自己实现一个自旋锁
// 思考一下这个锁是可重入还是不可重入
public class MyLock {
private AtomicReference<Thread> reference = new AtomicReference<>();
public void lock(){
Thread thread = Thread.currentThread();
// 如果当前引用中有thread,那么说明在加锁中,其他线程自旋等待
while(!reference.compareAndSet(null,thread)){
System.out.println(thread.getName()+"尝试加锁");
}
}
public void unlock(){
Thread thread = Thread.currentThread();
// 解锁,如果当前线程是自己引用置为null
reference.compareAndSet(thread,null);
System.out.println(thread.getName()+"解锁");
}
}
独占锁/共享锁/互斥锁
独占锁
指该锁一次只能被一个线程锁持有。对ReentrantLock和Synchronized而言都是独占锁。
共享锁
该锁可以被多个线程持有
读写锁
ReentrantReadWriteLock 其读锁就是共享锁,写锁就是共享锁;读写,写读,写写的过程互斥,互斥锁。
示例:
public class MyData {
private ReentrantReadWriteLock readWriteLock = new ReentrantReadWriteLock();
private volatile Map<String, Object> map = new HashMap<>();
public Object get(String key) {
// 添加读锁
readWriteLock.readLock().lock();
Object o = null;
try {
System.out.println(Thread.currentThread().getName() + "读取");
o = map.get(key);
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "读取完成");
} catch (Exception e) {
e.printStackTrace();
} finally {
readWriteLock.readLock().unlock();
}
return o;
}
public void put(String key, Object value) {
// 添加写锁
readWriteLock.writeLock().lock();
try {
System.out.println(Thread.currentThread().getName() + "写入");
map.put(key, value);
try {
Thread.sleep(300);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "写入完成");
} catch (Exception e) {
e.printStackTrace();
} finally {
readWriteLock.writeLock().unlock();
}
}
public static void main(String[] args) {
MyData m = new MyData();
for (int i = 0; i < 10; i++) {
final int temp = i;
new Thread(() -> {
m.put(temp + "", null);
}, "writeThread" + i).start();
}
for (int i = 0; i < 10; i++) {
final int temp = i;
new Thread(() -> {
m.get(temp + "");
}, "readThread" + i).start();
}
}
}