Synchronzied与ReentrantLock(Lock接口的一个实现类)的对比
ReentrantLock有很多 Synchronzied 不具备的功能,下文展示一些实现lock的实现类,记住一些典型案例
演示锁-ReentrantLock基本使用示例代码如下(重要的事情是解锁哈)
package com.example.java.base.concurrency.lock.lock;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
/**
* ReentrantLockTest:ReentrantLock锁的基本使用默认是非公平的锁(可以通过构造方法传入true改为公平锁)
* Lock不会像synchronized一样,异常的时候自动释放锁,所以最佳实践是,finally中释放锁,以便保证发生异常的时候锁一定被释放
* 关于synchronized与ReentrantLock对比和选择,在java并发编程与实战的书上是解释得很清楚了,当然也可以参考网上相关博客
*
* @author zhangxiaoxiang
* @date 2020/11/14
*/
public class ReentrantLockTest {
private static Lock lock = new java.util.concurrent.locks.ReentrantLock();
public static void main(String[] args) {
System.out.println("先获取锁");
//锁【lock.lock】必须紧跟try代码块,且unlock要放到finally第一行。
lock.lock();
try {
System.out.println(Thread.currentThread().getName() + "开始执行任务(这里是业务代码)");
} finally {
lock.unlock();
System.out.println("要记得在 try的 finally 里面显示的释放锁哦");
}
}
}
演示锁-ReentrantLock调用tryLock()方法来避免死锁示例代码
package com.example.java.base.concurrency.lock.lock;
import java.util.Random;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
/**
* ReentrantLockTryLockTest:
* 用tryLock来避免死锁
*
* @author zhangxiaoxiang
* @date 2020/11/14
*/
public class ReentrantLockTryLockTest implements Runnable {
/**
* 标记位
*/
int flag = 1;
static Lock lock1 = new ReentrantLock();
static Lock lock2 = new ReentrantLock();
public static void main(String[] args) {
ReentrantLockTryLockTest r1 = new ReentrantLockTryLockTest();
ReentrantLockTryLockTest r2 = new ReentrantLockTryLockTest();
r1.flag = 1;
r1.flag = 0;
//不要显式创建线程,请使用线程池。(演示图个方便)
new Thread(r1).start();
new Thread(r2).start();
}
@Override
public void run() {
for (int i = 0; i < 100; i++) {
if (flag == 1) {
try {
if (lock1.tryLock(800, TimeUnit.MILLISECONDS)) {
try {
System.out.println("线程1获取到了锁1");
Thread.sleep(new Random().nextInt(1000));
if (lock2.tryLock(800, TimeUnit.MILLISECONDS)) {
try {
System.out.println("线程1获取到了锁2");
System.out.println("线程1成功获取到了两把锁");
break;
} finally {
lock2.unlock();
System.out.println("---lock2.unlock()---");
}
} else {
System.out.println("线程1获取锁2失败,已重试");
}
} finally {
lock1.unlock();
System.out.println("---lock1.unlock()---");
Thread.sleep(new Random().nextInt(1000));
}
} else {
System.out.println("线程1获取锁1失败,已重试");
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
if (flag == 0) {
try {
if (lock2.tryLock(3000, TimeUnit.MILLISECONDS)) {
try {
System.out.println("线程2获取到了锁2");
Thread.sleep(new Random().nextInt(1000));
if (lock1.tryLock(800, TimeUnit.MILLISECONDS)) {
try {
System.out.println("线程2获取到了锁1");
System.out.println("线程2成功获取到了两把锁");
break;
} finally {
lock1.unlock();
System.out.println("---lock1.unlock()---");
}
} else {
System.out.println("线程2获取锁1失败,已重试");
}
} finally {
lock2.unlock();
System.out.println("---lock2.unlock()---");
Thread.sleep(new Random().nextInt(1000));
}
} else {
System.out.println("线程2获取锁2失败,已重试");
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
演示锁-ReentrantLock调用lockInterruptibly()方法
package com.zhang.myjuc.a3.lock.lock;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
/**
* ReentrantLockInterruptiblyDemo:可中断锁
* @author zhangxiaoxiang
* @date 2020/8/16
*/
public class ReentrantLockInterruptiblyDemo implements Runnable {
private Lock lock = new ReentrantLock();
public static void main(String[] args) {
ReentrantLockInterruptiblyDemo lockInterruptibly = new ReentrantLockInterruptiblyDemo();
Thread thread0 = new Thread(lockInterruptibly);
Thread thread1 = new Thread(lockInterruptibly);
thread0.start();
thread1.start();
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
thread1.interrupt();
}
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + "尝试获取锁");
try {
lock.lockInterruptibly();
try {
System.out.println(Thread.currentThread().getName() + "获取到了锁");
Thread.sleep(5000);
} catch (InterruptedException e) {
System.out.println(Thread.currentThread().getName() + "睡眠期间被中断了");
} finally {
lock.unlock();
System.out.println(Thread.currentThread().getName() + "释放了锁");
}
} catch (InterruptedException e) {
System.out.println(Thread.currentThread().getName() + "获得锁期间被中断了");
}
}
}
ReentrantLock 可重入锁-应用场景(订电影票)
package com.zhang.myjuc.a3.lock.reentrantlock;
import java.util.concurrent.*;
import java.util.concurrent.locks.ReentrantLock;
/**
* CinemaBookSeat:演示多线程预定电影票
*
* @author zhangxiaoxiang
* @date 2020/08/16
*/
public class CinemaBookSeat implements Runnable{
private static ReentrantLock lock=new ReentrantLock();
private static void bookSeat(){
}
public static void main(String[] args) {
ExecutorService service = Executors.newCachedThreadPool();
//这里显示的创建可以更加熟悉线程池的参数,最重要的是那个队列长度(默认是整形类最大值,搞不好容易OOM),
// 符合阿里规范,表示这里有4个核心线程,如果不够用就增加至8个,存货时间是60秒,之后不用 就回收,节省资源
ThreadPoolExecutor executor = new ThreadPoolExecutor(4,
8, 60L, TimeUnit.SECONDS, new LinkedBlockingQueue<>(5));
//这里模拟多个线程订票
for (int i = 0; i < 10; i++) {
service.submit(new CinemaBookSeat());
}
}
@Override
public void run() {
lock.lock();
try {
System.out.println("用户"+Thread.currentThread().getName()+" 开始预定座位,为你锁定座位3秒钟,如果不买自动释放");
Thread.sleep(3000);
System.out.println("用户"+Thread.currentThread().getName()+" 买票订座完成");
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
}
ReentrantLock 可重入锁-演示公平和非公平的情况
package com.zhang.myjuc.a3.lock.reentrantlock;
import java.util.Random;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
/**
* FairLock:演示公平和不公平两种情况 注意构造函数和结果的对比,,主要注意0线程持有锁后是立即执行还是在队列后执行
*
* @author zhangxiaoxiang
* @date 2020/8/17
*/
public class FairLock {
public static void main(String[] args) {
PrintQueue printQueue = new PrintQueue();
Thread thread[] = new Thread[10];
for (int i = 0; i < 10; i++) {
thread[i] = new Thread(new Job(printQueue));
}
for (int i = 0; i < 10; i++) {
thread[i].start();
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
class Job implements Runnable {
PrintQueue printQueue;
public Job(PrintQueue printQueue) {
this.printQueue = printQueue;
}
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + "排队中...");
printQueue.printJob(new Object());
System.out.println(Thread.currentThread().getName() + "打印完毕---ok");
}
}
class PrintQueue {
private Lock queueLock = new ReentrantLock(false);
public void printJob(Object document) {
queueLock.lock();
try {
int duration = new Random().nextInt(5) + 1;
System.out.println(Thread.currentThread().getName() + "第一份-----正在打印,需要" + duration + "秒");
Thread.sleep(duration * 1000);
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
queueLock.unlock();
}
queueLock.lock();
try {
int duration = new Random().nextInt(5) + 1;
System.out.println(Thread.currentThread().getName() + "第二份-----正在打印,需要" + duration + "秒");
Thread.sleep(duration * 1000);
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
queueLock.unlock();
}
}
}
上面代码运行结果
ReentrantReadWriteLock 读写锁-演示可以共读,但是不可共写
package com.zhang.myjuc.a3.lock.readwrite;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.ReentrantReadWriteLock;
/**
* CinemaReadWrite:读写锁示例(电影院示例升级)
*
* @author zhangxiaoxiang
* @date 2020/08/16
*/
public class CinemaReadWrite {
private static ReentrantReadWriteLock reentrantReadWriteLock = new ReentrantReadWriteLock();
private static ReentrantReadWriteLock.ReadLock readLock = reentrantReadWriteLock.readLock();
private static ReentrantReadWriteLock.WriteLock writeLock = reentrantReadWriteLock.writeLock();
private static void read() {
//获取读锁
readLock.lock();
try {
System.out.println(Thread.currentThread().getName() + " 得到了读锁,正在读取");
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
//释放读锁
readLock.unlock();
System.out.println(Thread.currentThread().getName() + " 释放了读锁");
}
}
private static void write() {
//获取写锁
writeLock.lock();
try {
System.out.println(Thread.currentThread().getName() + " 得到了写锁,正在写入");
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
//释放写锁
writeLock.unlock();
System.out.println(Thread.currentThread().getName() + " 释放了写锁");
}
}
public static void main(String[] args) {
ThreadPoolExecutor executor = new ThreadPoolExecutor(5,
5, 60L, TimeUnit.SECONDS, new LinkedBlockingQueue<>(5));
//这里由于要为线程添加名字,所以这么写(和视频不一样)
executor.submit(new Thread(() -> read(), "Thead1"));
executor.submit(new Thread(() -> read(), "Thead2"));
executor.submit(new Thread(() -> write(), "Thead3"));
executor.submit(new Thread(() -> write(), "Thead4"));
}
}
自旋锁 在代码里面体现
package com.zhang.myjuc.a3.lock.spinlock;
import java.util.concurrent.atomic.AtomicReference;
/**
* SpinLock:自旋锁
*
* @author zhangxiaoxiang
* @date 2020/08/16
*/
public class SpinLock {
/**
* AtomicReference 可以自动更新的对象引用
*/
private AtomicReference<Thread> sign = new AtomicReference<>();
/**
* 模拟加锁
*/
public void lock() {
Thread thread = Thread.currentThread();
//自旋锁这里用到了CAS 如果当前值==预期值,则自动将该值设置为给定的更新值。
while (!sign.compareAndSet(null, thread)) {
//放开注释展示消耗过程
// System.out.println("自旋锁获取失败,再次尝试");
}
}
/**
* 模拟释放锁
*/
public void unlock() {
Thread currentThread = Thread.currentThread();
sign.compareAndSet(currentThread, null);
}
//测试方法
public static void main(String[] args) {
SpinLock spinLock = new SpinLock();
Runnable runnable = new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + " 开始尝试获取自旋锁");
spinLock.lock();
System.out.println(Thread.currentThread().getName() + " 获取到了自旋锁");
try {
Thread.sleep(300);
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
spinLock.unlock();
System.out.println(Thread.currentThread().getName() + " 释放了自旋锁");
}
}
};
Thread thread = new Thread(runnable);
Thread thread2 = new Thread(runnable);
thread.start();
thread2.start();
}
}
代码运行结果
锁的降级和非公平和公平的ReentrantReadWriteLock的策略 待补充...