最近在讀java多線程編程核心技術,記錄學習心得java
實現多線程有兩種:1.繼承Thread類 2.實現Runnable接口編程
1.1繼承Thread類安全
class Thread implements Runnable (Thread 繼承了Runnable接口)
使用多線程技術時,代碼運行結果與代碼執行順序或調用順序是無關的.多線程
1.2實現Runnable接口(若是已經有父類了,不能在繼承Thread類,則實現Runnable接口)dom
把Thread類對象傳入構造函數,能夠實現將一個Thread對象的run方法交給其餘的線程使用ide
public class CountOperate extends Thread{ public CountOperate(){ System.out.println("CountOperate---begin"); System.out.println("Thread.currentThread().getName="+Thread.currentThread().getName()); System.out.println("this.getName="+this.getName()); 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("this.getName="+this.getName()); System.out.println("run----end"); } }
第一種:函數
public static void main(String[] args) { CountOperate c=new CountOperate(); Thread t1=new Thread("A"); t1.start(); /*output: CountOperate---begin Thread.currentThread().getName=main this.getName=Thread-0 CountOperate----end */ }
是main線程執行構造函數(ps:這個main和main方法不要緊,只不過名字相同)學習
第二種:測試
public static void main(String[] args) { CountOperate c=new CountOperate(); Thread t1=new Thread(c,"A"); t1.start(); /* CountOperate---begin Thread.currentThread().getName=main this.getName=Thread-0 CountOperate----end run---begin Thread.currentThread().getName=A this.getName=Thread-0 run----end */ }
main線程執行構造方法,start()會自動調用run方法this
2 實例變量與線程安全
這裏就涉及到一個線程安全的問題了.
public class MyThread extends Thread{ private int count=5; @Override public void run() { super.run(); count--; System.out.println("由"+Thread.currentThread().getName()+"計算 count="+count); } }
public static void main(String[] args) { MyThread myThread=new MyThread(); Thread a=new Thread(myThread,"a"); Thread b=new Thread(myThread,"b"); Thread c=new Thread(myThread,"c"); Thread d=new Thread(myThread,"d"); Thread e=new Thread(myThread,"e"); a.start(); b.start(); c.start(); d.start(); e.start(); /*output: 由a計算 count=2 由c計算 count=2 由d計算 count=1 由b計算 count=2 由e計算 count=0 */ }
緣由:i--,分爲3步4, 1)取得原有的i值 2)計算i-1 3)對i進行賦值 同時訪問i值,就會產生非線程安全問題
解決方法:在run方法synchronized 關鍵字
3.Thread currentThread():獲取當前線程對象
boolean siAlive():判斷當前的進程是否處於活動狀態
String getId() 獲取線程的惟一標識
4.線程中斷
interrupt():中止線程
interrupted():判斷當前線程是否已經中斷
isInterrupted():測試線程是否已經中斷
ps:比較interrupted()方法和isInterrupted()方法?
從源碼看:
public static boolean interrupted() { return currentThread().isInterrupted(true); }
舉個例子咱們能更好理解:
//線程類 public class MyThread extends Thread{ @Override public void run() { super.run(); for (int i = 0; i <50000; i++) { System.out.println("i="+(i+1)); } } }
//測試類 public class Run2 { public static void main(String[] args) { Thread.currentThread().interrupt(); System.out.println("是否中止1?="+Thread.interrupted()); System.out.println("是否中止2?="+Thread.interrupted()); } /*output: 是否中止1?=true 是否中止2?=false */ }
緣由:interrupted()方法具備清除狀態的功能
ps:native修飾方法
public void interrupt() { if (this != Thread.currentThread()) checkAccess(); synchronized (blockerLock) { Interruptible b = blocker; if (b != null) { interrupt0(); // Just to set the interrupt flag b.interrupt(this); return; } } interrupt0(); }
private native void interrupt0();
native修飾方法,表示調用的是原生態方法,方法對應的實現不是在當前文件,而是在用其餘語言(如C和C++)實現的文件中.
public boolean isInterrupted() { return isInterrupted(false); }
例子:
public static void main(String[] args) { try { MyThread myThread=new MyThread(); myThread.start(); myThread.sleep(1000); myThread.interrupt(); System.out.println("是否中止1?="+myThread.isInterrupted()); System.out.println("是否中止2?="+myThread.isInterrupted()); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("end!"); } /*取打印結果: 是否中止1?=false 是否中止2?=false end! */
isInterrupted()測試線程對象是否已是中斷狀態,但不清除狀態標誌.
5.在沉睡中中止:
public class MyThread extends Thread{ @Override public void run() { super.run(); try { System.out.println("run begin"); System.out.println("線程1="+Thread.currentThread().getName()); Thread.sleep(200000); System.out.println("run end"); } catch (InterruptedException e) { System.out.println("在沉睡中中止!進入catch!"); e.printStackTrace(); } } }
public static void main(String[] args) { MyThread thread =new MyThread(); try { thread.start(); System.out.println("線程2="+Thread.currentThread().getName()); Thread.sleep(2000); thread.interrupt(); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("end!"); } /* output: 線程2=main run begin 線程1=Thread-0 end! 在沉睡中中止!進入catch! java.lang.InterruptedException: sleep interrupted at java.lang.Thread.sleep(Native Method) at us.codecraft.tinyioc.MyThread.run(MyThread.java:59) */ }
stop() 包裏中止線程,可是可能會形成數據不一致的結果,不建議使用
使用return 中止線程
while (true) { if (Thread.interrupted()) { System.out.println("中止了"); return; } System.out.println(System.currentTimeMillis()); }
6.暫停線程
suspend()(暫停線程)和resume()(重啓線程)方法
缺點:
(1).獨佔,若是使用不當會,會形成公共的同步對象的獨佔,使其餘線程沒法訪問公共同步對象
(2).不一樣步
7.yield():放棄當前的cpu資源,將他讓給其餘的任務去佔用cpu執行時間,但放棄的時間不肯定,有可能剛剛放棄,立刻又獲取cpu時間片
@Override public void run() { long beginTime=System.currentTimeMillis(); int count =0; for (int i = 0; i <5000000 ; i++) { //Thread.yield(); count=count+(i+1); } long endTime=System.currentTimeMillis(); System.out.println("用時:"+(endTime-beginTime)+"毫秒"); }
public static void main(String[] args) { MyThread thread=new MyThread(); thread.start(); /*output:用時:11毫秒*/ //去掉Thread.yield() /*用時:453毫秒*/ }
8.線程的優先級 setPriority()
public final void setPriority(int newPriority) { ThreadGroup g; checkAccess(); //線程優先級分爲1-10,若是小於1大於10 則拋出異常 if (newPriority > MAX_PRIORITY || newPriority < MIN_PRIORITY) { throw new IllegalArgumentException(); } if((g = getThreadGroup()) != null) { //若是設置權限登記大於線程組的最大等級,就將設置權限設爲線程租最大等級 if (newPriority > g.getMaxPriority()) { newPriority = g.getMaxPriority(); } setPriority0(priority = newPriority); } }
優先級能夠被繼承(此繼承非彼繼承),好比由A線程啓動B線程,B線程的優先級與A是同樣的
8 優先級具備規則性
public class MyThread1 extends Thread{ @Override public void run() { long beginTime=System.currentTimeMillis(); long addResult=0; for(int j = 0; j <10; j++){ //業務邏輯 for (int i = 0; i <50000; i++) { Random random=new Random(); random.nextInt(); addResult=addResult+i; } } long endTime=System.currentTimeMillis(); System.out.println("***** thread 1 use time ="+(endTime-beginTime)); } }
public class MyThread2 extends Thread{ @Override public void run() { long beginTime=System.currentTimeMillis(); long addResult=0; for (int i = 0; i <10; i++) { //業務邏輯 for (int j = 0; j <50000; j++) { Random random=new Random(); random.nextInt(); addResult=addResult+i; } } long endTime=System.currentTimeMillis(); System.out.println("***** thread 2 use time ="+(endTime-beginTime)); } }
public class Run { public static void main(String[] args) { for (int i = 0; i <5 ; i++) { MyThread1 thread1=new MyThread1(); thread1.setPriority(1); thread1.start(); MyThread2 thread2=new MyThread2(); thread2.setPriority(10); thread2.start(); } } }
結果:
//***** thread 2 use time =130 ***** thread 2 use time =263 ***** thread 2 use time =279 //***** thread 2 use time =156 ***** thread 2 use time =284 ***** thread 2 use time =328 //***** thread 1 use time =248 ***** thread 1 use time =292 ***** thread 2 use time =364 //***** thread 2 use time =355 ***** thread 1 use time =315 ***** thread 1 use time =393 //***** thread 1 use time =370 ***** thread 1 use time =334 ***** thread 1 use time =351 //***** thread 2 use time =387 ***** thread 1 use time =370 ***** thread 2 use time =433 //***** thread 2 use time =396 ***** thread 2 use time =428 ***** thread 1 use time =437 //***** thread 1 use time =397 ***** thread 2 use time =433 ***** thread 2 use time =440 //***** thread 1 use time =431 ***** thread 1 use time =402 ***** thread 1 use time =440 //***** thread 1 use time =455 ***** thread 2 use time =447 ***** thread 1 use time =443
總結:這是3次執行結果,咱們發現,優先級較高則優先執行完run()方法中的任務,但這個結果不能說的太確定,由於優先級還具備隨機性(第二列),也就是優先級高的線程不必定每一次都先執行完.
也就是說優先級高的獲取cpu資源的機率大,可是有時候機率低的也有可能先執行完.
9守護線程
線程有兩種,第一種是用戶線程,第二種是守護線程(例如垃圾回收器)
若是沒有非守護線程,則守護線程自動銷燬
例子:
public class MyThread extends Thread{ private int i=0; @Override public void run() { try { while (true) { i++; System.out.println("i=" + i); Thread.sleep(1000); } } catch (InterruptedException e) { e.printStackTrace(); } } }
public class Run { public static void main(String[] args) { try { MyThread thread=new MyThread(); //設置爲守護線程 thread.setDaemon(true); thread.start(); Thread.sleep(5000); //主線程沉睡,守護線程離開thread對象再也不打印 System.out.println("我離開thread對象就再也不打印了"); } catch (InterruptedException e) { e.printStackTrace(); } } /*output: i=1 i=2 i=3 i=4 i=5 我離開thread對象就再也不打印了 i=6 */ }