简介
在实际多线程运用时,我们可能需要多个线程之间同步运行,如线程1线运行完,线程2再运行
线程固定顺序运行
- 两个线程保证:
- 2线程先打印b
- 1线程再打印a
wait/notify实现
设置一个Boolean变量,如果为假,让其中一个线程自旋阻塞
/**
* 两个线程保证:
* 2线程先打印b
* 1线程再打印a
*
* wait/notify实现
*/
public class TwoThread {
static final Object lock = new Object();
static boolean t2Run = false;
public static void main(String[] args) {
Thread t1 = new Thread(() -> {
synchronized (lock){
while(!t2Run){ //此处不可为if(!t2Run),否则会出现假唤醒的情况,即唤醒后会在执行完lock.wait()停止了,不会执行下面的代码
try {
lock.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println(Thread.currentThread().getName()+":a");
}
},"t1");
Thread t2 = new Thread(() -> {
synchronized (lock){
System.out.println(Thread.currentThread().getName()+":b");
t2Run = true;
lock.notify();
}
},"t2");
t1.start();
t2.start();
}
}
park/unpark实现(简单)
- LockSupport就是通过控制变量_counter来对线程阻塞唤醒进行控制的。原理有点类似于信号量机制。
- 当调用park()方法时,会将_counter置为0,同时判断前值,等于1说明前面被unpark过,则直接退出,否则将使该线程阻塞。
- 当调用unpark()方法时,会将_counter置为1,同时判断前值,小于1会进行线程唤醒,否则直接退出。
- 形象的理解,线程阻塞需要消耗凭证(permit),这个凭证最多只有1个。当调用park方法时,如果有凭证,则会直接消耗掉这个凭证然后正常退出;
- 但是如果没有凭证,就必须阻塞等待凭证可用;而unpark则相反,它会增加一个凭证,但凭证最多只能有1个。
- 为什么可以先唤醒线程后阻塞线程?
- 因为unpark获得了一个凭证,之后调用park因为有凭证消费,故不会阻塞。
- 为什么唤醒两次后阻塞两次会阻塞线程。
- 因为凭证的数量最多为1,连续调用两次unpark和调用一次unpark效果一样,只会增加一个凭证;而调用两次park却需要消费两个凭证。
public class TwoThredP2 {
public static void main(String[] args) {
Thread t1 = new Thread(() -> {
LockSupport.park();
System.out.println(Thread.currentThread().getName() + ":a");
}, "t1");
Thread t2 = new Thread(() -> {
System.out.println(Thread.currentThread().getName() + ":b");
LockSupport.unpark(t1);
},"t2");
t1.start();
t2.start();
}
}
ReentrantLock实现
public class TwoThread3 {
static ReentrantLock reentrantLock = new ReentrantLock();
static Condition conditionA = reentrantLock.newCondition();
static boolean t2Run = false;
public static void main(String[] args) {
Thread t1 = new Thread(() -> {
reentrantLock.lock();
try{
while (!t2Run){
try {
conditionA.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println(Thread.currentThread().getName()+":a");
}finally {
reentrantLock.unlock();
}
},"t1");
Thread t2 = new Thread(() -> {
reentrantLock.lock();
try{
System.out.println(Thread.currentThread().getName()+":b");
t2Run = true;
conditionA.signal();
}finally {
reentrantLock.unlock();
}
},"t2");
t1.start();
t2.start();
}
}
线程交替运行
- 实现三个线程按顺序循环打印abc
- 1打印a
- 2打印b
- 3打印c
wait/notify
- 思路:设置一个参数标志flag,为1时1执行并将flag置为2,2时2执行并将flag置为3,为3时3执行并将flag置为1
public class ThreeThread1 {
public static void main(String[] args) {
WaitNotify waitNotify = new WaitNotify(5);
new Thread(()->{
waitNotify.printf(1,2,"a");
},"t1").start();
new Thread(()->{
waitNotify.printf(2,3,"b");
},"t2").start();
new Thread(()->{
waitNotify.printf(3,1,"c");
},"t3").start();
}
}
class WaitNotify{
private int loopNum ;
private int flag = 1;
public WaitNotify(int loopNum) {
this.loopNum = loopNum;
}
public int getLoopNum() {
return loopNum;
}
public void setLoopNum(int loopNum) {
this.loopNum = loopNum;
}
public int getFlag() {
return flag;
}
public void setFlag(int flag) {
this.flag = flag;
}
public void printf(int currentFlag,int nextFlag,String str){
for(int i=0;i<loopNum;i++){
synchronized (this){
while (currentFlag!=flag){
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.printf(str);
flag = nextFlag;
this.notifyAll();//此处不可使用notify,因为notify是随机唤醒
}
}
}
}
park/unpark
public class ThreeThread2 {
static Thread t1 ;
static Thread t2 ;
static Thread t3 ;
public static void main(String[] args) {
ParkUnpark parkUnpark = new ParkUnpark(5);
t1 = new Thread(()->{
parkUnpark.printf("a",t2);
},"t1");
t2 = new Thread(()->{
parkUnpark.printf("b",t3);
},"t2");
t3 = new Thread(()->{
parkUnpark.printf("c",t1);
},"t3");
t1.start();
t2.start();
t3.start();
LockSupport.unpark(t1);
}
}
class ParkUnpark{
private int loopNum;
public ParkUnpark(int loopNum){
this.loopNum = loopNum;
}
public void printf(String str,Thread next){
for (int i = 0;i<loopNum;i++){
// System.out.printf(Thread.currentThread().getName());
LockSupport.park();
System.out.printf(str);
LockSupport.unpark(next);
}
}
}
ReentrantLock
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;
public class ThreeThread3 {
public static void main(String[] args) {
Rlock rlock = new Rlock(5);
Condition a = rlock.newCondition();
Condition b = rlock.newCondition();
Condition c = rlock.newCondition();
Thread t1 = new Thread(()->{
rlock.printf("a",a,b);
});
Thread t2 = new Thread(()->{
rlock.printf("b",b,c);
});
Thread t3 = new Thread(()->{
rlock.printf("c",c,a);
});
t1.start();
t2.start();
t3.start();
try {
Thread.sleep(1000);//此处必须先让主线程沉睡一会,避免主线程先进入唤醒,t1再沉睡,导致死锁
} catch (InterruptedException e) {
e.printStackTrace();
}
rlock.lock();
try{
a.signal();
}finally {
rlock.unlock();
}
}
}
class Rlock extends ReentrantLock{
private int loopNum;
public Rlock(int loopNum){
this.loopNum = loopNum;
}
public void printf(String str, Condition current,Condition next){
for(int i=0;i<loopNum;i++){
this.lock();
try {
try {
current.await();
System.out.printf(str);
next.signal();
} catch (InterruptedException e) {
e.printStackTrace();
}
}finally {
this.unlock();
}
}
}
}