Java-线程同步(1)说到Lock对象,但是和synchronized相比似乎只是多了一个tryLock和lockInterruptibly 功能?并不是这样,我们接着看Lock更加高大上的用法。
读锁与写锁
案例:有多个线程同时操作一个对象,这个对象有一个get方法和一个set方法。现在,我们期望的其实是:
- 如果没有线程调用set方法,那么我们希望所有的线程都可以get
- 如果有线程调用set方法,那么我们希望等待set结束后在调用get
- 我们set方法本身是同步的
这种情况,我们可以用读写锁很好的解决。
PS: 事实上,读写锁有很多的概念以及很多的情况!
```java
public void get() {
mLock.readLock().lock();
System.out.println("get(),线程:"+Thread.currentThread().getName());
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
mLock.readLock().unlock();
}
public void set() {
mLock.writeLock().lock();
System.out.println("set(),线程:"+Thread.currentThread().getName());
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
mLock.writeLock().unlock();
}
1. 同时获取读锁情况
final ThreadSync threadSync = new ThreadSync();
Thread thread1 = new Thread(new Runnable() {
@Override
public void run() {
threadSync.get();
}
});
Thread thread2 = new Thread(new Runnable() {
@Override
public void run() {
threadSync.get();
}
});
thread1.start();
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
thread2.start();
image.png
可以看到,瞬间输出两句日志,说明它们互相之间没有加锁
2. 同时获取写锁情况
image.png从时间上可以看出,两句日志相差1秒,正好是我延时的时间。所以说明它们互相之间加锁
3. 先获取写锁后获取读锁
image.png这种情况下,也是无法获取到读锁的,因为有线程在写!
4. 先获取读锁后获取写锁
image.png这个时间也相差一秒,也就是说:在读锁锁定的过程中,是不允许其他线程去写
5. 先获取读,在去请求写,最后在请求读
因为先获取到读锁之后,其他线程请求读锁可以直接获取。那么会不会出现一直无法获取到写锁的情况呢?
我通过四个线程去操作这个:
- 第一个线程先获取读锁
- 第二个线程去获取写锁
-
最后两个线程去获取读锁
image.png
可以看到:第一个线程和第二个线程之间有延时。但是第三个和第四个线程并没有跨过第二个请求写锁的线程,而是等待第二个线程释放锁之后,才在获取读锁的。
在读写锁中,其实有一个队列,就是为了防止这种一直无法调用到读锁或者写锁的情况。
// 获取锁队列的长度
mLock.getQueueLength()
image.png
可以看到:
- 当第一个线程请求读锁后,由于直接过去到读锁,所以队列为0
- 当第二、三、四线程都请求后,锁的数量也由0变为1,2,3
thread1.start();
Thread.sleep(100); //保证线程已经启动
System.out.println("锁队列长度:"+mLock.getQueueLength());
thread2.start();
Thread.sleep(100);//保证线程已经启动
System.out.println("锁队列长度:"+mLock.getQueueLength());
thread3.start();
Thread.sleep(100);//保证线程已经启动
System.out.println("锁队列长度:"+mLock.getQueueLength());
thread4.start();
Thread.sleep(100);//保证线程已经启动
System.out.println("锁队列长度:"+mLock.getQueueLength());
网友评论