记录了如何控制多个线程的执行顺序,以练习题的形式记录
一、两个线程顺序执行
题目描述:
线程t1会打印A,线程t2会打印B,实现先打印B再打印A
题目分析:
实现的关键是线程1执行的时候要确定线程2已经执行过了,如果线程2还没执行就让线程1继续等待
wait notify 解法
public class Test7 {
private static final Logger LOGGER = LoggerFactory.getLogger(Test7.class);
private static boolean t2IsRun;
private static Object lock = new Object();
public static void main(String[] args) {
Thread t1 = new Thread(new Runnable() {
@Override
public void run() {
synchronized (lock){
//t2还没执行前让t1 先wait
while (!t2IsRun){
try {
lock.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//t2执行完了
LOGGER.info("A");
}
}
},"t1");
Thread t2 = new Thread(new Runnable() {
@Override
public void run() {
synchronized (lock) {
LOGGER.info("B");
//通知t1可以执行了
t2IsRun=true;
lock.notifyAll();
}
}
},"t2");
t1.start();
t2.start();
}
}
当然这里的lock对象可以换成ReentrantLock
,这样就不用使用synchronized
,可以在ReentrantLock
的条件变量
Condition
上进行等待,使用await和signal方法控制,道理是一样的。
Thread t1 = new Thread(new Runnable() {
@Override
public void run() {
// t2还没执行前让t1 先wait
lock.lock();
try {
while (!t2IsRun) {
try {
condition.await();
} catch (InterruptedException e) {
e.printStackTrace();
return;
}
}
} finally {
lock.unlock();
}
//t2执行完了
LOGGER.info("A");
}
}, "t1");
Thread t2 = new Thread(new Runnable() {
@Override
public void run() {
lock.lock();
try {
LOGGER.info("B");
//通知t1可以执行了
t2IsRun = true;
condition.signal();
} finally {
lock.unlock();
}
}
}, "t2");
t1.start();
t2.start();
park unpark解法
private static final Logger LOGGER = LoggerFactory.getLogger(Test7.class);
public static void main(String[] args) {
Thread t1 = new Thread(new Runnable() {
@Override
public void run() {
// t2还没执行前让t1先暂停
LockSupport.park();
//t2执行完了
LOGGER.info("A");
}
}, "t1");
Thread t2 = new Thread(new Runnable() {
@Override
public void run() {
LOGGER.info("B");
//通知t1可以执行了
LockSupport.unpark(t1);
}
}, "t2");
t1.start();
t2.start();
}
这种解法里利用了park unpark方法的特性,
如果t1先执行park方法,线程t1会暂停执行,进入Waiting状态,当t2执行了unpark方法后t1恢复执行进入Runnable状态。
如果t2先执行unpark方法t1后边再执行park方法,则t1执行park方法时不会进行等待,会继续执行。
这个原理可以用一个比喻来理解,线程里有一个存干粮的地方,而且只能存一份,默认是空的,没有干粮,
park方法就是用来检查当前有没有干粮,如果没有干粮,线程就会开始等待,啥时候干粮来了就把干粮吃掉然后继续向下执行,注意这时候干粮槽又空了。如果当前有干粮,线程就会把干粮吃掉然后继续执行也就是不会等待。
unpark方法就是补充干粮,所以就会产生一种效果: 先执行的unpark可以让后执行的park方法不暂停。
二、三个线程交替执行
题目描述:
线程t1打印A,线程t2打印B,线程t3打印C,实现A,B,C这样的打印,然后循环10次
题目分析:
控制两个线程的先后可以使用布尔变量,控制三个线程的先后顺序可以使用int a变量来存储3中状态
a=1 t1运行,a=2 t2运行, a=3 t3运行,如果不满足条件就等待,t1运行完后让a=2,t2运行完让a=3,t3运行完让
a=1,这样就实现了交替
wait notify解法
public class Test7 {
private static final Logger LOGGER = LoggerFactory.getLogger(Test7.class);
private static final Object lock = new Object();
private static int a=1;
public static void main(String[] args) {
Thread t1 = new Thread(new Runnable() {
@Override
public void run() {
for (int i = 0; i < 10; i++) {
//锁放在里边是在每一次循环的时候加锁,循环体执行完就释放锁,后边它如果抢到锁了就
//会开始下一轮循环,但是因为a的状态不对又会wait然后释放锁
//注意锁加在for循环外边也是可以的,因为t1执行完会改变标记a,a的值改变后下次循环
//就会wait然后释放锁,这样别的线程就能抢到锁了
synchronized (lock){
while (a!=1) {
try {
lock.wait();
} catch (InterruptedException e) {
e.printStackTrace();
return;
}
}
LOGGER.info("A");
//改变标记 唤醒让下一个线程开始执行
a=2;
lock.notifyAll();
}
}
}
},"t1");
Thread t2 = new Thread(new Runnable() {
@Override
public void run() {
for (int i = 0; i < 10; i++) {
synchronized (lock){
while (a!=2){
try {
lock.wait();
} catch (InterruptedException e) {
e.printStackTrace();
return;
}
}
LOGGER.info("B");
//叫醒下一个线程
a=3;
lock.notifyAll();
}
}
}
},"t2");
Thread t3 = new Thread(new Runnable() {
@Override
public void run() {
for (int i = 0; i < 10; i++) {
synchronized (lock){
while (a!=3){
try {
lock.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
LOGGER.info("C");
a=1;
lock.notifyAll();
}
}
}
},"t3");
t1.start();
t2.start();
t3.start();
}
}
await和signal 解法
public class Test7 {
private static final Logger LOGGER = LoggerFactory.getLogger(Test7.class);
private static final ReentrantLock lock = new ReentrantLock();
private static Condition condition1 =lock.newCondition();
private static Condition condition2 =lock.newCondition();
private static Condition condition3 =lock.newCondition();
public static void main(String[] args) throws InterruptedException {
Thread t1 = new Thread(new Runnable() {
@Override
public void run() {
for (int i = 0; i < 10; i++) {
lock.lock();
try {
//先wait
condition1.await();
LOGGER.info("A");
//叫醒下一个condition的线程
condition2.signal();
} catch (InterruptedException e) {
e.printStackTrace();
return;
} finally {
lock.unlock();
}
}
}
},"t1");
Thread t2 = new Thread(new Runnable() {
@Override
public void run() {
for (int i = 0; i < 10; i++) {
lock.lock();
try {
condition2.await();
LOGGER.info("B");
//叫醒下一个condition的线程
condition3.signal();
} catch (InterruptedException e) {
e.printStackTrace();
return;
} finally {
lock.unlock();
}
}
}
},"t1");
Thread t3 = new Thread(new Runnable() {
@Override
public void run() {
for (int i = 0; i < 10; i++) {
lock.lock();
try {
condition3.await();
LOGGER.info("C");
//叫醒下一个condition的线程
condition1.signal();
} catch (InterruptedException e) {
e.printStackTrace();
return;
} finally {
lock.unlock();
}
}
}
},"t3");
t1.start();
t2.start();
t3.start();
//等待,确保三个线程都进入了等待
Thread.sleep(1000);
//主线程获取锁叫醒t1
lock.lock();
try {
condition1.signal();
} finally {
lock.unlock();
}
}
}
准备三个Condition,一开始让三个线程都去condition等待,每个线程打印完后去唤醒下一个应该执行的线程,
等所有线程都运行后主线程中叫醒t1
park unpark解法
public class Test7 {
private static final Logger LOGGER = LoggerFactory.getLogger(Test7.class);
static Thread t1;
static Thread t2;
static Thread t3;
public static void main(String[] args) throws InterruptedException {
t1 = new Thread(new Runnable() {
@Override
public void run() {
for (int i = 0; i < 10; i++) {
LockSupport.park();
LOGGER.info("A");
LockSupport.unpark(t2);
}
}
},"t1");
t2 = new Thread(new Runnable() {
@Override
public void run() {
for (int i = 0; i < 10; i++) {
LockSupport.park();
LOGGER.info("B");
LockSupport.unpark(t3);
}
}
},"t2");
t3 = new Thread(new Runnable() {
@Override
public void run() {
for (int i = 0; i < 10; i++) {
LockSupport.park();
LOGGER.info("C");
LockSupport.unpark(t1);
}
}
},"t3");
t1.start();
t2.start();
t3.start();
//利用unpark方法的特性,不用关是park先执行还是unpark先执行,叫醒t1
LockSupport.unpark(t1);
}
}
这种解法还是一开始让所有线程都暂停住,然后利用unpark方法的特性在主线程里让t1恢复执行
标签:顺序,Thread,多个,lock,t2,t1,线程,new From: https://www.cnblogs.com/chengxuxiaoyuan/p/16947339.html