Lock接口是JDK1.5新增的,其在JUC的locks包下,比synchronized关键字更强大
- Lock接口 互斥锁,排他锁
的外部实现类只有ReentrantReadWirteLock,其他的实现类都是在class内部实现的,没有public,也就是说其无法在不同包之间访问!!!
Segment是ConcurrentHashMap实现的主要原理 - ReadWriteLock接口 读写锁
注意其实现的接口
1.ReentrantReadWriteLock类实现了ReadWriteLock
2.ReentrantReadWriteLock类的内部类WriteLock和ReadLock实现了Lock接口,没有实现ReadWriteLock接口
提供2个接口方法 - 读操作:共享锁,可以继续加共享锁
- 写操作:排他锁,不可继续加任何锁
Lock接口使用ReentrantLock类(互斥锁,拍他锁)
该实现是由Lock接口实现的
基础使用
基础使用非常简单,使用lock和unlock,锁住之间的代码
class TestA {
private Lock lock=new ReentrantLock();
public void testLock(){
lock.lock();
IntStream.iterate(1,vo->vo+=1).limit(4).forEach(vo-> System.out.println(Thread.currentThread().getName()));
lock.unlock();
}
public static void main(String[] args) {
TestA testA = new TestA();
new Thread(()->testA.testLock()).start();
new Thread(()->testA.testLock()).start();
new Thread(()->testA.testLock()).start();
new Thread(()->testA.testLock()).start();
}
}
因为lock在外面,锁住了里面的代码,所以foreach方法不会被分开执行
Condition接口
synchronized和wait/notify等方法使用可以唤醒线程,但无法唤醒指定的线程,因为其是由JVM执行的。
而Condition接口的对象的存在可以实现多路通知的功能,在一个lock对象中创建多个condition对象实例,线程与condition绑定,可以唤醒指定线程,更加灵活
void await() throws InterruptedException;
void signal();
注意使用condition的wait方法前必须使用lock锁,不然会抛InterruptedException
使用Condition的wait/signal方法机制来实现Object的wait/notify方法
使用lock的newCondition方法返回一个新的condition实例
注意其Condition必须要作用在一个lock上,因为只signal一次,所以在此wait之后不被唤醒
Unsafe类-awiat原理
通过源码可以看到使用的是Unsafe类的park方法
因为unsafe的不安全性,权限过大,所以无法通过AppClassLoader加载得到
只能通过反射得到
与park对应的还有unpark
唤醒指定线程
作用于同一个lock上,但是注册绑定的condition不同,唤醒不同的condition信号即可唤醒指定的线程
//唤醒指定线程
class D {
private Lock lock = new ReentrantLock();
private Condition condition1 = lock.newCondition();
private Condition condition2 = lock.newCondition();
public void await1() {
while (true) {
lock.lock();
System.out.println("await1时间" + System.currentTimeMillis());
try {
Thread.sleep(1000);
condition1.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
lock.unlock();
}
}
public void signal1() {
lock.lock();
System.out.println("signal1时间" + System.currentTimeMillis());
condition1.signal();
lock.unlock();
}
public void await2() {
while (true) {
lock.lock();
System.out.println("await2时间" + System.currentTimeMillis());
try {
Thread.sleep(1000);
condition2.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
lock.unlock();
}
}
public void signal2() {
lock.lock();
System.out.println("signal2时间" + System.currentTimeMillis());
condition2.signal();
lock.unlock();
}
public static void main(String[] args) {
D d = new D();
Thread thread = new Thread(() -> d.await1());
Thread thread1 = new Thread(() -> d.await2());
// Thread thread2 = new Thread(() -> d.signal1());
Thread thread3 = new Thread(() -> d.signal2());
thread.start();
thread1.start();
// thread2.start();
thread3.start();
}
}
公平锁和非公平锁
- 公平锁
先到先得,每次获取锁之前都会检查队列里面有没有排队等待锁释放的线程,如果没有才会尝试获取锁,如果有则排队等待 - 非公平锁
在线程获取锁之前不管有没有排队等待的线程,都会尝试先获取锁,只有失败了才会排队等待
设置方法在实例化lock的时候使用有参构造传入boolean类型
ReentrantLock常用API
int getHoldCount()查看当前线程lock数目即持有此锁的次数
public class CC {
private static ReentrantLock lock = new ReentrantLock();
public static void main(String[] args) {
System.out.println("1 " + lock.getHoldCount());
lock.lock();
System.out.println("2 " + lock.getHoldCount());
lock.lock();
System.out.println("3 " + lock.getHoldCount());
lock.lock();
System.out.println("4 " + lock.getHoldCount());
lock.unlock();
System.out.println("5 " + lock.getHoldCount());
lock.unlock();
System.out.println("6 " + lock.getHoldCount());
lock.unlock();
System.out.println("7 " + lock.getHoldCount());
lock.unlock();
System.out.println("8 " + lock.getHoldCount());
}
}
最后因为没有持有锁进行unlock抛出异常
监视方法
这些方法返回的结果都是具有时效性的,不是一成不变,所以不要用于结构控制
int getQueueLength()查看排队队列数目
查看有多少个线程在排队等待
值得注意的是JDK源码里面也写清楚了,即时返回数目也可能会因为进程的动态变化导致不准确,所以最好不要用于流程控制
class CA {
private static ReentrantLock lock = new ReentrantLock();
private void test() {
lock.lock();
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
public static void main(String[] args) throws InterruptedException {
CA ca = new CA();
Thread thread1 = new Thread(() -> ca.test());
Thread thread2 = new Thread(() -> ca.test());
Thread thread3 = new Thread(() -> ca.test());
Thread thread4 = new Thread(() -> ca.test());
System.out.println("0 "+lock.getQueueLength());
thread1.start();
Thread.sleep(100);
System.out.println("1 "+lock.getQueueLength());
thread2.start();
Thread.sleep(100);
System.out.println("2 "+lock.getQueueLength());
thread3.start();
Thread.sleep(100);
System.out.println("3 "+lock.getQueueLength());
thread4.start();
Thread.sleep(100);
System.out.println("4 "+lock.getQueueLength());
Thread.sleep(4000);
System.out.println("5 "+lock.getQueueLength());
}
}
结果如下图,在第四个线程启动之后主线程休眠4秒后等待数为0,说明都执行完毕,线程也结束了
其他类似方法
- int getWaitingThreads(Condition condition)
查看wait等待此condition的线程数目 - boolean hasWaiters(Condition condition)
查看lock给定的condition中是否有等待队列 - boolean hasQueuedThread(Thread thread)
查看该thread是否在此lock的等待队列中 - boolean hasQueuedThreads()
查看此lock是否有等待队列 - boolean hasWaiters(Condition condition)
- boolean isFair()
查看此lock锁是否为公平锁 - boolean isHeldByCurrentThread()
查询当前thread线程是否持有此lock锁 - boolean isLocked()
查询该lock锁是否被任何线程持有,没有释放 - lockInterruptibly()、trylock()、lock()
详细的不说了,知乎上面的一句话简单明了
https://www.zhihu.com/question/36771163
线程间通信交替执行
通过统一的condition的设置,再根据条件判断,如下代码可以实现交替打印ABC
//按顺序执行线程
class CD {
private Lock lock = new ReentrantLock();
private Condition condition = lock.newCondition();
//标识位,volatile强制线程从公共堆栈中取值
private volatile int flag = 1;
private void A() {
lock.lock();
while (flag != 1) {
try {
condition.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("A");
flag = 2;
condition.signalAll();
lock.unlock();
}
private void B() {
lock.lock();
while (flag != 2) {
try {
condition.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("B");
flag = 3;
condition.signalAll();
lock.unlock();
}
private void C() {
lock.lock();
while (flag != 3) {
try {
condition.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("C");
flag = 1;
condition.signalAll();
lock.unlock();
}
public static void main(String[] args) {
CD cd = new CD();
for (int i = 0; i < 10; i++) {
new Thread(() -> cd.A()).start();
new Thread(() -> cd.B()).start();
new Thread(() -> cd.C()).start();
}
}
}
ReadWriterLock接口使用ReentrantReadWriteLock读写锁
lock接口的ReentrantLock锁排他性效率并发如果不修改的话只读区的化,则执行效率不高
其使用的writeLock()和readLock()返回的还是Lock接口实现
ReentrantReadWriteLock锁的读共享
使用lock.readLock()返回Lock锁之后再使用lock()方法
public class DD {
private ReadWriteLock lock=new ReentrantReadWriteLock();
private String useName="superMan";
public void testRead(){
lock.readLock().lock();
System.out.println("start"+Thread.currentThread().getName()+" "+System.currentTimeMillis());
try {
Thread.sleep(3000);
System.out.println("start"+Thread.currentThread().getName()+" "+System.currentTimeMillis());
} catch (InterruptedException e) {
e.printStackTrace();
}finally {
lock.readLock().unlock();
}
}
public static void main(String[] args) {
DD dd = new DD();
new Thread(()->dd.testRead()).start();
new Thread(()->dd.testRead()).start();
new Thread(()->dd.testRead()).start();
}
}
结果如下,读之间并不影响
ReentrantReadWriteLock锁的写互斥
使用lock.wirteLock()返回Lock锁之后再使用lock()方法
验证:
- 只要有写锁就不能再加任何锁
- 只要有读锁只能加读锁
//测试读写、写写
class DA {
private ReadWriteLock lock=new ReentrantReadWriteLock();
private String useName="superMan";
public void testRead(){
lock.readLock().lock();
System.out.println("startRead"+Thread.currentThread().getName()+" "+System.currentTimeMillis());
try {
Thread.sleep(3000);
System.out.println("endRead"+Thread.currentThread().getName()+" "+System.currentTimeMillis());
} catch (InterruptedException e) {
e.printStackTrace();
}finally {
lock.readLock().unlock();
}
}
public void testWirte(){
lock.writeLock().lock();
System.out.println("startWirte"+Thread.currentThread().getName()+" "+System.currentTimeMillis());
try {
Thread.sleep(3000);
System.out.println("endWirte"+Thread.currentThread().getName()+" "+System.currentTimeMillis());
} catch (InterruptedException e) {
e.printStackTrace();
}finally {
lock.writeLock().unlock();
}
}
public static void main(String[] args) {
DA da = new DA();
new Thread(()->da.testWirte()).start();
new Thread(()->da.testRead()).start();
new Thread(()->da.testWirte()).start();
}
}
结果如下
Comments | 0 条评论