多線程(一)
這邊來談談java中,我對對多線程的理解
在了解多線程前,先說說進程。
進程就是正在運行的應用程序。 當你打開任務管理器的時候,你就會發現很多的進程。
而我們要說的線程,就是依賴於進程而存在的,一個進程可以開啟多個線程。
Thread類
說到線程,就必須來說說Thread類。
Thread類是說有線程的父類。具體請參見api
線程的創建以及執行(圖解如下)
繼承Thread類,或者實現rennable接口。 當繼承了父類後,需要重寫父類的run方法,這個run方法裏面就寫你要執行的代碼,當這個線程啟動的時候,就會執行你重寫的run方法裏面的內容。上邊的圖run裏面寫的是一個for循環,輸出的是一到一百。 線程啟動:start方法即可實現。 代碼實現:開啟線程,並啟動,輸出結果 public class MyThread extends Thread{ //1.繼承Thread類 //2.重寫run方法,重寫run方法中的代碼之後,當我們啟動了這個線程之後,我們的這個線程就會執行run方法中的代碼 public class MyThread extends Thread{ //1.繼承Thread類 //2.重寫run方法,重寫run方法中的代碼之後,當我們啟動了這個線程之後,我們的這個線程就會執行run方法中的代碼 @Override public void run() { //需求:開啟該線程之後,執行一個for循環 for (int i = 0; i < 10; i++) { System.out.println(i); } } //在測試類中啟動,來看輸出。 //代碼實現 public class Test { public static void main(String[] args) { //只要我們創建了一個線程對象,並且啟動該線程的實例,我們就相當於開啟了一///個線程 MyThread mt = new MyThread(); mt.start();//1.開啟了一個線程 2.讓開啟的這個線程執行他對應的類中的run方//法 //在次創建一個子線程,並開啟這個子線程執行他的run方法 MyThread mt2 = new MyThread(); mt2.start(); } 最終輸出的是我們在run方法裏寫的for循環。 接下來我們用第二種方法,實現一個runnable接,並重寫run方法 public class MyThread implements Runnable{ @Override public void run() { //啟動該線程對象之後,需要執行的代碼 for (int i = 0; i < 10; i++) { System.out.println(i); } } } public class MyThread implements Runnable{ @Override public void run() { //啟動該線程對象之後,需要執行的代碼 for (int i = 0; i < 10; i++) { System.out.println(i); } } } public class Test { public static void main(String[] args) { //創建Mythread對象 MyThread mt = new MyThread(); Thread t1 = new Thread(mt); t1.start(); } } 這就是線程的創建和啟動 線程的調度和控制 線程休眠(Thread.sleep(毫秒值)) 線程名稱(setName(),getName();) 線程的調度及優先級setPriority(10)(註意默認值是5,區間在1-10之間) 什麽叫線程優先級:說白了就是設置你搶占cpu執行權搶占到的概率 MyThread t1 = new MyThread(); MyThread t2 = new MyThread(); MyThread t3 = new MyThread(); //給三個線程設置姓名 t1.setName("劉備"); t2.setName("張飛"); t3.setName("關羽"); //設置線程的優先級 //線程的調度及優先級setPriority(10)(註意默認值是5,區間在1-10之間) //t1.setPriority(100);//設置的區間必須在1-10之間 t1.setPriority(10); //開啟線程 t1.start(); t2.start(); t3.start(); //共有100張票,將ticket改為靜態之後,被類的所有對象所共享 static int ticket = 100; @Override public void run() { //用一個while true循環模擬三個窗口一直處於打開的狀態 while (true) { //只有當ticket>0的時候,才可以出售票 if (ticket>0) { System.out.println(getName()+"正在出售第:"+ticket--+"張票"); } } } public static void main(String[] args) { //創建三個線程模擬三個售票窗口 MyThread mt1 = new MyThread(); MyThread mt2 = new MyThread(); MyThread mt3 = new MyThread(); //給線程設置名稱 mt1.setName("窗口一"); mt2.setName("窗口二"); mt3.setName("窗口三"); //啟動線程,開啟售票 mt1.start(); mt2.start(); mt3.start(); } 以上代碼是一個售票案例,是一個多線程的案例。 這裏說到cpu的執行權。下圖簡單說說,執行權和執行。
這是那個案例的輸出結果,在之後我們會談到線程不安全性問題。
這裏說幾點:執行權的搶占是隨機的,誰搶到就執行誰,可以通過sleep來驗證(詳見java相關\第十五天代碼+資料)
在使用runnable實現售票案例的時候,在線程睡一會之後,會出現線程安全性問題。
如何解決多線程安全問題
*線程安全執行效率就低
A:同步代碼塊(測試不是同一個鎖的情況,測試是同一個鎖的情況)
synchronized(對象) {
需要被同步的代碼。
}
需求:1.測試不是同一把鎖的時候線程安全嗎? 2.如果是同一把鎖線程安全嗎?
兩個問題:1.對象是什麽 ?
答:任意對象 ,相當於是一把鎖,只要線程進去就把鎖鎖上
2.需要同步的代碼?
答:被線程執行的代碼
鎖對象問題
a:同步代碼
塊(定義一個抽象類,裏面專門定義一個鎖)
任意對象
b:同步方法(僅適用於實現runable接口)
public synchronized void sellTicket(){同步代碼}
this
靜態同步方法
類的字節碼對象
public static synchronized void sellTicket() {
需要同步的代碼
}
public class MyThread implements Runnable{ //定義100張票 int ticket = 100; Object obj = new Object(); @Override public void run() { while (true) { //同步代碼塊 //synchronized (new Object()) {//t1,t2,t3三個線程不共享同一把鎖每個線程都有自己的議案鎖 synchronized (obj) {//這樣3個線程才可以共享同一把鎖 if (ticket>0) { //考慮到實際的生活中,我們需要給每一個線程加入一定的延遲,模擬一下這種效果 try { Thread.sleep(100); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } System.out.println(Thread.currentThread().getName()+"正在出售第:"+ticket--+"張票"); }
//當被同步的代碼執行完畢之後,t1手裏拿著的obj這個鎖才會被釋放,
//t1,t2,t3重新搶占cpu的執行權,誰搶到了繼續拿著obj這個鎖,執行同步代碼塊中的內容
}
}
總之,要解決線程安全性問題,就是上邊的方法,關於線程,有好多知識點需要鞏固,當然有些東西會在下一次的文章中寫(多線程二)。
多線程(一)