目的
了解多線程的概念 初步掌握多線程的使用方法
多線程
要了解多線程,首先要了解兩個(gè)概念 進(jìn)程和線程
進(jìn)程:
正在運(yùn)行的一個(gè)程序
系統(tǒng)會(huì)為這個(gè)進(jìn)程分配獨(dú)立的內(nèi)存空間
線程:
具體執(zhí)行任務(wù)的最小單位
一個(gè)進(jìn)程最少擁有一個(gè)線程(主線程 運(yùn)行起來就執(zhí)行的線程)
線程之間是共享內(nèi)存資源的
線程之間可以通信(通常在主線程和子線程之間)
每一個(gè)線程都有自己的運(yùn)行回路(生命周期)
所以說,線程是操作系統(tǒng)運(yùn)行的基本單位,它被封裝在進(jìn)程中,一個(gè)進(jìn)程可以包含多個(gè)線程。
為什么要使用多線程
如果在主線程中存在有比較耗時(shí)的操作,下載視頻上傳文件,這些操作會(huì)阻塞主線程,后面的任務(wù)必須等這些任務(wù)執(zhí)行完畢,之后才能執(zhí)行,用戶體驗(yàn)比較差,為了不阻塞主線程,需要將耗時(shí)的任務(wù)放在子線程中去處理,也就形成了多線程
線程的生命周期
NEW 新建 線程剛剛被創(chuàng)造好
RUNNABLE: 就緒狀態(tài)
BLOCKED 阻塞狀態(tài)
WAITING 等待
TERMINATED 終止
如圖:

創(chuàng)建線程和啟動(dòng)
(1)繼承Thread類創(chuàng)建線程類,步驟如下
1.定義一個(gè)繼承Thread類的子類,并重寫該類的run()方法;
2.創(chuàng)建Thread子類的實(shí)例,即創(chuàng)建了線程對(duì)象;
3.調(diào)用該線程對(duì)象的start()方法啟動(dòng)線程。
public class MyClass {
public static void main(String[] args){
//main方法里面執(zhí)行的代碼 是在主線程里面執(zhí)行的
//創(chuàng)建thread對(duì)象
TestThread tt = new TestThread();
//設(shè)置線程的名稱
tt.setName("子線程1");
//開啟任務(wù)
tt.start();
TestThread tt2 = new TestThread();
//設(shè)置線程的名稱
tt2.setName("子線程2");
//開啟任務(wù)
tt2.start();}
}
class TestThread extends Thread{
//實(shí)現(xiàn)run方法
//方法里面就是具體需要執(zhí)行的代碼
@Override
public void run() {
String name = Thread.currentThread().getName();
for (int i = 0; i < 100; i++) {
System.out.println("name:"+name+" "+(i+1));
}
super.run();
}
}
(2.)實(shí)現(xiàn)Runnable接口創(chuàng)建線程類,步驟如下
1.定義Runnable接口的實(shí)現(xiàn)類,并重寫該接口的run()方法;
2.創(chuàng)建Runnable實(shí)現(xiàn)類的實(shí)例,并以此實(shí)例作為Thread的target對(duì)象,即該Thread對(duì)象才是真正的線程對(duì)象。
public class MyClass {
public static void main(String[] args){
PXDThread pt = new PXDThread();
//使用Thread操作這個(gè)任務(wù)
Thread t = new Thread(pt);
t.setName("子線程1");
t.start();
Thread t2 = new Thread(pt);
t2.setName("子線程2");
t2.start();
}
}
class PXDThread implements Runnable{
public void run() {
for (int i = 0; i < 100; i++) {
System.out.println(Thread.currentThread().getName()+":"+(i+1));
}
}
}
線程安全
線程安全就是多線程訪問時(shí),采用了加鎖機(jī)制,當(dāng)一個(gè)線程訪問該類的某個(gè)數(shù)據(jù)時(shí),進(jìn)行保護(hù),其他線程不能進(jìn)行訪問直到該線程讀取完,其他線程才可使用。不會(huì)出現(xiàn)數(shù)據(jù)不一致或者數(shù)據(jù)污染。比如說在買票系統(tǒng)中,如果沒有采用加鎖機(jī)制,就會(huì)導(dǎo)致,一張票被兩個(gè)人所購(gòu)買的情況。
如何實(shí)現(xiàn)線程安全
實(shí)現(xiàn)線程安全,主要通過,synchronized 或者LOCK 進(jìn)行加鎖解鎖,主要有兩種方法
1.同步代碼塊
2.同步方法
public class MyClass {
public static void main(String[] args){
Ticket ticketCQ = new Ticket("重慶");
Thread t1 = new Thread(ticketCQ);
t1.start();
Ticket ticketSH = new Ticket("上海");
Thread t2 = new Thread(ticketSH);
t2.start();
}
}//用于賣票的任務(wù)
class Ticket implements Runnable{
public static int num = 100;
String name;
public Ticket(String name){
this.name = name;
}
static final Object obj = new Object();
@Override
public void run() {
for (int i = 1; i <= 100; i++) {
//判斷有沒有票
synchronized (obj) {
//需要同步的代碼
if (num > 0) {
System.out.println(name + "出票:" + (101 - num));
num--;
try {
obj.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}finally {
//通知其他線程執(zhí)行
notify();
}
} else {
break;
}
}
}
這里我們用synchronized同步代碼塊的方法 進(jìn)行了加鎖解鎖,但實(shí)際上同步方法,本質(zhì)上也是利用了同步代碼塊,只是同步的監(jiān)聽器是當(dāng)前對(duì)象本身,容易出錯(cuò),所以必須確保多個(gè)對(duì)象調(diào)用的同步方法是操作的同一個(gè)對(duì)象。