在JDK5中增加了Lock锁接口,有ReentrantLock实现类,ReentrantLock锁称为可重入锁,它功能比synchronized 多。
锁的可重入是指,当一个线程获得一个对象锁后,再次请求该对象锁时是可以获得该对象的锁的。
/**
* 演示锁的可重入性
*/
public class Test01 {
public synchronized void sm1 () {
System.out.println("同步方法1");
// 线程执行sm1 方法,默认this作为锁对象,在sm1 方法中调用了sm2 方法,注意当前线程还是持有this锁对象的
// sm2 同步方法默认的锁对象也是this对象,要执行sm2 必须先获得this 锁对象,当前this 对象被当前线程持有,
// 可以再次获得this对象,这就是锁的可重入性。假设,锁不可重入的话,可能会造成死锁。
sm2();
}
private synchronized void sm2 () {
System.out.println("同步方法2");
sm3();
}
private synchronized void sm3 () {
System.out.println("同步方法3");
}
public static void main(String[] args) {
Test01 test01 = new Test01();
new Thread(new Runnable() {
@Override
public void run() {
test01.sm1();
}
}).start();
}
}
调用lock方法获得锁,调用unlock 方法释放锁。
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
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();
}
};
// 启动3个线程
new Thread(r).start();
new Thread(r).start();
new Thread(r).start();
}
}
/**
* 使用lock锁同步不同方法中的同步代码块
*/
public class Test03 {
static Lock lock = new ReentrantLock();// 定义锁对象
public static void sm1() {
// 经常在try代码块中获得 lock 锁,经常在finally子句中释放锁
try{
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 锁的可重入性
*/
public class Test04 {
static class Subthread extends Thread {
private static Lock lock = new ReentrantLock(); // 定义锁对象
public static int num = 0; // 定义变量
@Override
public void run() {
for (int i = 0; i < 10000; i++) {
try{
// 可重入锁指可以反复获得该锁
lock.lock();
lock.lock();
num++;
} finally {
lock.unlock();
lock.unlock();
}
}
}
}
public static void main(String[] args) throws InterruptedException {
Subthread t1 = new Subthread();
Subthread t2 = new Subthread();
t1.start();
t2.start();
t1.join();
t2.join();
System.out.println(Subthread.num);
}
}
lockInterruptibly() 方法的作用:如果当前线程未被中断则获得锁,如果当前线程被中断则出现异常
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
/**
* lockInterruptibly() 方法的作用: 如果当前线程未被中断则获得锁,如果当前线程被中断则出现异常.
*/
public class Test05 {
static class Servier {
private Lock lock = new ReentrantLock(); // 定义锁对象
public void serviceMethod(){
try {
// lock.lock(); // 获得锁定,即使调用了线程的interrupt()方法,他也没有真正的中断线程
lock.lockInterruptibly(); // 如果线程被中断了,不会获得锁,会产生异常;
System.out.println(Thread.currentThread().getName() +" -- begin lock");
// 执行一段耗时的操作
for (int i = 0; i < Integer.MAX_VALUE; i++) {
new StringBuilder();
}
System.out.println(Thread.currentThread().getName() + "--end lock");
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock(); // 释放锁
System.out.println(Thread.currentThread().getName() +"-- 释放锁");
}
}
}
public static void main(String[] args) throws InterruptedException {
Servier servier = new Servier();
Runnable runnable = new Runnable() {
@Override
public void run() {
servier.serviceMethod();
}
};
Thread t1 = new Thread(runnable);
t1.start();
Thread.sleep(50);
Thread t2 = new Thread(runnable);
t2.start();
Thread.sleep(50);
t2.interrupt(); // 中断t2 线程
}
}
运行结果:
对于synchronized 内部锁来说,如果一个线程在等待锁,只有两个结果:要么该线程获得锁继续执行,要么就继续等待。
对于ReentrantLock 可重入锁来说,提供另外一种可能,在等待锁的过程中,程序可以根据需要取消对锁的请求。
lockInterruptibly() 解决死锁问题
import java.util.Random;
import java.util.concurrent.locks.ReentrantLock;
/**
* 通过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.lock();
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 {
// 偶数, 先锁2 再锁1
lock2.lockInterruptibly();
System.out.println(Thread.currentThread().getName() + "获得锁2,还需要获得锁1");
Thread.sleep(new Random().nextInt(500));
lock1.lockInterruptibly();
System.out.println(Thread.currentThread().getName() +"获得了锁1与锁2>>>");
}
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
if(lock1.isHeldByCurrentThread()) {
// 判断当前线程是否持有该锁,如果持有就释放锁
lock1.unlock();
}
if (lock2.isHeldByCurrentThread()) {
lock2.unlock();
}