再學—Java基礎:多執行緒(1)
程序與執行緒:
- 什麼是程序:就是應用程式在執行時期,所佔用的記憶體空間區域。
一個程式也可以說是一個程序,開闢的記憶體可能不是連續的0-88,99-120的記憶體塊,這樣的話可能就造成了其他程式和當前的程式的資料錯亂,還有一些什神奇的錯誤,這時候偉大的工程師,採用了虛擬記憶體,就是虛擬出來連續的記憶體,指向非連續的記憶體這地址)
- 什麼是執行緒:是程序中的單個順序控制流,是一條執行路徑。
單執行緒:一個程序只有一個可執行的路徑
多執行緒:一個程序有多個可執行的路徑
執行緒的實現方式
執行緒是依託於程序而存在的,而程序是系統創建出去的,java並不能作業系統,但是可以調C/C++,實現建立程序,從而可以實現執行緒。(執行緒程序都是由作業系統,依靠JVM找作業系統,才能實現執行緒的功能)
- Thread類:繼承
public class MyThread extends Thread { @Override public void run() { super.run(); //執行緒一般執行的是耗時任務尤其是在android 端 for (int i = 0; i < 100; i++) { System.out.println(i); } } } //開啟執行緒 public class ThreadDemo { public static void main(String[] args) { MyThread myThread = new MyThread(); myThread.start(); } }
- Runnable :實現
public class RunnableDemo implements Runnable { @Override public void run() { //執行緒一般執行的是耗時任務尤其是在android 端 for (int i = 0; i < 100; i++) { System.out.println(i); } } } //開啟執行緒 public class ThreadDemo { public static void main(String[] args) { RunnableDemorunnableDemo= new RunnableDemo (); //Theand的構造方法 可以出入一個runnable物件 new Thread(runnableDemo ).start(); } }
兩種方式對比:實現介面,避免單繼承侷限性
這樣我們就開啟了兩個執行緒,
那麼,我們在繼承Thread類或實現Runnable類之後,為什麼要重寫run()方法呢?
因為不是類中的所有程式碼都需要被執行緒執行的(個人理解繼承Thread或實現Runnable的子類,不一定全是要 執行在run()方法裡,可能存在其他的自定義方法,和成員變數)。而這個時候,為了區分哪些程式碼能夠被執行緒執行,java提供了Thread類中的run()用來包含那些被執行緒執行的程式碼。
-
設定和獲取執行緒的名字
Thread類的基本獲取和設定方法
public final String getName():獲取執行緒的名稱。
public final void setName(String name):設定執行緒的名稱
public class ThreadDemo { public static void main(String[] args) { RunnableDemo runnableDemo = new RunnableDemo(); Thread thread = new Thread(runnableDemo); thread.setName("001"); System.out.println(thread.getName()); thread.start(); MyThread myThread = new MyThread(); myThread.setName("002"); System.out.println(myThread.getName()); myThread.start(); //獲取當前正在執行的執行緒名字 System.out.println(Thread.currentThread().getName()); } }
-
執行緒的操作
public static void sleep(long millis):執行緒休眠
public class MyThread extends Thread { @Override public void run() { super.run(); //執行緒一般執行的是耗時任務尤其是在android 端 try { //當前執行緒被執行的時候,會每次睡眠一秒 Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } for (int i = 0; i < 100; i++) { System.out.println(i); } } }
public final void wait() :執行緒等待
public class MyThread extends Thread { @Override public void run() { super.run(); //執行緒一般執行的是耗時任務尤其是在android 端 synchronized (this) { try { //次執行緒就會被掛起,放棄CPU的執行權和釋放鎖,等待notify() wait(); //wait(1000); //wait(2000, 1000); } catch (InterruptedException e) { e.printStackTrace(); } } for (int i = 0; i < 100; i++) { System.out.println(i); } } }
sleep()和wait()的區別:
使用上
從使用角度看,sleep是Thread執行緒類的靜態方法,而wait是Object頂級類的費靜態方法()。
sleep可以在任何地方使用(指的是當前執行的執行緒休眠),而wait只能在同步方法或者同步塊中使用
CPU及資源鎖釋放
sleep,wait呼叫後都會暫停當前執行緒並讓出cpu的執行時間,但不同的是sleep不會釋放當前持有的物件的鎖資源,到時間後會繼續執行,而wait會放棄所有鎖並需要notify/notifyAll後重新獲取到物件鎖資源後才能繼續執行。
異常捕獲
sleep需要捕獲或者丟擲異常,而wait/notify/notifyAll不需要。
為什麼執行緒方法等待,喚醒寫了Object類?
notify 等待,喚醒本鎖上的執行緒,必須有鎖的支援,鎖是任意物件,將wait,notify方法寫了Object類,保證任意物件鎖都可以呼叫執行緒,等待喚醒方法
public final void stop():中斷執行緒
public class StopThreadDemo { public static void main(String[] args) { Thread t = new Thread(new StopThread()); t.start(); t.stop(); } } classStopThread implements Runnable { @Override public void run() { while (true) { System.out.println("run........."); } } }
執行緒無任何輸出,直接停止了,不再繼續執行了
public void interrupt():中斷執行緒
public class InterruptThreadDemo { public static void main(String[] args) { InterruptThread t = new InterruptThread(); t.start(); try { //如果5秒 不醒來 我就弄死你 Thread.sleep(5000); t.interrupt(); } catch (InterruptedException e) { e.printStackTrace(); } } } class InterruptThread extends Thread { @Override public void run() { try { Thread.sleep(10000); } catch (InterruptedException e) { System.out.println("執行緒被終止了"); } System.out.println("run........."); } }
但是interrupt()方法執行後,它會終止執行緒的狀態,還會繼續執行run方法裡面的程式碼。
執行緒的終止 1 stop 過時 2,interrupt 3,標記執行迴圈,run方法結束
public final void join():執行緒加入
public class JoinThread { public static void main(String[] String) { JoinRunnable joinRunnable1 = new JoinRunnable(); JoinRunnable joinRunnable2 = new JoinRunnable(); JoinRunnable joinRunnable3 = new JoinRunnable(); Thread t1=new Thread(joinRunnable1); t1.setName("我是 1"); Thread t2=new Thread(joinRunnable2); t2.setName("我是 2"); Thread t3=new Thread(joinRunnable3); t3.setName("我是 3"); t1.start(); try { t1.join(); } catch (InterruptedException e) { e.printStackTrace(); } t2.start(); t3.start(); } } class JoinRunnable implements Runnable { @Override public void run() { for (int i = 0; i < 10; i++) { System.out.println(Thread.currentThread().getName().toString()); } } }
等待該執行緒終止,使用了join方法的執行緒,會一直執行結束,其他執行緒搶CPU的資源
public static void yield():執行緒禮讓
/** * 執行緒的讓步 yield */ public class YieldThreadDemo { public static void main(String[] args) { YieldRunnable yieldRunnable = new YieldRunnable(); Thread t0 = new Thread(yieldRunnable); Thread t1 = new Thread(yieldRunnable); t0.start(); t1.start(); } } class YieldRunnable implements Runnable { @Override public void run() { for (int x = 0; x < 100; x++) { Thread.yield(); System.out.println(Thread.currentThread().getName() + "....run"); } } }
這個方法暫停當前正在執行的執行緒物件,並執行其他執行緒,出現互相謙讓的狀態。
讓多個執行緒的執行更和諧,但是不能靠它保證一人一次。
public final void setDaemon(boolean on):守護執行緒
/** * setDaemon(true)守護執行緒 */ public class DaemomThreadDemo { public static void main(String[] args) { Thread t0 = new Thread(new DaemomRunnable()); t0.setDaemon(true);//執行緒t0 是 main執行緒的守護執行緒,如果main執行完或者main執行緒終止,main的守護執行緒都死亡 t0.start(); } } class DaemomRunnable implements Runnable { @Override public void run() { for (int i = 30; i > 0; i++) { System.out.println("run........"); } } }
當執行緒設為A執行緒的守護執行緒,A死亡後,所有的守護執行緒都死亡
執行緒的執行狀態圖

Thread.png
第一次畫圖,見笑了,:smile::smile::smile:
- 模擬火車站賣票,引出多執行緒的資料安全問題
public class ThreadDemo { public static void main(String[] args) { RunnableDemo runnableDemo1 = new RunnableDemo(); RunnableDemo runnableDemo2 = new RunnableDemo(); RunnableDemo runnableDemo3 = new RunnableDemo(); new Thread(runnableDemo1).start(); new Thread(runnableDemo2).start(); new Thread(runnableDemo3).start(); } } public class RunnableDemo1 implements Runnable { //定義一百張車票 private int mTicket = 100; @Override public void run() { while (mTicket > 0) { //每出售一張 --操作 System.out.println(Thread.currentThread().getName() + " 出售車票= " + mTicket-- + " 張"); } } }

data.jpg
多執行緒狀態下,資料出現了問題,怎麼解決多執行緒下的資料安全問題,請看下節