java基礎知識回顧之java Thread類--java執行緒實現常見的兩種方式(一)
java基礎知識回顧之java Thread類--java執行緒實現常見的兩種方式(一)
建立執行緒的第一種方式:
/ * 步驟:
* 1.繼承Thread類
* 2. 重寫Thread的run方法
* 目的:將自定義的程式碼儲存在run方法中,執行自定義執行緒
* start 1.啟動執行緒,2呼叫run方法
* 為什麼要覆蓋run方法?
* Thread類用於描述執行緒,該類定義了一個功能,用於儲存執行緒要執行的程式碼,該儲存功能就是run方法
* 也就是說Thread中的run方法,用於儲存執行緒要執行的程式碼。
* 主執行緒要執行的方法存放在main裡面,這是虛擬機器定義的
* 虛擬機器開啟主執行緒,也就是執行main方法裡面的程式碼:
*
*
*/
程式碼演示:
*發現執行結果每一次都不同,因為多個執行緒都在獲取CPU的使用權,cpu執行到誰,誰就執行,明確
* 一點,cpu在某一個時刻,只能執行一個程式(多核除外),CPU在做快速的切換,達到看上去是同時執行的
* 多執行緒的執行行為在互相的搶奪CPU的執行權。
* 這是多執行緒的一個特性:隨機性
public class HelloThread extends Thread { private String name; public HelloThread(String name){ this.name=name; } public void run(){ for(int i=0;i<=60;i++){ System.out.println(name+"執行"+i); } } public static void main(String[]args){ HelloThread h1 = new HelloThread("A副執行緒");//建立一個執行緒 //HelloThread h2 = new HelloThread("B"); //兩個執行緒併發的執行 h1.start();//開啟執行緒,並執行該執行緒的run方法 //h1.run();//僅僅是物件呼叫方法,是主執行緒的順序執行 for(int i=0;i<60;i++){ System.out.println("主執行緒執行中。。。。。。。。。。"+i); } //h2.start(); } }
總結:通過程式碼的測試:說明開啟執行緒是使用start方法,虛擬機器會自動呼叫run方法,等到搶到CUP使用權,執行緒從就緒狀態轉為執行狀態,而不能直接呼叫run()方法,因為在main方法裡面如果呼叫run方法,相當於jvm主執行緒執行了一個普通的方法。所以是不能實現多執行緒,只是主執行緒在順序的往下執行。
java基礎知識回顧之java Thread類--java執行緒實現常見的兩種方式實現Runnable介面(二)
建立執行緒的第二中方式:
/**
*
步驟:
1定義類實現Runnable介面
2.實現Runnable介面中的run方法。
3.通過Thread類建立執行緒物件,並將Runnable 介面的子類物件作為實際引數傳給Thread類的構造方法、
4.呼叫Thread類的start方法開啟執行緒,並呼叫Runnable介面子類的run方法
為什麼要將Runnable介面子類的物件傳遞給Thread類的構造方法?
因為執行緒的任務(要執行的程式碼)都封裝在Runnable介面的子類物件的run方法中,所以線上程物件建立的時候就必須明確要執行的任務。
*
*/
package com.lp.ecjtu.Thread; public class HelloRunnableThread implements Runnable{ private String name; public String getName() { return name; } public void setName(String name) { this.name = name; } public HelloRunnableThread(String name){ this.name = name; } @Override public void run() { for(int i=0;i<=100;i++){ System.out.println(name+"執行"+i); } } public static void main(String[] args){ HelloRunnableThread h1 = new HelloRunnableThread("A"); HelloRunnableThread h2 = new HelloRunnableThread("B"); Thread t1 = new Thread(h1);//h1為傳入執行緒構造方法的介面 Thread t2 = new Thread(h2); System.out.println("執行緒啟動之前---》"+t1.isAlive());//測試執行緒是否處於活動狀態 t1.start();//開啟執行緒,進入可執行狀態 System.out.println("執行緒啟動之後---》"+t1.isAlive()); t2.start(); } }
輸出:
執行緒啟動之前---》false
執行緒啟動之後---》true
A執行0
B執行0
B執行1
B執行2
B執行3
B執行4
B執行5
B執行6
B執行7
B執行8
B執行9
B執行10
B執行11
B執行12
B執行13
A執行1
B執行14
A執行2
B執行15
java基礎知識回顧之java Thread類學習(三)--java執行緒實現常見的兩種方式實現好處:
總結:實現Runnable介面比繼承Thread類更有優勢:
1.因為java只能單繼承,實現Runnable介面可以避免單繼承的侷限性
2.繼承Thread類,多個執行緒不能處理或者共享同一個資源,但是實現Runnable介面可以處理同一個資源。
下面我們做個測試:驗證下。車站的售票系統售票的例子,車站的各個售票口相當於各個執行緒,我們先使用第一種方法幾繼承Thread類的方式實現:
程式碼如下:
package com.lp.ecjtu.Thread; /** * * @author Administrator * 車站的售票系統售票的例子,車站的各個售票口相當於各個執行緒。 * */ public class TicketsThread extends Thread{ //用靜態變數存放這100張票,這樣就不會賣重複 private int tickets = 100; public void run(){ while(true){ if(tickets > 0){ System.out.println(Thread.currentThread().getName()+"***sale***"+(--tickets)); } } } /** * @param args */ public static void main(String[] args) { // 不合理的買票程式,因為,不同執行緒都可以賣同一張票, //現實生活中不是這樣的,視窗1買完第99張票,視窗2不可以賣了。 TicketsThread t1 = new TicketsThread(); TicketsThread t2 = new TicketsThread(); TicketsThread t3 = new TicketsThread(); t1.start(); t2.start(); t3.start(); } }
程式碼的原理圖如下:
總結:不合理的賣票方法,不同執行緒都可以賣同一張票,現實生活中不是這樣的,視窗1賣完99號票,視窗2就不可以再賣這張票了。
怎麼解決呢?使用實現Runnable介面的方法就可以解決,程式碼如下:
public class TicketsRunnable implements Runnable { private int ticket=100; //Object obj = new Object(); public TicketsRunnable(){ System.out.println("*****************************"); } @Override public void run() { // TODO Auto-generated method stub while(true){ //synchronized(obj){ //同步程式碼塊 if(ticket > 0){//當執行緒0被調起的時候,當執行到這條判斷語句的時候,執行緒1被調起搶了CPU資源,執行緒0進入凍結狀態。 try { Thread.sleep(100);//中斷當前活躍的執行緒,或者執行的執行緒 } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName()+"正在賣票"+ticket--); //System.out.println(Thread.currentThread().getId()); //System.out.println(Thread.currentThread().getName()); } //} } } /** * @param args */ public static void main(String[] args) { // TODO Auto-generated method stub TicketsRunnable runna = new TicketsRunnable(); Thread t1 = new Thread(runna); Thread t2 = new Thread(runna); Thread t3 = new Thread(runna); t1.start(); t2.start(); t3.start(); } }
上面程式碼簡單記憶體圖理解如下:
總結:三個視窗再賣100張票,直到賣完,3個執行緒才停止。賣的票的號碼沒有重複,真正實現了現實生活中的賣票的效果。但是存在一個執行緒安全問題,通過測試執行發現,打印出了0,-1,-2等錯票,多執行緒執行出現了安全問題,這就需要引入執行緒鎖機制,下一篇部落格詳細的講解怎樣避免這一問題。