上一篇:多线程04--线程的原子性、CAS_fengxianaa的博客-CSDN博客
1. 基础概念
/**
* Lock:是一个接口,jdk1.5后的另一种加锁方式
*
* 有了 synchronized,那为什么还需要Lock?
* 1. 使用 synchronized,如果其中代码执行的速度特别慢,其他线程便只能干巴巴地等待,非常影响程序执行效率。
* 而 Lock 可以做到只等待一定的时间,如果还获取不到锁,就去做其他事情。
*
* 2. 使用 synchronized,当多个线程执行读操作时,依然会阻塞,但其实,读和读之间是不冲突的
* 而 Lock 可以做到多个读线程之间不阻塞
*
* ReentrantLock:可重入锁,是 Lock 的代表实现类
*/
public class Lock01_ReentrantLock01 {
public static void main(String[] args) {
Lock lock = new ReentrantLock();
new Thread(() ->{
lock.lock();//加锁
try {
System.out.println(Thread.currentThread().getName() + "加锁后执行。。。。。。");
ThreadHelper.sleep(3000);
} finally {
//释放锁,为了保证Lock锁一定会释放,所以要在finally中加上这句代码
lock.unlock();
}
}).start();
ThreadHelper.sleep(3000);
new Thread(() ->{
lock.lock();//加锁
System.out.println(Thread.currentThread().getName() + "加锁后执行。。。。。。");
lock.unlock();//释放锁,为了保证Lock锁一定会释放,所以要在finally中加上这句代码
}).start();
}
}
2. 基础方法
/**
* ReentrantLock 常用方法
* lock()
* 阻塞当前线程,直到获取锁,阻塞时,调用interrupt没用
*
* lockInterruptibly()
* 判断线程是否已中断
* 是:报 InterruptedException
* 否:阻塞当前线程,直到获取锁,阻塞时,如果调用interrupt,报 InterruptedException
*
* unlock()
* 释放锁
*
*/
public class Lock01_ReentrantLock02 {
public static void main(String[] args) throws InterruptedException {
Lock lock = new ReentrantLock();
new Thread(() ->{
lock.lock();//加锁
System.out.println(Thread.currentThread().getName() + "加锁后执行。。。。。。");
ThreadHelper.sleep(3000);
lock.unlock();//释放锁,为了保证Lock锁一定会释放,所以要在finally中加上这句代码
}).start();
ThreadHelper.sleep(500);
Thread t = new Thread(() ->{
lock.lock();//加锁
// lock.lockInterruptibly();//中断时,抛出 InterruptedException
System.out.println(Thread.currentThread().getName() + "加锁后执行。。。。。。");
lock.unlock();
});
t.start();
ThreadHelper.sleep(100);
t.interrupt();
}
}
常用方法:
/**
* ReentrantLock 常用方法
* tryLock()
* 尝试获取锁,该方法不阻塞线程
* 成功:true
* 失败:false
*
* tryLock(long timeout, TimeUnit unit)
* 在指定时间内阻塞线程,直到获取锁,阻塞时,如果调用interrupt,报 InterruptedException
* 成功:true
* 失败:false
*
*/
public class Lock01_ReentrantLock03 {
public static void main(String[] args) throws InterruptedException {
Lock lock = new ReentrantLock();
new Thread(() ->{
lock.lock();//加锁
System.out.println(Thread.currentThread().getName() + "加锁后执行。。。。。。");
ThreadHelper.sleep(3000);
lock.unlock();//释放锁,为了保证Lock锁一定会释放,所以要在finally中加上这句代码
}).start();
ThreadHelper.sleep(500);
Thread t = new Thread(() ->{
boolean bool = lock.tryLock();//尝试获取锁
// boolean bool = lock.tryLock(5, TimeUnit.SECONDS);
if(bool){
System.out.println(Thread.currentThread().getName() + "加锁后执行。。。。。。");
lock.unlock();
}else{
System.out.println(Thread.currentThread().getName() + "没有拿到锁。。。。。。");
}
});
t.start();
ThreadHelper.sleep(100);
t.interrupt();
}
}
Condition
/**
* Condition:配合Lock,唤醒指定的线程
*
* await()
* 当前线程等待被唤醒,等待时候释放锁,如果等待时被interrupt,抛出 InterruptedException
*
* signal()
* 唤醒调用 await() 方法的线程,当前线程释放锁后,调用 await() 方法的线程会继续执行
* 如果有好几个线程都调用了 await(),那么唤醒等待时间最长的
*
* signalAll()
* 当前线程释放锁后,唤醒所有调用 await() 方法的线程
*/
public class Lock01_ReentrantLock04 {
public static void main(String[] args) throws InterruptedException {
Lock lock = new ReentrantLock();
Condition one = lock.newCondition();
new Thread(() -> {
try {
lock.lock();
System.out.println(Thread.currentThread().getName() + "加锁后等待。。。。。。");
one.await();//线程等待,等待时候释放锁,当调用one.signal()之后,继续往下执行
System.out.println(Thread.currentThread().getName() + "苏醒,继续执行。。。。。。。。。。。。");
} catch (Exception e) {
} finally {
lock.unlock();
}
},"线程A").start();
ThreadHelper.sleep(3000);
new Thread(() -> {
try {
lock.lock();
System.out.println(Thread.currentThread().getName() + "加锁后等待。。。。。。");
one.await();//线程等待,等待时候释放锁,当调用one.signal()之后,继续往下执行
System.out.println(Thread.currentThread().getName() + "苏醒,继续执行。。。。。。。。。。。。");
} catch (Exception e) {
} finally {
lock.unlock();
}
},"线程B").start();
ThreadHelper.sleep(5000);
new Thread(() -> {
try {
lock.lock();
System.out.println(Thread.currentThread().getName() + "执行one.signal()。。。。。。");
// one.signal();//这时候线程A、B,都在等待,当线程C释放锁后,等待时间最长的继续运行
one.signalAll();
ThreadHelper.sleep(3000);
} catch (Exception e) {
} finally {
lock.unlock();
}
},"线程C").start();
}
}
3. 公平锁和非公平锁
/**
* 公平锁和非公平锁
* 公平锁: 讲究先来后到。比如,同时有多个线程在等待一个锁,当锁被释放时,等待时间最久的线程(最先请求的线程)会获得锁,现实场景:排队买票
* 非公平锁: 谁抢到就是谁的,可能导致某个线程一直抢不到锁
*
* synchronized 的本质就是非公屏锁
*/
public class Lock01_ReentrantLock05 {
public static void main(String[] args) {
Lock lock = new ReentrantLock();//默认非公屏锁
lock = new ReentrantLock(true);//公平锁
}
}
4. ReadWriteLock
/**
* ReadWriteLock 读写锁,也是一个接口
*
* ReentrantReadWriteLock 可重入的读写锁,是ReadWriteLock的主要实现类
* 允许多个读线程同时访问,但不允许写和读、写和写同时访问。
* 相对于 ReentrantLock (排他锁),提高了并发性
*
*/
public class Lock02_ReadWriteLock01 {
public static void main(String[] args) throws InterruptedException {
ReadWriteLock lock = new ReentrantReadWriteLock();
/**
* 1. 多个线程竞争读锁,不会产生阻塞
*/
for(int i=0;i<2;i++){
new Thread(() -> {
lock.readLock().lock();//使用读锁
System.out.println(Thread.currentThread().getName()+"拿到了读锁。。。。。。");
ThreadHelper.sleep(3000);
lock.readLock().unlock();// 实际中,这句代码需要放到 finally 中
}).start();
}
/**
* 2. 多个线程竞争写锁,会产生阻塞
*/
// for(int i=0;i<2;i++){
// new Thread(() -> {
// lock.writeLock().lock();
// System.out.println(Thread.currentThread().getName()+"拿到了写锁。。。。。。");
// ThreadHelper.sleep(3000);
// lock.writeLock().unlock();// 实际中,这句代码需要放到 finally 中
// }).start();
// }
/**
* 3. 锁降级:写锁——>读锁,即 同一个线程先获取写锁,还没释放,仍然可以继续获取读锁
*/
// lock.writeLock().lock();
// System.out.println("主线程获取了写锁。。。。。。");
// lock.readLock().lock();
// System.out.println("主线程又获取了读锁。。。。。。");
// lock.readLock().unlock();
// lock.writeLock().unlock();
}
}