最近在博客園中逛,看到多線程想學習一下,雖然作者本人感覺挺詳細的,但對於新手沒學過多線程理解還是有難度的,最多也就是明白大概意思,自己寫還是一片空白,我看過多個博客以后覺得還需要打代碼才能徹底理解多線程,以下以一個經典多線程題目為例,一個菜鳥解題的具體步驟:
題目:建立三個線程,A線程打印10次A,B線程打印10次B,C線程打印10次C,要求線程同時運行,交替打印10次ABC。
1.看到三個線程,同時運行,交替打印,和大神解答寫的各種流程、sleep、synchronized、wait、notify一陣頭痛。還是自己慢慢來,打印10個A先。。
package main; /** * * 多線程題目解答 * @author 光 * @version 2015年11月20日 上午10:46:53 * */ public class ManyThreadPrint implements Runnable { public String name; public ManyThreadPrint(String name){ this.name=name; } @Override public void run() { int count = 10; while(count > 0){ System.out.print(name); count--; } } public static void main(String[] args) { ManyThreadPrint mtpa = new ManyThreadPrint("A"); new Thread(mtpa).start(); } }
解析:使用實現Runnable接口的方式使用線程,main方法中添加實例使用構造器傳參A,重寫run()方法中循環打印10次
運行結果如下:
AAAAAAAAAA
2.十次A打印完成,哈哈,把B和C打印以下試試
package main; /** * * 多線程題目解答 * @author 光 * @version 2015年11月20日 上午10:46:53 * */ public class ManyThreadPrint implements Runnable { public String name; public ManyThreadPrint(String name){ this.name=name; } @Override public void run() { int count = 10; while(count > 0){ System.out.print(name); count--; } } public static void main(String[] args) { ManyThreadPrint mtpa = new ManyThreadPrint("A"); ManyThreadPrint mtpb = new ManyThreadPrint("B"); ManyThreadPrint mtpc = new ManyThreadPrint("C"); new Thread(mtpa).start(); new Thread(mtpb).start(); new Thread(mtpc).start(); } }
解析:只是在main方法中添加兩個實例把B和C穿進去,添加兩個線程執行
運行結果如下:
AAAAAAAAAABBBBBBBBBBCCCCCCCCCC
手賤又運行一下:
AAAAAAAAAACCCCCCCCCCBBBBBBBBBB
這怎么回事。。在試一下:
ABBBBBBBBBBAAAAAAAAACCCCCCCCCC
好吧,結果10個A,10個B,10個C全部打印出來了,就是結果不固定。
3.查了一下資料,原來是3個線程同時執行,順序不固定。只好讓后面的線程先等一下再執行:
package main; /** * * 多線程題目解答 * @author 光 * @version 2015年11月20日 上午10:46:53 * */ public class ManyThreadPrint implements Runnable { public String name; public ManyThreadPrint(String name){ this.name=name; } @Override public void run() { int count = 10; while(count > 0){ System.out.print(name); count--; } } public static void main(String[] args) throws InterruptedException { ManyThreadPrint mtpa = new ManyThreadPrint("A"); ManyThreadPrint mtpb = new ManyThreadPrint("B"); ManyThreadPrint mtpc = new ManyThreadPrint("C"); new Thread(mtpa).start(); Thread.sleep(10); new Thread(mtpb).start(); Thread.sleep(10); new Thread(mtpc).start(); Thread.sleep(10); } }
解析:有一點需要注意,Thread.sleep(10);是下一個線程啟動需要等待的時間,由於最后結果需要交替打印,所以第三個線程執行后也有一個等待,相當於下輪的第一個線程等待。
結果如下(執行幾次都一樣):
AAAAAAAAAABBBBBBBBBBCCCCCCCCCC
3.接下來就是交替ABC按照大神的方法,建立3個對象,雖然是三個普通的Object,卻在run()中通過同步、等待、釋放鎖來實現線程的執行順序,先看一下代碼:
package main; /** * * 多線程題目解答 * @author 光 * @version 2015年11月20日 上午10:46:53 * */ public class ManyThreadPrint implements Runnable { public String name; public Object prev; public Object self; public ManyThreadPrint(String name, Object prev, Object self) { this.name = name; this.prev = prev; this.self = self; } @Override public void run() { int count = 10; while(count > 0){ synchronized (prev) { synchronized (self) { System.out.print(name); count--; self.notify(); } try { prev.wait(); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } } public static void main(String[] args) throws InterruptedException { Object a = new Object(); Object b = new Object(); Object c = new Object(); ManyThreadPrint mtpa = new ManyThreadPrint("A",c,a); ManyThreadPrint mtpb = new ManyThreadPrint("B",a,b); ManyThreadPrint mtpc = new ManyThreadPrint("C",b,c); new Thread(mtpa).start(); Thread.sleep(10); new Thread(mtpb).start(); Thread.sleep(10); new Thread(mtpc).start(); Thread.sleep(10); } }
解析:
改變構造器,每次創建實例是除了打印的字符串,還有兩個對象,這一點要注意,這兩個對象只是普通的Object,跟線程沒一毛線關系,只是通過線程對對象鎖的操作來控制流程。關鍵代碼在run()中的循環里面,一點一點來。
這里synchronized (self)、synchronized (prev),self當前對象鎖,prev前一個(前綴)對象鎖,當線程獲取兩個對象鎖時執行打印;需要注意。將synchronized (self)放入synchronized (prev)中表示打印A線程執行后,進入等待前置C對象的線程池里面,這樣在打印C時喚醒一下C對象,那么打印C完成后就會立刻打印A,不過這樣執行需要保證3個線程的啟動順序,即main方法中的Thread.sleep(10)。啟動打印A線程時,打印A,喚醒自己,進入等待C的線程池,釋放AC。當3個線程按照啟動順序完成時,即打印C線程完成后,會立刻打印A,打印A線程完成后,立刻打印B,依照順序循環。
也就是說,在3個線程啟動完成后,已經將打印A的線程進入等待C對象的線程池里;將打印B的線程進入等待A對象的線程池里;將打印C的線程進入等待B對象的線程池里。這時按照打印A線程、打印B線程、打印C線程的順序執行,就保證下一輪以打印A線程開始,從而保證以后打印順序。
在自己打代碼的過程中發現了很多感覺看懂的時候沒發現的東西,所以,學東西,最好自己要手打一遍。