互斥锁:
在上节中我们谈到了Object类的notify()方法随机唤醒单个线程,而不是唤醒指定线程,这就会导致一个问题,比如三个线程A,B,C,A在执行线程体,B,C在等待,A执行完该B执行了,notify方法随机唤醒一个线程,显然不能用,notifyAll方法把两个线程都唤醒,然后C再次设置成等待,这不太好.
为了解决不能唤醒指定线程的问题,jdk5.0的增加了ReenTrantLock类和Condition接口来替换synchronized关键字和wait、notify方法。
先看ReenTrantLock类,暂时不深入分析
ReenTrantLock类:在java.util.concurrent.locks包下面,java.util.concurrent包中是常用的并发控制类
构造方法:
public ReentrantLock(),创建非公平锁对象,随机竞争来得到锁,先lock的线程不一定先获得锁,默认的构造方法。
public ReentrantLock(boolean fair) ,创建公平锁对象,按线程加锁的顺序来获取锁
构造方法的源码如下(jdk11):
public ReentrantLock() {
sync = new NonfairSync();
}
public ReentrantLock(boolean fair) {
sync = fair ? new FairSync() : new NonfairSync();
}
//NonfairSync和NonfairSyn两个类是ReenTrantLock的内部类
列举了几个常用方法:
void lock()如果锁可以, 对线程上锁., 如果锁已被其他线程占有, 暂时禁用当前线程, 直到当前线程获取到锁,再对线程上锁.
unvoid unlock()对线程解锁,持有锁的线程才能释放锁
boolean tryLock(): 此方法是尝试获取锁,如果锁可用, 则对该线程上锁返回true, 否则返回false. 锁被其他线程占有时, 不会禁用当前线程, 当前线程继续往下执行代码.
Condition newCondition()返回一个Condition实例
getHoldCount() 查询当前线程保持住此锁的次数,当前线程执行(lock和unlock)的次数
getQueueLength()返回正等待获取此锁的线程预估计最大数,比如启动10个线程,1个线程获得锁,此时返回的是9
hasQueuedThread(Thread thread)返回给定的线程是否在等待获取此锁
hasQueuedThreads()是否有线程等待此锁
isFair()判断该锁是否为公平锁
isLock()此锁是否被别的线程获取到
Condition接口:在java.util.concurrent.locks包下
Condition是监视器接口,Condition对象是由lock对象创建,同一个锁Lock对象可创建多个Condition的对象,即创建多个对象监视器,每个线程一个监视器。await方法和signal方法用来让指定线程等待和唤醒指定线程.跟wait/notify相同的是,await/signal也是在同步代码区内执行.
常用方法:
void await() throws InterruptedException让指定线程等待
void signal()唤醒指定的正在等待的线程
void signalAll()唤醒所有正在等待的线程
示例:两个线程轮流打印0-100
package chen_chapter_9;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;
public class ReenTrantLockTest01 {
static PrintNum pn = null;// 内部类调用静态成员变量
public static void main(String[] args) {
pn = new PrintNum();
Thread t1 = new Thread("线程t1") {
@Override
public void run() {
pn.print1();
}
};
Thread t2 = new Thread("线程t2") {
@Override
public void run() {
pn.print2();
}
};
t1.start();
t2.start();
}
}
class PrintNum {
private ReentrantLock r1 = new ReentrantLock();//创建互斥锁对象
private Condition c1 = r1.newCondition();//创建多个线程的监视器对象
private Condition c2 = r1.newCondition();
private int i = 0;
private boolean flag = true;
public void print1() {
while (i < 100) {
r1.lock();//lock()和unlock()之间的内容类似于以前的synchronized同步代码块
if (!flag) {
flag = !flag;
try {
c1.await();//指定这个线程等待
} catch (InterruptedException e) {
e.printStackTrace();
}
} else {
flag = !flag;
System.out.println(Thread.currentThread().getName() + " : " + i++);
c2.signal();//唤醒另一个线程
}
r1.unlock();
}
}
public void print2() {
while (i < 100) {
r1.lock();
if (!flag) {
flag = !flag;
try {
c2.await();//指定这个线程等待
} catch (InterruptedException e) {
e.printStackTrace();
}
} else {
flag = !flag;
System.out.println(Thread.currentThread().getName() + " : " + i++);
c1.signal();//唤醒另一个线程
}
r1.unlock();
}
}
}
示例:三个线程轮流打印0-100
package chen_chapter_9;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;
public class ReenTrantLockTest2 {
static PrintNum1 pn = null;// 内部类调用静态成员变量
public static void main(String[] args) {
pn = new PrintNum1();
Thread t1 = new Thread("线程t1") {
@Override
public void run() {
pn.print1();
}
};
Thread t2 = new Thread("线程t2") {
@Override
public void run() {
pn.print2();
}
};
Thread t3 = new Thread("线程t3") {
@Override
public void run() {
pn.print3();
}
};
t1.start();
t2.start();
t3.start();
}
}
class PrintNum1 {
private ReentrantLock r1 = new ReentrantLock();// 创建互斥锁对象
private Condition c1 = r1.newCondition();// 创建多个线程的监视器对象
private Condition c2 = r1.newCondition();
private Condition c3 = r1.newCondition();
private int i = 0;
private int flag = 1;
public void print1() {
while (i < 100) {
r1.lock();// lock()和unlock()之间的内容类似于以前的synchronized同步代码块
if (flag != 1) { // 三个线程中,保证对同一个变量,只有一个不进入等待状态
try {
c1.await();// 指定这个线程等待
} catch (InterruptedException e) {
e.printStackTrace();
}
} else {
System.out.println(Thread.currentThread().getName() + " : " + i++);
flag = 2;
c2.signal();// 唤醒另一个线程
}
r1.unlock();
}
}
public void print2() {
while (i < 100) {
r1.lock();
if (flag != 2) {
try {
c2.await();// 指定这个线程等待
} catch (InterruptedException e) {
e.printStackTrace();
}
} else {
System.out.println(Thread.currentThread().getName() + " : " + i++);
flag = 3;
c3.signal();// 唤醒另一个线程
}
r1.unlock();
}
}
public void print3() {
while (i < 100) {
r1.lock();
if (flag != 3) {
try {
c3.await();// 指定这个线程等待
} catch (InterruptedException e) {
e.printStackTrace();
}
} else {
System.out.println(Thread.currentThread().getName() + " : " + i++);
flag = 1;
c1.signal();// 唤醒另一个线程
}
r1.unlock();
}
}
}
synchronized和ReenTrantLock的区别:
synchronized
ReenTrantLock