多線程交替打印1~10的奇偶數(shù)
思路
- 搞兩條線程匕垫,一條線程打印奇數(shù)任務(wù)狰腌,一條線程打印偶數(shù)任務(wù)除破。
- 為了防止線程間的無序爭搶,使用synchronized鎖琼腔。
- 為了保證交互打印瑰枫,使用wait/notify,打印完一條先等待,讓別的線程打印光坝。以此循環(huán)尸诽。
實(shí)現(xiàn)1:synchronized方法對象鎖
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ù)方法里面上來先notify,然后結(jié)束調(diào)用wait盯另?
- 首先調(diào)用notify方法性含,將另一個(gè)線程喚醒。但是另一個(gè)線程根本進(jìn)不來當(dāng)前的方法(因?yàn)橛衧ynchronized鎖)鸳惯。
- 后面調(diào)用wait方法商蕴,將當(dāng)前線程等待,wait方法會(huì)釋放鎖芝发,另一個(gè)線程就可以進(jìn)來了绪商。
實(shí)現(xiàn)2:synchronized(this)對象鎖
這種實(shí)現(xiàn)和實(shí)現(xiàn)1沒什么區(qū)別,實(shí)現(xiàn)1雖然是synchronized修飾方法的方式辅鲸,實(shí)際上還是使用當(dāng)前對象的鎖格郁。
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();
}
}
說明:通過余數(shù)判斷來執(zhí)行迅涮,需要循環(huán)30次,有點(diǎn)不合理徽龟。想了下叮姑,執(zhí)行10次,循環(huán)10次据悔,但是讓線程按照ABC執(zhí)行就可以了传透。根據(jù)這個(gè)想法寫出了改進(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)前對象的。而this在線程的環(huán)境下朱盐,代表的是當(dāng)前線程實(shí)例對象本身耕拷。所以this.getName是當(dāng)前線程實(shí)例對象的線程名稱是什么。
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
說明:
① 首先搞明白該例子中一共有三條線程:
main線程:執(zhí)行main方法的線程骚烧。
Thread-0線程:當(dāng)前CountOperate線程實(shí)例的線程。
Thread-1線程:new Thread類創(chuàng)建的線程闰围。
② CountOperate初始化代碼說明:
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類,所以當(dāng)前線程的名稱是main羡榴,狀態(tài)是true碧查。而當(dāng)前CountOperate線程實(shí)例為Thread-0,沒有啟動(dòng)所以狀態(tài)為false校仑。
③ Thread1線程說明:
main begin t1 isAlive=false
main end t1 isAlive=true
Thread-1線程的線程執(zhí)行器沒有啟動(dòng)忠售,那么狀態(tài)為false。執(zhí)行start方法迄沫,Thread-1線程的線程執(zhí)行器啟動(dòng)稻扬,那么狀態(tài)就為true了。
④ run方法執(zhí)行說明:
run---begin
Thread.currentThread().getName()=Thread-1
Thread.currentThread().isAlive()=true
this.getName()=Thread-0
this.isAlive()=false
run---end
首先執(zhí)行run方法的線程為Thread-1羊瘩,所以當(dāng)前代碼塊中的線程為Thread1(Thread.currentThread().getName()=Thread-1)泰佳,狀態(tài)自然為true。
但是this.getName()是代表當(dāng)前線程Thread-0的實(shí)例尘吗,因?yàn)镃ountOperate線程根本沒有啟動(dòng)逝她,所以狀態(tài)為false。