ReentraantLock
调用lock方法,获得锁,调用unlock方法释放锁
一、lock的基本使用
- 相当于同步代码块实现线程同步
- 与同步代码块synchronized一样,但是锁要是一把锁
package com.dome.lock.reentrant;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
/**
* @author qb
* @version 1.0
* @Description
* 锁的基本使用
* @date 2021/3/11 9:27
*/
public class Test02 {
//定义显示锁
static Lock lock = new ReentrantLock();
//定义方法
public static void sm(){
//先获得锁
lock.lock();
//for 同步代码
for (int i = 0; i < 100; i++) {
System.out.println(Thread.currentThread().getName() +"---"+i);
}
//释放锁
lock.unlock();
}
public static void main(String[] args) {
Runnable r = new Runnable() {
@Override
public void run() {
sm();
}
};
new Thread(r).start();
new Thread(r).start();
new Thread(r).start();
}
}
执行结果
package com.dome.lock.reentrant;
import java.util.Random;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
/**
* @author qb
* @version 1.0
* @Description
* @date 2021/3/11 9:33
*/
public class Test03 {
static Lock lock = new ReentrantLock();
public static void sm1(){
//经常在try代码块中获得lock锁后,经常在finally子句中释放锁
try {
lock.lock();//获得锁 lock是同一个lock
System.out.println(Thread.currentThread().getName() +"--method 1 "+System.currentTimeMillis());
Thread.sleep(new Random().nextInt(1000));
System.out.println(Thread.currentThread().getName() +"--method 1 "+System.currentTimeMillis());
} catch (InterruptedException e) {
e.printStackTrace();
}finally {
lock.unlock(); //释放锁
}
}
public static void sm2(){
//经常在try代码块中获得lock锁后,经常在finally子句中释放锁
try {
lock.lock();//获得锁
System.out.println(Thread.currentThread().getName() +"--method 2 "+System.currentTimeMillis());
Thread.sleep(new Random().nextInt(1000));
System.out.println(Thread.currentThread().getName() +"--method 2 "+System.currentTimeMillis());
} catch (InterruptedException e) {
e.printStackTrace();
}finally {
lock.unlock(); //释放锁
}
}
public static void main(String[] args) {
Runnable r1 = new Runnable() {
@Override
public void run() {
sm1();
}
};
Runnable r2 = new Runnable() {
@Override
public void run() {
sm2();
}
};
new Thread(r1).start();
new Thread(r1).start();
new Thread(r1).start();
new Thread(r2).start();
new Thread(r2).start();
new Thread(r2).start();
}
}
执行结果
二、ReentrantLock锁的可重入性
dome
package com.dome.lock.reentrant;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
/**
* @author qb
* @version 1.0
* @Description
* ReentrantLock 锁的可重入性
* @date 2021/3/11 9:41
*/
public class Test04 {
static class SubThread extends Thread{
// private Lock lock = new ReentrantLock(); //锁对象 // private 锁 不是同一把锁还是不同步
private static Lock lock = new ReentrantLock(); //锁对象
public static int num = 0; //定义变量
@Override
public void run() {
for (int i = 0; i < 1000; i++) {
try{
//可重入锁指的是可以反复获得该锁,也要全部释放
lock.lock();
lock.lock();
num++;
}finally {
lock.unlock();
lock.unlock();
}
}
}
}
public static void main(String[] args) {
SubThread t1 = new SubThread();
SubThread t2 = new SubThread();
t1.start();
t2.start();
try {
t1.join();
t2.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(SubThread.num);
}
}
执行结果
三、 lockInterruptibly方法使用
lockInterruptibly()方法的作用:如果当前线程未被中断则获取锁,如果当前线程被中断则抛出异常
dome
package com.dome.lock.reentrant;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
/**
* @author qb
* @version 1.0
* @Description
* lockInterruptibly
* @date 2021/3/11 9:52
*/
public class Test05 {
static class Service{
private Lock lock = new ReentrantLock(); //锁对象
public void serviceMethod(){
try {
//lock.lock(); //既是调用了线程的interrupt方法,它也没有真正的中断
lock.lockInterruptibly(); //如果线程被中断了,它不会获得锁,会产生异常
System.out.println(Thread.currentThread().getName()+"--开始 lock");
//执行耗时操作
for (int i = 0; i < Integer.MAX_VALUE; i++) {
new StringBuilder();
}
System.out.println(Thread.currentThread().getName() +"---结束 lock");
} catch (InterruptedException e){
e.printStackTrace();
}finally {
System.out.println(Thread.currentThread().getName()+"****释放锁");
lock.unlock();
}
}
}
public static void main(String[] args) {
Service s = new Service();
Runnable r = new Runnable() {
@Override
public void run() {
s.serviceMethod();
}
};
Thread t1 = new Thread(r);
t1.start();
try {
Thread.sleep(50);
} catch (InterruptedException e) {
e.printStackTrace();
}
Thread t2 = new Thread(r);
t2.start();
try {
Thread.sleep(50);
} catch (InterruptedException e) {
e.printStackTrace();
}
t2.interrupt(); //中断t2线程
}
}
执行结果
四、lockInterruptibly可以解决死锁
- 对于synchronized内部锁来说,如果一个线程在等待锁,只有两个结果:要么线程获得锁继续执行,要么就保持等待
- 对于ReentrantLock可重入锁来说,提供另外一种可能,在等待锁的过程中,程序可以根据需要取消对锁的请求
dome
package com.dome.lock.reentrant;
import java.util.Random;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
/**
* @author qb
* @version 1.0
* @Description
* @date 2021/3/11 10:10
*/
/**
* 通过ReentrantLock的lockInterruptibly方法,避免死锁
*/
public class Test06 {
static class IntLock implements Runnable{
//创建连个锁对象
public static ReentrantLock lock1 = new ReentrantLock();
public static ReentrantLock lock2 = new ReentrantLock();
int lockNum; //决定使用哪儿个锁
public IntLock(int lockNum) {
this.lockNum = lockNum;
}
@Override
public void run() {
try {
if(lockNum % 2 == 1){ //奇数 先锁1,在锁2
lock1.lockInterruptibly();
System.out.println(Thread.currentThread().getName()+"获得锁1,还要获得锁2");
Thread.sleep(new Random().nextInt(500));
lock2.lockInterruptibly();
System.out.println(Thread.currentThread().getName()+"同时获得了锁1和锁2");
}else{//偶数
lock2.lockInterruptibly();
System.out.println(Thread.currentThread().getName()+"获得锁2,还要获得锁1");
Thread.sleep(new Random().nextInt(500));
lock1.lockInterruptibly();
System.out.println(Thread.currentThread().getName()+"同时获得了锁2和锁1");
}
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
if(lock1.isHeldByCurrentThread()){
lock1.unlock();
}
//如果当前线程持有锁,就释放
if(lock2.isHeldByCurrentThread()){
lock2.unlock();
}
System.out.println(Thread.currentThread().getName() +"线程退出");
}
}
}
public static void main(String[] args) throws InterruptedException {
IntLock intLock1 = new IntLock(11);
IntLock intLock2 = new IntLock(22);
Thread t1 = new Thread(intLock1);
Thread t2 = new Thread(intLock2);
t1.start();
t2.start(); //产生死锁
//在main线程中等待3000秒,如果有线程还没有获得锁就中断改线程
//将lock 换为lockInterruptibly
Thread.sleep(3000);
/* if(t1.isAlive()){
t1.interrupt();
}*/
//t2就放弃对锁1的申请,同时释放锁二,t1线程完成任务
if(t2.isAlive()){
t2.interrupt();
}
}
}
执行结果
五、 tryLock() 基本使用
- tryLock: 作用在给定等待时长内,锁没有被另外的线程持有,并且当前线程也没有被中断,则获得该锁,通过该方法可以实现锁对象的限时等待
- tryLock仅在调用时,锁定未被其他线程持有的的锁,如果调用方法时锁对象被其他线程持有则放弃,调用方法尝试获得锁,如果该锁没有被其他线程调用,则返回true表示锁定成功,如果返回false,则不等待,获取锁失败
- tryLock()没有时长参数的就尝试一次
dome
package com.dome.lock.reentrant;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.ReentrantLock;
/**
* @author qb
* @version 1.0
* @Description
* tryLock 使用
* @date 2021/3/11 10:31
*/
public class Test07 {
static class TimeLock implements Runnable{
private static ReentrantLock lock = new ReentrantLock();
@Override
public void run() {
try {
if(lock.tryLock(3, TimeUnit.SECONDS)){ //获得锁返回true
System.out.println(Thread.currentThread().getName()+"获得锁,执行耗时任务");
//假设Thread-0先尺有所,完成任务4秒钟,Thread-1,在3秒钟没有获得锁,便放弃
// Thread.sleep(4000);
Thread.sleep(2000);
}else{
System.out.println(Thread.currentThread().getName()+"没有获得锁");
}
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
if(lock.isHeldByCurrentThread()){ //如果当前线程持有锁
lock.unlock();
}
}
}
}
public static void main(String[] args) {
TimeLock timeLock = new TimeLock();
Thread t1 = new Thread(timeLock);
Thread t2 = new Thread(timeLock);
t1.start();
t2.start();
}
}
执行结果
避免死锁
package com.dome.lock.reentrant;
import java.util.Random;
import java.util.concurrent.locks.ReentrantLock;
/**
* @author qb
* @version 1.0
* @Description
*
* 使用tryLock()避免死锁
*
* @date 2021/3/11 10:49
*/
public class Test08 {
static class IntLock implements Runnable{
//创建连个锁对象
public static ReentrantLock lock1 = new ReentrantLock();
public static ReentrantLock lock2 = new ReentrantLock();
int lockNum; //决定使用哪儿个锁
public IntLock(int lockNum) {
this.lockNum = lockNum;
}
@Override
public void run() {
if(lockNum % 2 == 0){ //偶数 先锁1 在锁2
while (true){
try {
if(lock1.tryLock()){
System.out.println(Thread.currentThread().getName()+"获得锁1,还想获得锁2");
Thread.sleep(new Random().nextInt(100));
try {
if(lock2.tryLock()){
System.out.println(Thread.currentThread().getName()+"同时获得锁1和锁2");
return; //结束run方法,
}
} finally {
if(lock2.isHeldByCurrentThread()){
lock2.unlock();
}
}
}
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
if(lock1.isHeldByCurrentThread()){
lock1.unlock();
}
}
}
}else{
while (true){
try {
if(lock2.tryLock()){
System.out.println(Thread.currentThread().getName()+"获得锁2,还想获得锁1");
Thread.sleep(new Random().nextInt(100));
try {
if(lock1.tryLock()){
System.out.println(Thread.currentThread().getName()+"同时获得锁1和锁2");
return; //结束run方法,
}
} finally {
if(lock1.isHeldByCurrentThread()){
lock1.unlock();
}
}
}
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
if(lock2.isHeldByCurrentThread()){
lock2.unlock();
}
}
}
}
}
}
public static void main(String[] args) {
IntLock intLock1 = new IntLock(11);
IntLock intLock2 = new IntLock(22);
Thread t1 = new Thread(intLock1);
Thread t2 = new Thread(intLock2);
t1.start();
t2.start();
//运行后,使用tryLock尝试获得锁,不会傻傻的等待,使用循环不停地再次尝试,只要等待的时间足够长,就会获取想要的资源
}
}