一、多線程概念
A:程序:程序指正在運作的程式。确切的來說,當一個程式進入記憶體運作,即變成一個進 程,程序是處于運作過程中的程式,并且具有一定獨立功能。
B:線程:線程是程序中的一個執行單元,負責目前程序中程式的執行,一個程序中至少有 一個線程。一個程序中是可以有多個線程的,這個應用程式也可以稱之為多線程程式。
C:簡而言之:一個程式運作後至少有一個程序,一個程序中可以包含多個線程
D:什麼是多線程呢?即就是一個程式中有多個線程在同時執行
二、多線程解析
多 -->多個
線—> 執行線路
程—>程式
多個執行線路的程式,一個程式開啟就會對應一個程序,一個程序中可以有多個執行線程。
三、多線程并發
并行:
同時執行;
并發:
輪流執行;
四、多線程使用
(1)建立線程第一種的步驟
1.定義一個類繼承Thread
2. 重寫run方法
3.将要執行的代碼放入到run方法中
4.建立子類執行個體
5.調用start();
調用run()和start()方法的差別:
run()方法隻是一個普通的方法
start()虛拟機會幫我們開啟一條執行線路
線程的特點:
從哪裡跌倒就從哪裡爬起來;
(2)建立線程的第二種的步驟
1.定義一個類去實作runnable接口
2.實作run方法
3. 将要執行的寫到run方法中,
4.建立實作類A的執行個體
5.建立Thread的執行個體,将A作為構造參數傳遞過來
6.通過hread的執行個體的執行個體開啟線程;
(3)兩種方式的匿名内部類的方式實作
new Thread(){//定義一個類繼承Thread
public void run(){//重寫Thread的run方法
需要執行的代碼;//将需要執行的代碼放入run方法中
}
}.start();//開啟線程
new Thread(new Runnable(){//定義一個類實作Runable
public void run(){//重寫Thread的run方法
需要執行的代碼;//将需要執行的代碼放入run方法中
}
}).start();//通過Thread開啟線程
五、多線程案例
用多線程模拟買火車票案例:需求(用三個線程模拟三個售票視窗,共同賣100張火車票,每個線程列印出賣第幾張票)以下是詳細源代碼:
package com.qx;
/*
* 1、使用同步代碼塊解決:
*
* 分析問題出現的原因:
* 要有多個線程
* 要有被多個線程所共享的資料
* 多個線程并發的通路共享的資料
*
* synchronized:同步(鎖),可以修飾代碼塊和方法,被修飾的代碼塊和方法一旦被某個線程通路,則直接鎖住,其他的線程将無法通路
*
* 同步代碼塊:
* synchronized(鎖對象){ }
*
* 注意:鎖對象需要被所有的線程所共享
* 同步:安全性高,效率低 非同步:效率高,但是安全性低
*
*
* 2、使用同步方法解決:
*
* 同步方法:使用關鍵字synchronized修飾的方法,一旦被一個線程通路,則整個方法全部鎖住,其他線程則無法通路
*
* 格式:
* A:修飾符 synchronized 傳回值 方法名() { }
*
* B:修飾符 static synchronized 傳回值 方法名() { }
*
* 注意:
* 非靜态同步方法的鎖對象是this
* 靜态的同步方法的鎖對象是目前類的位元組碼對象
*
*/
public class TicketThread implements Runnable {
//定義火車票數量
//int tickets = 100; //方法一
static int tickets = 100; //方法二,需要使用靜态修飾
//建立一個對象
Object obj = new Object();
//方法一
/*@Override
public void run() {
//出售火車票
while(true) {
synchronized (obj) {
//當火車票小于0張,則停止售票
if(tickets > 0) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
//鍊式程式設計
System.out.println(Thread.currentThread().getName() + ":" +tickets--);
}
}
}
}*/
//方法二
@Override
public void run() {
//出售火車票
while (true) {
//method(); //使用synchronized方法
method2(); //使用static synchronized 靜态方法
}
}
private synchronized void method(){
if(tickets >0 ){ 當火車票小于0張,則停止售票
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
//鍊式程式設計
System.out.println(Thread.currentThread().getName() + ":" +tickets--);
}
}
private static synchronized void method2() {
if (tickets > 0) { 當火車票小于0張,則停止售票
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
//鍊式程式設計
System.out.println(Thread.currentThread().getName() + ":" + tickets--);
}
}
}
火車買票案例測試代碼
package com.qx;
public class TicketThreadDemo {
public static void main(String[] args) {
//建立線程對象
TicketThread tkh = new TicketThread();
//建立三個售票視窗以及顯示售票視窗名稱
Thread t1 = new Thread(tkh);
t1.setName("售票視窗1");
Thread t2 = new Thread(tkh);
t2.setName("售票視窗2");
Thread t3 = new Thread(tkh);
t3.setName("售票視窗3");
//啟動線程
t1.start();
t2.start();
t3.start();
}
}
六、線程應用場景
A:多線程的常見應用場景:
1、背景任務,例如:定時向大量(100w以上)的使用者發送郵件;
2、異步處理,例如:發微網誌、記錄日志等;
3、分布式計算
B:多線程最多的場景:web伺服器本身;各種專用伺服器(如遊戲伺服器);
七、線程間的通行
注意重點: 線程間的通信是通過同步鎖來通信,注意需要通信的線程的鎖必須一樣吧
wait();//使目前線程處于等待狀态, 這個隻能被notify()或者notifyAll()喚醒
notify();//喚醒這把鎖上除本線程以外的其他任意一條線程;
final的最後一個作用:延長資料的生命周期,把資料存儲到常量池中,JDK1.8後,自動 加final
内部類通路局部變量的時候,局部變量需要被final修飾,延長變量的生命周期
八、線程生命周期
九、異常注意事項
A:如果父類方法有異常,子類在重寫這個方法的時候,不能比父類的這個方法抛出的異常大
B:如果父類的方法沒有異常,子類在重寫這個方法的時候,就不能有異常;