多線程交替打印1~10的奇偶數(shù)
思路
- 搞兩條線程,一條線程打印奇數(shù)任務(wù),一條線程打印偶數(shù)任務(wù)。
- 為了防止線程間的無(wú)序爭(zhēng)搶?zhuān)褂胹ynchronized鎖。
- 為了保證交互打印,使用wait/notify,打印完一條先等待,讓別的線程打印。以此循環(huán)。
實(shí)現(xiàn)1:synchronized方法對(duì)象鎖
public class MyTask {
public synchronized void printNumber(int i) {
try {
this.notify();
System.out.println(Thread.currentThread().getName() + " " + i);
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public class SwapPrint {
public static void main(String[] args) {
final MyTask myTask = new MyTask();
Thread t1 = new Thread(new Runnable() {
@Override
public void run() {
for (int i = 1 ; i <= 10 ; i+=2) {
myTask.printNumber(i);
}
}
});
t1.setName("Thread t1");
t1.start();
Thread t2 = new Thread(new Runnable() {
@Override
public void run() {
for (int i = 2 ; i <= 10 ; i+=2) {
myTask.printNumber(i);
}
}
});
t2.setName("Thread t2");
t2.start();
}
}

思考:
有的人會(huì)搞不懂業(yè)務(wù)方法里面上來(lái)先notify,然后結(jié)束調(diào)用wait?
- 首先調(diào)用notify方法,將另一個(gè)線程喚醒。但是另一個(gè)線程根本進(jìn)不來(lái)當(dāng)前的方法(因?yàn)橛衧ynchronized鎖)。
- 后面調(diào)用wait方法,將當(dāng)前線程等待,wait方法會(huì)釋放鎖,另一個(gè)線程就可以進(jìn)來(lái)了。
實(shí)現(xiàn)2:synchronized(this)對(duì)象鎖
這種實(shí)現(xiàn)和實(shí)現(xiàn)1沒(méi)什么區(qū)別,實(shí)現(xiàn)1雖然是synchronized修飾方法的方式,實(shí)際上還是使用當(dāng)前對(duì)象的鎖。
public class MyTask {
public void printNumber(int i) {
try {
synchronized (this) {
this.notify();
System.out.println(Thread.currentThread().getName() + " " + i);
this.wait();
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
思考:
synchronized關(guān)鍵字只是控制當(dāng)前代碼區(qū)域,只能有一個(gè)線程進(jìn)入。
wait/notify方法可以讓當(dāng)前線程等待,也可以喚醒其他線程。
實(shí)現(xiàn)3:等待隊(duì)列Condition實(shí)現(xiàn)
public class MyTask {
private ReentrantLock rl = new ReentrantLock();
private Condition condition = rl.newCondition();
public void printNumber(int i) {
try {
rl.lock();
condition.signal();
System.out.println(Thread.currentThread().getName() + " " + i);
condition.await();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
rl.unlock();
}
}
}
思考:
- 調(diào)用lock方法,當(dāng)前線程設(shè)置同步狀態(tài),加入到aqs同步隊(duì)列中,獲取重入鎖。
- 調(diào)用signal方法,喚醒等待隊(duì)列中的線程,然后加入到aqs同步隊(duì)列。
- 調(diào)用await方法,將當(dāng)前線程加入到aqs等待隊(duì)列。
ABC三個(gè)線程如何保證順序執(zhí)行
題目
ABC三個(gè)線程如何保證順序執(zhí)行。三個(gè)線程同時(shí)啟動(dòng),然后按照順序執(zhí)行,每個(gè)線程執(zhí)行10次。
思路
首先想到了等待隊(duì)列Condition喚醒部分線程,使用ReentrantLock進(jìn)行加鎖。
初始版實(shí)現(xiàn)
/**
* @description A\B\C三個(gè)線程順序執(zhí)行10次
*
* @author sunpy
* @date 2018年11月28日 下午2:23:45
*/
public class MyTest {
static class MyTask {
private static ReentrantLock rl = new ReentrantLock();
private static Condition conditionA = rl.newCondition();
private static Condition conditionB = rl.newCondition();
private static Condition conditionC = rl.newCondition();
private static int number = 0;
public void execute() {
rl.lock();
try {
while (number < 30) {
if (number % 3 == 0) {
System.out.println(Thread.currentThread().getName() + " - " + number);
number++;
conditionB.signal();
conditionA.await();
}
if (number % 3 == 1) {
System.out.println(Thread.currentThread().getName() + " - " + number);
number++;
conditionC.signal();
conditionB.await();
}
if (number % 3 == 2) {
System.out.println(Thread.currentThread().getName() + " - " + number);
number++;
conditionA.signal();
conditionC.await();
}
}
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
rl.unlock();
}
}
}
public static void main(String[] args) {
final MyTask myTask = new MyTask();
new Thread(new Runnable() {
@Override
public void run() {
myTask.execute();
}
}, "A").start();
Thread.sleep(1000);
new Thread(new Runnable() {
@Override
public void run() {
myTask.execute();
}
}, "B").start();
Thread.sleep(1000);
new Thread(new Runnable() {
@Override
public void run() {
myTask.execute();
}
}, "C").start();
}
}
說(shuō)明:通過(guò)余數(shù)判斷來(lái)執(zhí)行,需要循環(huán)30次,有點(diǎn)不合理。想了下,執(zhí)行10次,循環(huán)10次,但是讓線程按照ABC執(zhí)行就可以了。根據(jù)這個(gè)想法寫(xiě)出了改進(jìn)版。
改進(jìn)版實(shí)現(xiàn)
/**
* @description A\B\C三個(gè)線程順序執(zhí)行10次
*
* @author sunpy
* @date 2018年11月28日 下午2:23:45
*/
public class MyTest {
static class MyTask {
private static ReentrantLock rl = new ReentrantLock();
private static Condition conditionA = rl.newCondition();
private static Condition conditionB = rl.newCondition();
private static Condition conditionC = rl.newCondition();
public void execute(String flag) {
rl.lock();
try {
for (int i = 1 ; i <= 10 ; i++) {
if ("A".equals(flag)) {
System.out.println(Thread.currentThread().getName() + " - " + i);
conditionB.signal();
conditionA.await();
}
if ("B".equals(flag)) {
System.out.println(Thread.currentThread().getName() + " - " + i);
conditionC.signal();
conditionB.await();
}
if ("C".equals(flag)) {
System.out.println(Thread.currentThread().getName() + " - " + i);
conditionA.signal();
conditionC.await();
}
}
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
rl.unlock();
}
}
}
}
Thread.currentThread.getName和this.getName的區(qū)別
this.getName
this的意思是代表當(dāng)前對(duì)象的。而this在線程的環(huán)境下,代表的是當(dāng)前線程實(shí)例對(duì)象本身。所以this.getName是當(dāng)前線程實(shí)例對(duì)象的線程名稱是什么。
Thread.currentThread.getName
Thread.currentThread.getName意思是在當(dāng)前代碼塊中執(zhí)行的線程名稱是什么。
例子
public class CountOperate extends Thread {
public CountOperate() {
System.out.println("CountOperate---begin");
System.out.println("Thread.currentThread().getName()=" + Thread.currentThread().getName());
System.out.println("Thread.currentThread().isAlive()=" + Thread.currentThread().isAlive());
System.out.println("this.getName()=" + this.getName());
System.out.println("this.isAlive()=" + this.isAlive());
System.out.println("CountOperate---end");
}
@Override
public void run() {
System.out.println("run---begin");
System.out.println("Thread.currentThread().getName()=" + Thread.currentThread().getName());
System.out.println("Thread.currentThread().isAlive()=" + Thread.currentThread().isAlive());
System.out.println("this.getName()=" + this.getName());
System.out.println("this.isAlive()=" + this.isAlive());
System.out.println("run---end");
}
}
public class Test {
public static void main(String[] args) {
CountOperate c = new CountOperate();
Thread t1 = new Thread(c);
System.out.println("main begin t1 isAlive=" + t1.isAlive());
t1.start();
System.out.println("main end t1 isAlive=" + t1.isAlive());
}
}
結(jié)果:
CountOperate---begin
Thread.currentThread().getName()=main
Thread.currentThread().isAlive()=true
this.getName()=Thread-0
this.isAlive()=false
CountOperate---end
main begin t1 isAlive=false
main end t1 isAlive=true
run---begin
Thread.currentThread().getName()=Thread-1
Thread.currentThread().isAlive()=true
this.getName()=Thread-0
this.isAlive()=false
run---end
說(shuō)明:
① 首先搞明白該例子中一共有三條線程:
main線程:執(zhí)行main方法的線程。
Thread-0線程:當(dāng)前CountOperate線程實(shí)例的線程。
Thread-1線程:new Thread類(lèi)創(chuàng)建的線程。
② CountOperate初始化代碼說(shuō)明:
CountOperate---begin
Thread.currentThread().getName()=main
Thread.currentThread().isAlive()=true
this.getName()=Thread-0
this.isAlive()=false
CountOperate---end
執(zhí)行構(gòu)造器,然后當(dāng)前main方法中執(zhí)行初始化CountOperate類(lèi),所以當(dāng)前線程的名稱是main,狀態(tài)是true。而當(dāng)前CountOperate線程實(shí)例為T(mén)hread-0,沒(méi)有啟動(dòng)所以狀態(tài)為false。
③ Thread1線程說(shuō)明:
main begin t1 isAlive=false
main end t1 isAlive=true
Thread-1線程的線程執(zhí)行器沒(méi)有啟動(dòng),那么狀態(tài)為false。執(zhí)行start方法,Thread-1線程的線程執(zhí)行器啟動(dòng),那么狀態(tài)就為true了。
④ run方法執(zhí)行說(shuō)明:
run---begin
Thread.currentThread().getName()=Thread-1
Thread.currentThread().isAlive()=true
this.getName()=Thread-0
this.isAlive()=false
run---end
首先執(zhí)行run方法的線程為T(mén)hread-1,所以當(dāng)前代碼塊中的線程為T(mén)hread1(Thread.currentThread().getName()=Thread-1),狀態(tài)自然為true。
但是this.getName()是代表當(dāng)前線程Thread-0的實(shí)例,因?yàn)镃ountOperate線程根本沒(méi)有啟動(dòng),所以狀態(tài)為false。