提示:以下是本篇文章正文内容,下面案例可供参考
遵守先来后到的是公平锁,不遵守的是非公平锁
// 默认的无参构造方法是非公平锁的
public ReentrantLock() {
sync = new NonfairSync();
}
/**
* fair == true,是公平锁
* fair == false,是非公平锁
*/
public ReentrantLock(boolean fair) {
sync = fair ? new FairSync() : new NonfairSync();
}
优缺点
指的是同一线程外层函数获得锁之后﹐内层递归函数仍然能获取该锁的代码,在同一个线程在外层方法获取锁的时候,在进入内层方法会自动获取锁,也就是说线程可以进入任何一个它已经拥有的锁所同步着的代码块
synchronized 和 ReentrantLock 都是可重入锁
代码示例
class Phone implements Runnable{
// 证明 synchronized 是可重入锁
/**
* 两个 synchronized 能够嵌套式使用
*
*
*/
public synchronized void sendMessage() {
// 调用该方法第一次加锁
System.out.println(Thread.currentThread().getName() + "\tsendMessage");
// 第二次加锁,使用 sendMessage 的锁,即共用同一把锁
sendEmail();
}
public synchronized void sendEmail() {
System.out.println(Thread.currentThread().getName() + "\tsendEmail");
}
// 证明 ReentrantLock
Lock lock = new ReentrantLock();
@Override
public void run() {
get();
}
public void get() {
// ReentrantLock 需要手动加锁解锁
// 但是要一一配对,只要配对,多少都可以
// 如果这里 lock 两次,unlock 一次就会导致线程一直阻塞等待解锁
lock.lock();
// lock.lock();
try {
System.out.println(Thread.currentThread().getName() + "\tget");
// 方法内部再次加锁
set();
} finally {
// 不配对,会一直死锁
lock.unlock();
// lock.unlock();
}
}
public void set() {
lock.lock();
try {
System.out.println(Thread.currentThread().getName() + "\tset");
} finally {
lock.unlock();
}
}
}
public class Main {
public static void main(String[] args) {
Phone phone = new Phone();
new Thread(() -> {
phone.sendMessage();
}, "t1").start();
new Thread(() -> {
phone.sendEmail();
}, "t2").start();
Thread.sleep(1000);
System.out.println();
System.out.println("#######################");
System.out.println();
new Thread(phone, "t3").start();
new Thread(phone, "t4").start();
}
}
输出结果
嵌套调用成功,输出的顺序和我们预想的相同。
synchronized 加锁和释放锁的原理
monitorenter 和 monitorexit 指令,monitor计数器
monitorenter指令:
monitorexit指令:将锁计数器减1,每执行一次 monitorexit 命令锁计数器就减1(这一点与 monitorenter 不同),当锁计数器为0,释放锁。
是指尝试获取锁的线程不会立即阻塞,而是采用循环的方式去尝试获取锁,这样的好处是减少线程上下文切换的消耗,缺点是循环会消耗CPU
synchronized 和 ReentrantLock 都是自旋锁,他们都会根据使用场景自动调整自旋时间。
代码示例
class SpinLockDemo {
// 原子引用线程
AtomicReference<Thread> atomicReference = new AtomicReference<>();
public void myLock() {
// 此时 atomicReference 内的线程为 null
Thread thread = Thread.currentThread();
System.out.println(Thread.currentThread().getName() + "\tcome in");
// CAS 自旋
// 将 thread 内的线程赋给 atomicReference,一直自旋直到成功
while (!atomicReference.compareAndSet(null, thread)) {
}
}
public void myUnLock() {
Thread thread = Thread.currentThread();
// 解锁
atomicReference.compareAndSet(thread, null);
System.out.println(Thread.currentThread().getName() + "\tcome out");
}
}
public class Main {
public static void main(String[] args) {
SpinLockDemo spinLockDemo = new SpinLockDemo();
new Thread(() -> {
spinLockDemo.myLock();
// 暂停一会线程,模拟一直占用,不能释放锁
try {
System.out.println("阻塞中......");
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
spinLockDemo.myUnLock();
}, "AA").start();
}
}
输出结果
独占锁:指该锁一次只能被一个线程所持有。对 ReentrantLock 和 Synchronized 而言都是独占锁
共享锁:指该锁可被多个线程所持有
ReentrantReadWriteLock
代码示例
class MyCache {// 资源类(缓存操作,需要较高地并发操作)
private volatile Map<String, Object> map = new HashMap<>();
// 这个锁不足以满足,多个线程的同时读写
// private Lock lock = new ReentrantLock();
// 读写锁分离
// private ReadWriteLock readWriteLock = new ReentrantReadWriteLock();// XXXXXXXXX
/**
* 写操作,不可共享
* @param key
* @param val
* @throws InterruptedException
*/
public void put(String key, Object val) throws InterruptedException {
try {
// 拿到 writeLock,加锁
// readWriteLock.writeLock().lock();// XXXXXXXXX
System.out.println(Thread.currentThread().getName() + "\t正在写入:" + key);
Thread.sleep(300);
map.put(key, val);
System.out.println(Thread.currentThread().getName() + "\t写入完成");
} catch (Exception e) {
e.printStackTrace();
} finally {
// 解锁
// readWriteLock.writeLock().unlock();// XXXXXXXXX
}
}
/**
* 读操作,可以共享
* @param key
* @throws InterruptedException
*/
public void get(String key) throws InterruptedException {
try {
// 拿到 readLock 加锁
// readWriteLock.readLock().lock();// XXXXXXXXX
System.out.println(Thread.currentThread().getName() + "\t正在读取");
Thread.sleep(300);
System.out.println(Thread.currentThread().getName() + "\t读取完成:" + map.get(key));
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
// 解锁
// readWriteLock.readLock().unlock();// XXXXXXXXX
}
}
}
public class ZZZZZZZZZZ {
public static void main(String[] args) {
MyCache myCache = new MyCache();
// 写
for (int i = 1; i <= 5; i++) {
final int tmpInt = i;
new Thread(() -> {
try {
myCache.put(tmpInt + "", tmpInt + "");
} catch (InterruptedException e) {
e.printStackTrace();
}
}, String.valueOf(i)).start();
}
// 读
for (int i = 1; i <= 5; i++) {
final int tmpInt = i;
new Thread(() -> {
try {
myCache.get(tmpInt + "");
} catch (InterruptedException e) {
e.printStackTrace();
}
}, String.valueOf(i)).start();
}
}
}
输出结果(加锁前)
输出结果(加锁后)
将代码注释有XXXXXXXXX的代码解开,就是加锁后版本