一.使用多線程的兩種方法java
來看一下thread類的源代碼:安全
class Thread implements Runnable {
首先能夠看出thread類也是實現Runable接口的run方法以下:多線程
public void run() { if (target != null) { target.run(); } }
下面就是一個建立繼承Thread的類的列子:ide
public class ExThreadText extends Thread { @Override public void run(){ for(int i=0;i<20;i++){ System.out.println("我本身建立的線程"); } } public static void main(String[] args) { new ExThreadText().start(); System.out.println("程序結束!"); } }
結果以下測試
首先咱們須要明白在這個程序裏面有多少個線程?應該是兩個線程一個是main方法的線程一個是我run方法裏面的一個線程this
從結果能夠看出這兩個線程的調用和建立的順序是無關的,spa
在這個代碼實例裏面咱們重寫了run方法,並使用 start 方法來調用,那爲何不用run方法來調用呢?咱們來看看run方法調用會有什麼結果:線程
能夠看出如今的兩個"線程"已是按照順序執行的了,其實如今並非多線程,就是一個單線程按照流程來執行。scala
總結:1.多線程的調用是無序的3d
2.多線程須要使用start方法來調用而不是run方法,一樣start方法來調用線程也是無序的
因爲Java不提供多繼承,因此當咱們繼承了Thread類的時候咱們就不能繼承其它的父類了,爲了解決這一個問題,我建議實現runable接口。
首先繼承runable接口必須重寫他的run方法,代碼以下:
public class ImRunableText implements Runnable { @Override public void run(){ //重寫run方法 } }
那怎麼使用這個runable接口的子類呢?
咱們來看看Thread類的構造方法:
能夠看出Thread類的構造器能夠接受實現Runable接口的子類對象(不要忘了thread類也是實現runable接口的),同時他還重載了一個構造器能夠爲線程命名。
那麼咱們就可使用這個runable的實現子類了,代碼以下:
public class ImRunableText implements Runnable { @Override public void run(){ //重寫run方法 for(int i=0;i<10;i++){ System.out.println("run方法"); } } public static void main(String[] args) { Thread thread =new Thread(new ImRunableText(),"線程"); thread.start(); System.out.println("結束了"); } }
結果以下:
4.1數據不共享
數據不共享那就是一個線程一個數據,單獨執行互不影響。代碼以下:
這是一個線程類,他有本身的字段num爲10。
public class DataNShare extends Thread{ private int num =10; private String name; public DataNShare(String name){ this.name=name; } @Override public void run(){ for(;num>0;){ System.out.println("當前線程爲:"+name); System.out.println("num值爲"+num); num--; } } }
在設置一個測試類,建立三個對象,各自進行測試代碼以下:
public class Text { public static void main(String[] args) { DataNShare d1=new DataNShare("線程1"); DataNShare d2=new DataNShare("線程2"); DataNShare d3=new DataNShare("線程3"); d1.start(); d2.start(); d3.start(); } }
測試結果:能夠看出一開始是沒有問題的,可是在後面出現了亂序的狀況。
那麼出現了亂序的狀況是否是就必定證實了程序出錯了呢?
咱們來改進一下這個DataNShare類中的Run方法
public void run(){ for(int i =1;num>=0;i++){ System.out.println("當前線程爲:"+name); System.out.println("num值爲"+num); num--; if(num==0){ System.out.println("*************i的值爲"+i+"*************"); } } }
那麼也就是說當個人 i 值輸出每一次輸出10那麼就是表明每一條線程都是執行互不影響的。
*3,雖然仍是存在亂序的狀況,可是至少保證咱們的每一條線程執行都是10次沒有問題的。那麼出現亂序的狀況就是輸出語句的問題。
4.2數據共享
數據共享就是多個線程能夠訪問一個數據,代碼以下:
放有共享數據的線程類:
public class DataShare extends Thread { private int num=3;//共享數據 @Override public void run(){ num--;//共享數據減一 System.out.println("當前線程爲:"+this.currentThread().getName()+"num值爲:"+num); } }
處理類:
public class Text { public static void main(String[] args) { //將共享數據放入3個線程裏進行處理 DataShare d=new DataShare(); Thread t1=new Thread(d,"t1"); Thread t2=new Thread(d,"t2"); Thread t3=new Thread(d,"t3"); t1.start(); t2.start(); t3.start(); } }
結果以下:
在這裏出現的這種狀況就是線程不安全狀態。那麼怎麼解決這個問題呢?
使用關鍵字synchronized(同步化的)來解決。
當一個方法使用該關鍵字那麼在多個線程執行這個方法時,每個線程得到執行該方法的執行權就會把這個方法上鎖結束後開鎖,只有等到這個方法沒有被上鎖時才能夠被其餘線程運行。
看看改進後的代碼:
public class DataShare extends Thread { private int num=3;//共享數據 @Override synchronized public void run(){ num--;//共享數據減一 System.out.println("當前線程爲:"+this.currentThread().getName()+"num值爲:"+num); } }
結果:
總結:當多個線程在共享一個數據時,可能會形成線程異常,應該使用關鍵字synchronized來實現同步化,在後面還會深刻了解同步化。