Lock的使用
文章目录
- 一、Lock的使用
- 二、使用Condition实现等待/通知
- 2.1 ReentrantLock和Condition
- 2.2 Condition类
- 2.3 ReentrantLock和Condition实现简单等待/通知
- 2.3 多个Condition实现通知部分线程
- 2.4 实现生产者一对一
- 2.5 生产者/消费者:多对多交替打印
- 2.6 公平锁与非公平锁
- 三、ReentrantLock的其他方法
- 3.1 getHoldCount()
- 3.2 int getQueueLength()
- 3.3 int getWaitQueueLength(Condition condition)
- 3.4 boolean hasQueuedThread(Thread thread)
- 3.5 boolean hasWaiters(Condition condition)
- 3.6 其他方法
- 3.6 使用Condition实现顺序执行
- 四、ReentrantReadWriteLock类
一、Lock的使用
1.1 ReentrantLock类
可以实现下线程间的同步互斥,类似于Synchronized的功能,且有更加强大的嗅探锁定,多路分支通知等功能。
1.2 使用ReentrantLock实现同步
- lock()方法获得锁
- unlock()方法释放锁
、
测试类:
public class RenntrankLock_test {
Lock lock = new ReentrantLock();
public void doSth(){
try {
lock.lock();
System.out.println(Thread.currentThread().getName() + " " + "你好");
Thread.sleep(4000);
System.out.println(Thread.currentThread().getName() + " " +"你好结束");
lock.unlock();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public void showSth(){
try {
lock.lock();
System.out.println(Thread.currentThread().getName() + " " +"hello");
Thread.sleep(4000);
System.out.println(Thread.currentThread().getName() + " " +"world");
lock.unlock();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public void eatSth(){
try {
System.out.println(Thread.currentThread().getName() + " " +"eat");
Thread.sleep(4000);
System.out.println(Thread.currentThread().getName() + " " +"no eat");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
线程A:
public class ThreadA extends Thread{
RenntrankLock_test renntrankLock_test;
public ThreadA(RenntrankLock_test renntrankLock_test){
this.renntrankLock_test = renntrankLock_test;
}
@Override
public void run() {
renntrankLock_test.doSth();
}
}
线程B:
public class ThreadB extends Thread {
RenntrankLock_test renntrankLock_test;
public ThreadB(RenntrankLock_test renntrankLock_test){
this.renntrankLock_test = renntrankLock_test;
}
@Override
public void run() {
renntrankLock_test.showSth();
}
}
线程C:
public class ThreadC extends Thread {
RenntrankLock_test renntrankLock_test;
public ThreadC(RenntrankLock_test renntrankLock_test){
this.renntrankLock_test = renntrankLock_test;
}
@Override
public void run() {
renntrankLock_test.eatSth();
}
}
主类:
public class main {
public static void main(String[] args) {
RenntrankLock_test renntrankLock_test = new RenntrankLock_test();
ThreadA threadA = new ThreadA(renntrankLock_test);
ThreadB threadB = new ThreadB(renntrankLock_test);
ThreadC threadC = new ThreadC(renntrankLock_test);
threadA.setName("A");
threadB.setName("B");
threadC.setName("C");
threadA.start();
threadB.start();
threadC.start();
}
}
结果:
B hello
C eat
B world
C no eat
A 你好
A 你好结束
结果说明:
先执行线程B的方法,线程B获得锁,再执行sleep方法时,不释放锁,所以执行非同步方法所在的线程C,C进入sleep,执行B剩下的,B执行结束,释放锁,C执行结束,A抢到锁,执行A到结束。
二、使用Condition实现等待/通知
2.1 ReentrantLock和Condition
关键字synchronized与wait和notifyAll/notify方法结合可以实现等待/通知模式,类ReentrantLock和Condition结合也可以实现等待/通知模式
2.2 Condition类
- JDK5出现的,使用它有更好的灵活性,比如可以实现多路通知功能,也就是可以在一个Lock对象里创建多个Condition(对象监视器)实例,线程对象可以注册在指定的Condition中,从而可以有选择的进行线程通知,在调度线程上更加灵活。
- 在使用notify/notifyAll()方法时,被通知的线程是jvm随机选择的。但是Condition是可以选择性通知的。会节省一部分的效率。
- 在使用condition.await()方法之前必须调用lock.lock()方法获得同步监视器
- 获得Lock实例
private Lock lock = new ReentrantLock();
- 获得Condition实例
private Condition condition = lock.newCondition();
2.3 ReentrantLock和Condition实现简单等待/通知
- 测试类:
public class waitNotify {
Lock lock = new ReentrantLock();
Condition condition = lock.newCondition();
public void await(){
try {
lock.lock();
System.out.println(Thread.currentThread().getName() + "准备进行等待");
condition.await();
System.out.println(Thread.currentThread().getName() + "结束等待");
lock.unlock();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public void signalAll(){
lock.lock();
System.out.println(Thread.currentThread().getName() + "准备通知");
condition.signal();
System.out.println(Thread.currentThread().getName() + "通知完后在再释放锁");
System.out.println(Thread.currentThread().getName() + "通知结束");
lock.unlock();
}
}
等待线程:
public class awaitThread extends Thread {
waitNotify waitNotify;
public awaitThread(waitNotify waitNotify){
this.waitNotify = waitNotify;
}
@Override
public void run() {
waitNotify.await();
}
}
通知线程:
public class signalThread extends Thread{
waitNotify waitNotify;
public signalThread(waitNotify waitNotify){
this.waitNotify = waitNotify;
}
@Override
public void run() {
waitNotify.signalAll();
}
}
主类:
public class mainTest1 {
public static void main(String[] args) {
try {
waitNotify waitNotify = new waitNotify();
new awaitThread(waitNotify).start();
Thread.sleep(1000);
new signalThread(waitNotify).start();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
结果:
Thread-0准备进行等待
Thread-1准备通知
Thread-1通知完后在再释放锁
Thread-1通知结束
Thread-0结束等待
2.3 多个Condition实现通知部分线程
- signalAll()
唤醒所有线程
测试:
public class waitNotify {
Lock lock = new ReentrantLock();
Condition conditionA = lock.newCondition();
Condition conditionB = lock.newCondition();
public void awaitA(){
try {
lock.lock();
//System.out.println(Thread.currentThread().getName() + "conditionA准备进行等待");
System.out.println("A在等待");
conditionA.await();
System.out.println("A被唤醒");
//System.out.println(Thread.currentThread().getName() + "conditionA结束等待");
lock.unlock();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public void signalAllA(){
lock.lock();
//System.out.println(Thread.currentThread().getName() + "conditionA准备通知");
conditionA.signalAll();
System.out.println("执行A的signalAll方法");
//System.out.println(Thread.currentThread().getName() + "conditionA通知完后在再释放锁");
//System.out.println(Thread.currentThread().getName() + "conditionA通知结束");
lock.unlock();
}
public void awaitB(){
try {
lock.lock();
//System.out.println(Thread.currentThread().getName() + "conditionB准备进行等待");
System.out.println("B在等待");
conditionB.await();
System.out.println("B被唤醒");
System.out.println(Thread.currentThread().getName() + "conditionB结束等待");
lock.unlock();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public void signalAllB(){
lock.lock();
//System.out.println(Thread.currentThread().getName() + "conditionB准备通知");
conditionB.signalAll();
System.out.println("执行B的signalAll方法");
//System.out.println(Thread.currentThread().getName() + "conditionB通知完后在再释放锁");
//System.out.println(Thread.currentThread().getName() + "conditionB通知结束");
lock.unlock();
}
}
wait线程:
public class awaitThread extends Thread {
waitNotify waitNotify;
public awaitThread(waitNotify waitNotify){
this.waitNotify = waitNotify;
}
@Override
public void run() {
try {
waitNotify.awaitA();
Thread.sleep(4000);
waitNotify.awaitB();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
signal线程:
public class signalThread extends Thread{
waitNotify waitNotify;
public signalThread(waitNotify waitNotify){
this.waitNotify = waitNotify;
}
@Override
public void run() {
waitNotify.signalAllA();
}
}
主方法:
public class mainTest1 {
public static void main(String[] args) {
try {
waitNotify waitNotify = new waitNotify();
new awaitThread(waitNotify).start();
Thread.sleep(1000);
new signalThread(waitNotify).start();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
结果:
A在等待
执行A的signalAll方法
A被唤醒
B在等待
.........
2.4 实现生产者一对一
测试类:
public class produceCost {
Lock lock = new ReentrantLock();
Condition condition = lock.newCondition();
boolean flag;
public void showA() {
try {
lock.lock();
while (flag) {
condition.await();
System.out.println(Thread.currentThread().getName() + " " + "*****");
}
flag = true;
condition.signalAll();
} catch (InterruptedException e) {
e.printStackTrace();
}finally {
lock.unlock();
}
}
public void showB() {
try {
lock.lock();
while (!flag) {
condition.await();
System.out.println(Thread.currentThread().getName() + " " + "-----");
}
flag = false;
condition.signalAll();
} catch (InterruptedException e) {
e.printStackTrace();
}finally {
lock.unlock();
}
}
}
线程A:
public class A extends Thread {
produceCost produceCost;
public A(produceCost produceCost){
this.produceCost = produceCost;
}
@Override
public void run() {
while (true) {
produceCost.showA();
}
}
}
线程B:
public class B extends Thread {
produceCost produceCost;
public B(produceCost produceCost){
this.produceCost = produceCost;
}
@Override
public void run() {
while (true) {
produceCost.showB();
}
}
}
主类:
public class test2 {
public static void main(String[] args) {
produceCost produceCost = new produceCost();
int j = 0;
A a = new A(produceCost);
a.setName("A");
a.start();
B b = new B(produceCost);
b.setName("B");
b.start();
}
}
结果:
A *****
B -----
A *****
B -----
A *****
B -----
........
说明:
生产者和消费者各有一组通知/等待方法
2.5 生产者/消费者:多对多交替打印
测试类:
public class produceCost {
Lock lock = new ReentrantLock();
Condition condition = lock.newCondition();
boolean flag;
public void showA() {
try {
lock.lock();
while (flag) {
condition.await();
}
flag = true;
System.out.println(Thread.currentThread().getName() + " " + "*****");
condition.signalAll();
} catch (InterruptedException e) {
e.printStackTrace();
}finally {
lock.unlock();
}
}
public void showB() {
try {
lock.lock();
while (!flag) {
condition.await();
}
System.out.println(Thread.currentThread().getName() + " " + "-----");
flag = false;
condition.signalAll();
} catch (InterruptedException e) {
e.printStackTrace();
}finally {
lock.unlock();
}
}
}
主类:
public class test2 {
public static void main(String[] args) {
produceCost produceCost = new produceCost();
int j = 0;
for (int i = 0; i < 10; i++) {
A a = new A(produceCost);
B b = new B(produceCost);
a.setName("A" +i);
b.setName("B" +i);
a.start();
b.start();
}
}
}
结果:
A1 *****
B2 -----
A7 *****
B3 -----
A1 *****
B2 -----
A7 *****
说明:打印语句放在nofityAll()方法前执行,不能在wait方法后执行,因为notifyAll()方法会唤醒同类线程导致打印语句出错。
2.6 公平锁与非公平锁
- 公平锁(创建Reentrantlock时参数为true)
公平锁表示线程获取锁的顺序是按照线程加锁的顺序来分配的 - 非公平锁 (正常创建ReentrantLock即可)
获取锁的抢占机制,即是随机获取锁的,可能造成某些线程一直拿不到锁,所以就不公平了。
测试类:
public class fairLock {
Lock lock = new ReentrantLock(true);
public void doSth(){
lock.lock();
System.out.println(Thread.currentThread().getName() + "获的锁子");
lock.unlock();
}
}
线程:
public class orignThread extends Thread {
fairLock fairLock;
public orignThread(fairLock fairLock){
this.fairLock = fairLock;
}
@Override
public void run() {
fairLock.doSth();
}
}
主类:
public class test3 {
public static void main(String[] args) {
fairLock fairLock = new fairLock();
orignThread thead[] = new orignThread[10];
for (int i = 0; i < 10; i++) {
thead[i] = new orignThread(fairLock);
thead[i].setName("A" + i);
}
for (int i = 0; i < 10; i++) {
thead[i].start();
}
}
}
结果:
A0获的锁子
A1获的锁子
A2获的锁子
A7获的锁子
A3获的锁子
A4获的锁子
A5获的锁子
A6获的锁子
A9获的锁子
A8获的锁子
说明:基本上是按加锁的顺序获的锁,测得时候尽量多开几个线程,100个以上才能看出效果
三、ReentrantLock的其他方法
3.1 getHoldCount()
- 查询当前线程保持此锁定的个数,也就是调用lock()方法的次数
public class fairLock {
ReentrantLock lock = new ReentrantLock();
public void doSth(){
lock.lock();
System.out.println(Thread.currentThread().getName() + "获的锁子" + lock.getHoldCount());
lock.unlock();
}
}
输出:
A0获的锁子1
A3获的锁子1
A1获的锁子1
A4获的锁子1
A2获的锁子1
A6获的锁子1
..........
说明:
每个线程都只获得了一次锁
3.2 int getQueueLength()
- 返回正等待获取次锁定的个数
public class fairLock {
ReentrantLock lock = new ReentrantLock();
public void doSth(){
lock.lock();
System.out.println(Thread.currentThread().getName() + " " + lock.getQueueLength());
lock.unlock();
}
}
结果:
A0 0
A50 49
A52 50
A1 50
A55 50
A2 49
.........
3.3 int getWaitQueueLength(Condition condition)
- 返回等待与此锁定相关的给定条件Condition的线程估计数
测试:
public class fairLock {
ReentrantLock lock = new ReentrantLock();
Condition condition = lock.newCondition();
public void doSth(){
try {
lock.lock();
System.out.println("正在等待的个数" + " " + lock.getWaitQueueLength(condition));
condition.await();
lock.unlock();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
结果:
正在等待的个数 0
正在等待的个数 1
正在等待的个数 2
正在等待的个数 3
正在等待的个数 4
正在等待的个数 5
正在等待的个数 6
正在等待的个数 7
.........
3.4 boolean hasQueuedThread(Thread thread)
- 查询指定的线程是否正在等待获取次锁定
测试:
public class fairLock {
ReentrantLock lock = new ReentrantLock();
Condition condition = lock.newCondition();
public void doSth(){
try {
lock.lock();
Thread.sleep(100000);
condition.await();
lock.unlock();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
主类:
public class test3 {
public static void main(String[] args) {
try {
fairLock fairLock = new fairLock();
orignThread thead[] = new orignThread[100];
for (int i = 0; i < 2; i++) {
thead[i] = new orignThread(fairLock);
thead[i].setName("A" + i);
}
for (int i = 0; i < 2; i++) {
thead[i].start();
}
Thread.sleep(1000);
System.out.println("是否正在等待此锁定" + fairLock.lock.hasQueuedThread(thead[1]));
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
结果:
是否正在等待此锁定true
3.5 boolean hasWaiters(Condition condition)
- 查询是否有线程等待与此锁定有关的Condition条件
测试类:
public class fairLock {
ReentrantLock lock = new ReentrantLock();
Condition condition = lock.newCondition();
public void doSth(){
try {
lock.lock();
condition.await();
lock.unlock();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public void getWaiters(){
lock.lock();
System.out.println(lock.hasWaiters(condition));
lock.unlock();
}
}
主类:
public class test3 {
public static void main(String[] args) {
try {
fairLock fairLock = new fairLock();
orignThread thead[] = new orignThread[100];
for (int i = 0; i < 2; i++) {
thead[i] = new orignThread(fairLock);
thead[i].setName("A" + i);
}
for (int i = 0; i < 2; i++) {
thead[i].start();
}
Thread.sleep(1000);
fairLock.getWaiters();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
结果:
true
说明:
该方法必须在同步方法中进行。
3.6 其他方法
- isFair()
判断锁是不是公平锁
lock.isFair()
- isHeldByCurrentThread()
查询当前线程是否保持此锁定
lock.isHeldByCurrentThread()
- isLocked()
查询此所是否由任意线程保持
lock.isLocked()
- lockInterruptibly()
如果当前线程未被中断,则获得此锁定,如果中断则出现异常
lock.lockInterruptibly()
- tryLock()
仅在调用时锁定未被另一个线程保持的情况下,才获取该锁定。
3.6 使用Condition实现顺序执行
测试类:
public class ConditionTest {
Lock lock = new ReentrantLock();
Condition condition = lock.newCondition();
static int i = 0;
public void run2(){
try {
lock.lock();
while ( i % 3 != 0) {
condition.await();
}
System.out.println(Thread.currentThread().getName() + "*****");
i = i + 1;
condition.signalAll();
} catch (InterruptedException e) {
e.printStackTrace();
}finally {
lock.unlock();
}
}
public void run3(){
try {
lock.lock();
while ( i % 3 != 1) {
condition.await();
}
System.out.println(Thread.currentThread().getName() + "-----");
i = i + 1;
condition.signalAll();
} catch (InterruptedException e) {
e.printStackTrace();
}finally {
lock.unlock();
}
}
public void run1(){
try {
lock.lock();
while ( i % 3 != 2) {
condition.await();
}
System.out.println(Thread.currentThread().getName() + "+++++");
i = i + 1;
condition.signalAll();
} catch (InterruptedException e) {
e.printStackTrace();
}finally {
lock.unlock();
}
}
};
线程A:
public class ThreadA extends Thread{
ConditionTest conditionTest;
public ThreadA(ConditionTest conditionTest){
this.conditionTest = conditionTest;
}
@Override
public void run() {
while (true) {
conditionTest.run1();
}
}
}
线程B:
public class ThreadB extends Thread {
ConditionTest conditionTest;
public ThreadB(ConditionTest conditionTest){
this.conditionTest = conditionTest;
}
@Override
public void run() {
while (true) {
conditionTest.run2();
}
}
}
线程C:
public class ThreadC extends Thread {
ConditionTest conditionTest;
public ThreadC(ConditionTest conditionTest){
this.conditionTest = conditionTest;
}
@Override
public void run() {
while (true) {
conditionTest.run3();
}
}
}
主方法:
public class main {
public static void main(String[] args) {
try {
ConditionTest conditionTest = new ConditionTest();
ThreadA threadA = new ThreadA(conditionTest);
ThreadB threadB = new ThreadB(conditionTest);
ThreadC threadC = new ThreadC(conditionTest);
threadA.setName("A");
threadB.setName("B");
threadC.setName("C");
threadA.start();
threadB.start();
threadC.start();
} catch (Exception e) {
e.printStackTrace();
}
}
}
结果:
..........
C-----
A+++++
B*****
C-----
A+++++
B*****
C-----
A+++++
B*****
C-----
..........
四、ReentrantReadWriteLock类
4.1 ReentrantLock类的缺点
ReentrantLock具有完全互斥排他的效果,即在同一时间只有一个线程在执行ReentrantLock.lock()方法后的任务,效率十分低下,所以提供了读写锁类ReentrantReadWriteLock。
4.2 ReentrantReadWriteLock类的锁
有两个锁,一个是读操作相关的锁,也称为共享锁,一个是写操作相关的锁,也叫排它锁,也就是多个读锁之间不互斥,而多个写锁之间互斥,读锁和写锁之间也互斥。在没有写入操作时,读操作的多个Thread都可以获得读锁,而进行写操作,必须获得写锁,即多个Thread可以同时进行读操作,但是同一时刻,只能有一个线程进行写操作。
4.3 读读共享
主类:
public class ReadLock {
ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
public void readSth(){
try {
lock.readLock().lock();
System.out.println(new Date(System.currentTimeMillis()) + "进行读操作");
Thread.sleep(4000);
} catch (InterruptedException e) {
e.printStackTrace();
}finally {
lock.readLock().unlock();
}
}
}
线程A:
public class A extends Thread {
ReadLock readLock;
public A(ReadLock readLock) {
this.readLock = readLock;
}
@Override
public void run() {
readLock.readSth();
}
}
线程B:
public class A extends Thread {
ReadLock readLock;
public A(ReadLock readLock) {
this.readLock = readLock;
}
@Override
public void run() {
readLock.readSth();
}
}
主类:
public class test2 {
public static void main(String[] args) {
ReadLock readLock = new ReadLock();
A a = new A(readLock);
B b = new B(readLock);
a.start();
b.start();
}
}
结果:
Sat Aug 10 16:55:46 CST 2019进行读操作
Sat Aug 10 16:55:46 CST 2019进行读操作
说明:两个线程是同一时间进行操作的,所以操作读的多个线程可以同时拥有读锁。
4.4 写写互斥
测试类:
public class WriteLockTest {
ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
public void readSth(){
try {
lock.writeLock().lock();
System.out.println(new Date(System.currentTimeMillis()) + "进行读操作");
Thread.sleep(4000);
} catch (InterruptedException e) {
e.printStackTrace();
}finally {
lock.writeLock().unlock();
}
}
}
结果:
Sat Aug 10 17:02:58 CST 2019进行读操作
Sat Aug 10 17:03:02 CST 2019进行读操作
说明:两个写操作是互斥进行的。
4.5 读写/写读互斥
测试类:
public class ReadWriteLockTest {
ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
public void writeSth(){
try {
lock.writeLock().lock();
System.out.println(new Date(System.currentTimeMillis()) + "进行写操作");
Thread.sleep(4000);
} catch (InterruptedException e) {
e.printStackTrace();
}finally {
lock.writeLock().unlock();
}
}
public void readSth(){
try {
lock.readLock().lock();
System.out.println(new Date(System.currentTimeMillis()) + "进行读操作");
Thread.sleep(4000);
} catch (InterruptedException e) {
e.printStackTrace();
}finally {
lock.readLock().unlock();
}
}
}
结果:
Sat Aug 10 17:07:08 CST 2019进行读操作
Sat Aug 10 17:07:12 CST 2019进行写操作
说明:
读写操作是互斥进行的。
上一篇: 解决MySQL数据库中与优化器有关的问题
下一篇: 如何在oracle 11g 中导出空表