Lock锁是一个类似于Synchronized的线程同步机制,但是Lock比Synchronized更加灵活。Lock是个接口,有两个实现类:ReentrantLock(重入锁)、ReentrantReadWriteLock(读写锁)
重入锁也叫做递归锁,指的是同一线程外层函数获得锁之后,内层递归函数仍然有获取该锁的代码,但不受影响。
public class ReentrantDemo implements Runnable {
Lock lock = new ReentrantLock();
@Override
public void run() {
set();
}
public void set() {
try {
lock.lock();
System.out.println("set 方法");
get();
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();// 必须在finally中释放
}
}
public void get() {
try {
lock.lock();
System.out.println("get 方法");
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
public static void main(String[] args) {
ReentrantDemo reentrantDemo = new ReentrantDemo();
new Thread(reentrantDemo).start();
}
测试结果:同一个线程,首先在set方法中获取锁,然后调用get方法,get方法中重复获取同一个锁。两个方法都执行成功
读写锁,可以分别获取读锁或写锁。也就是说将数据的读写操作分开,分成2个锁来分配给线程,从而使得多个线程可以同时进行读操作。读锁使用共享模式;写锁使用独占模式;读锁可以在没有写锁的时候被多个线程同时持有,写锁是独占的。当有读锁时,写锁就不能获得;而当有写锁时,除了获得写锁的这个线程可以获得读锁外,其他线程不能获得读锁
执行三个线程进行读写操作,并设置一个屏障,线程依次准备就绪后未获取锁之前都在等待,当第三个线程执行
cyclicBarrier.await()后屏障解除,三个线程同时执行。
public class WriteAndReadLockTest {
private static ReentrantReadWriteLock reentrantReadWriteLock = new ReentrantReadWriteLock();
private static ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(10, 10,
60L, TimeUnit.SECONDS, new LinkedBlockingQueue<Runnable>());
private static CyclicBarrier cyclicBarrier = new CyclicBarrier(3);
private static int i = 100;
public static void main(String[] args) {
threadPoolExecutor.execute(()->{
read(Thread.currentThread());
});
threadPoolExecutor.execute(()->{
write(Thread.currentThread());
});
threadPoolExecutor.execute(()->{
read(Thread.currentThread());
});
threadPoolExecutor.shutdown();
}
private static void read(Thread thread) {
try {
cyclicBarrier.await();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (BrokenBarrierException e) {
e.printStackTrace();
}
reentrantReadWriteLock.readLock().lock();
try {
System.out.println("读线程 "+ thread.getName() + " 开始执行, i=" + i);
Thread.sleep(1000);
System.out.println(thread.getName() +" is over!");
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
reentrantReadWriteLock.readLock().unlock();
}
}
private static void write(Thread thread) {
try {
cyclicBarrier.await();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (BrokenBarrierException e) {
e.printStackTrace();
}
reentrantReadWriteLock.writeLock().lock();
try {
i++;
System.out.println("写线程 "+ thread.getName() + " is doing, i=" + i);
System.out.println(thread.getName() +" is over!");
} finally {
reentrantReadWriteLock.writeLock().unlock();
}
}
}
执行结果:线程3先获取到了读锁,因为读锁时是可以共享的,所以线程1也可以获取到读锁,线程1、线程3读操作完成后,将读锁释放后,线程2才能获取到写锁并开始执行写操作
public class LockAndUnlock implements Runnable{
Lock lock = new ReentrantLock();
@Override
public void run() {
//上锁
lock.lock();
try {
System.out.println("ThreadA获取锁");
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock(); // 解锁
System.out.println("ThreadA释放锁");
}
}
public static void main(String[] args) {
LockAndUnlock lockAndUnlock = new LockAndUnlock();
Thread threadA = new Thread(lockAndUnlock,"threadA");
threadA.start();
}
}
实例:当两个线程同时通过lock.lockInterruptibly()想获取某个锁时,假若此时线程A获取到了锁,而线程B只有在等待,那么对线程B调用threadB.interrupt()方法能够中断线程B的等待过程。
public class LockInterruptiblyDemo {
private static Lock lock = new ReentrantLock();
public static void main(String[] args) {
Thread threadA = new Thread(new Runnable() {
@Override
public void run() {
try {
lock.lockInterruptibly();
System.out.println("ThreadA获得锁 ");
// 模拟线程A持有锁的一段时间
Thread.sleep(2000);
} catch (InterruptedException e) {
System.out.println("ThreadA在等待锁时被中断");
} finally {
if (Thread.holdsLock(lock)) {
lock.unlock();
}
}
}
});
Thread threadB = new Thread(new Runnable() {
@Override
public void run() {
try {
lock.lockInterruptibly();
System.out.println("ThreadB获得锁");
} catch (InterruptedException e) {
System.out.println("ThreadB在等待锁时被中断");
} finally {
if (Thread.holdsLock(lock)) {
lock.unlock();
}
}
}
});
threadA.start();
// 让线程A先获取到锁
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
threadB.start();
// 在线程B等待锁的过程中,中断线程B
threadB.interrupt();
}
}
实例:当两个线程同时通过lock.tryLock()想获取某个锁时,假若此时线程A获取到了锁,而线程B不会等待,直接放弃获取锁
public class TryLock{
private static Lock lock = new ReentrantLock();
public static void main(String[] args) {
Thread threadA = new Thread(new Runnable() {
@Override
public void run() {
if (lock.tryLock()) {
try {
System.out.println("ThreadA获得了锁");
// 模拟线程A持有锁的一段时间
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
} else {
System.out.println("ThreadA获取锁失败");
}
}
});
Thread threadB = new Thread(new Runnable() {
@Override
public void run() {
if (lock.tryLock()) {
try {
System.out.println("ThreadB获得了锁");
} finally {
lock.unlock();
}
} else {
System.out.println("ThreadB获取锁失败");
}
}
});
threadA.start();
// 让线程A先获取到锁
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
threadB.start();
}
}
如果锁定可用,则此方法立即返回值true。
如果锁不可用,则当前线程将被禁用以进行线程调度,并且在发生以下三种情况之一之前处于休眠状态:
● 当前线程获取锁。
● 其他一些线程中断当前线程。
● 等待时间过去了,返回false
public class TryLockParam implements Runnable{
Lock lock = new ReentrantLock();
@Override
public void run() {
try {
//假设 threadA 线程先持有锁, 完成任务需要 4 秒钟,
//这个时候 threadB 线程尝试获得锁, threadB 线程在 3 秒内还没有获得锁的话, 那么它就不再等了,直接放弃
if (lock.tryLock(3, TimeUnit.SECONDS)) {
System.out.println(Thread.currentThread().getName() + "获得锁,执行耗时任务");
Thread.sleep(1000 * 4);
}else {
System.out.println(Thread.currentThread().getName() + "放弃获得锁");
}
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
if (Thread.holdsLock(lock)) {
lock.unlock();
}
}
}
public static void main(String[] args) {
TryLockParam tryLockParam=new TryLockParam();
Thread threadA=new Thread(tryLockParam,"threadA");
Thread threadB=new Thread(tryLockParam,"threadB");
threadA.start();
threadB.start();
}
}
如果锁不可用,则当前线程将被禁用以进行线程调度,并且在发生以下三种情况之一之前处于休眠状态,当前线程是可以被中断的。
public class TryLockParam implements Runnable{
Lock lock = new ReentrantLock();
@Override
public void run() {
try {
//假设 threadA 线程先持有锁, 完成任务需要 4 秒钟,
//这个时候 threadB 线程尝试获得锁, threadB 线程在 3 秒内还没有获得锁的话, 那么它就不再等了,直接放弃
if (lock.tryLock(3, TimeUnit.SECONDS)) {
System.out.println(Thread.currentThread().getName() + "获得锁");
for (int i = 0; i <= 500000000; i++) {
if (i == 500000000) {
System.out.println("运算结束");
}
}
lock.unlock();
}else {
System.out.println(Thread.currentThread().getName() + "放弃获得锁");
}
} catch (InterruptedException e) {
System.out.println(Thread.currentThread().getName() +"中断");
e.printStackTrace();
}
}
public static void main(String[] args) {
TryLockParam tryLockParam=new TryLockParam();
Thread threadA=new Thread(tryLockParam,"threadA");
Thread threadB=new Thread(tryLockParam,"threadB");
threadA.start();
threadB.start();
try {
TimeUnit.MILLISECONDS.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
threadA.interrupt();
threadB.interrupt();
}
}