知識(shí)點(diǎn)
- 進(jìn)程:正在運(yùn)行的某個(gè)程序 如:QQ 瀏覽器
系統(tǒng)會(huì)為這個(gè)進(jìn)程分配獨(dú)立的內(nèi)存資源 - 線程:具體執(zhí)行任務(wù)的最小單位
一個(gè)進(jìn)程最少擁有一個(gè)線程(主線程 運(yùn)行起來就執(zhí)行的線程)
線程之間是共享一片內(nèi)存空間(進(jìn)程申請(qǐng))
線程之間可以通信 (數(shù)據(jù)傳遞:多數(shù)為主線程和子線程)
每一個(gè)線程都有自己的運(yùn)行回路(生命周期) - 線程的生命周期 狀態(tài)
New:新建 線程剛被創(chuàng)建
RUNNABLE:就緒狀態(tài)(準(zhǔn)備好了章姓,隨時(shí)可以運(yùn)行)只要搶到時(shí)間片就可以運(yùn)行
BLOCKED:阻塞狀態(tài) (時(shí)間片結(jié)束 sleep wait)
WAITING:等待 wait
TIMED_WAITING
TERMINATED - 為什么需要?jiǎng)?chuàng)建一個(gè)子線程
如果在主線程中存在比較耗時(shí)的操作 如:下載視頻凫乖,上傳文件虫埂,數(shù)據(jù)處理
這些操作會(huì)阻塞主線程春寿,后面的任務(wù)必須等這些任務(wù)執(zhí)行完畢捏卓,之后才能執(zhí)行堕虹,用戶體驗(yàn)比較差
為了不阻塞主線程猪落,需要將耗時(shí)的任務(wù)放在子線程中去處理 -
線程阻塞的原理
內(nèi)容梗概
-1. 如何創(chuàng)建一個(gè)(子)線程
-2.線程的同步
-3.主線程和子線程之間使用數(shù)據(jù)回調(diào)
-4.兩個(gè)線程交替執(zhí)行
-5.線程間的通信
主要內(nèi)容
-1.如何創(chuàng)建一個(gè)(子)線程
- 寫一個(gè)類繼承于Thread,實(shí)現(xiàn)run方法
public class MyClass{
static TestThread1 tt2;
public static void main(String[] args){
// 創(chuàng)建Thread的對(duì)象
TestThread1 tt=new TestThread1();
// 設(shè)置線程的名稱
tt.setName("子線程1");
// 開啟任務(wù)
tt.start();
}
}
class TestThread1 extends Thread{
//實(shí)現(xiàn)run方法
@Override
public void run() {
//run方法里就是具體需要執(zhí)行的代碼
String name=Thread.currentThread().getName();
for (int i = 1; i <= 100; i++) {
System.out.println(name+":"+i);
}
super.run();
}
}
注:不能同時(shí)開啟同一個(gè)線程,會(huì)出錯(cuò)
TestThread1 tt=new TestThread1();
tt.setName("子線程1");
tt.start();
TestThread1 tt2;
tt2=new TestThread1();
tt2.setName("子線程2");
tt2.start();
- 實(shí)現(xiàn)Runnable接口,實(shí)現(xiàn)run方法
(1)創(chuàng)建任務(wù):創(chuàng)建類實(shí)現(xiàn)Runnable接口
(2)使用Thread,為這個(gè)任務(wù)分配線程
(3)開啟任務(wù),start()
有四種使用方法蹬刷,先創(chuàng)建一個(gè)類
class ZRThread implements Runnable{
@Override
public void run() {
for (int i = 1; i< 100; i++) {
System.out.println(Thread.currentThread().getName()+": "+i);
}
}
}
第一種方法:
//步驟1:創(chuàng)建一個(gè)任務(wù):創(chuàng)建一個(gè)類實(shí)現(xiàn)Runnable接口
ZRThread zt=new ZRThread();
//步驟2:使用Thread操作這個(gè)任務(wù)
Thread t=new Thread(zt);
t.setName("子線程1");
t.start();
Thread t2=new Thread(zt);
t2.setName("子線程2");
t2.start();
第二種方法:
//這個(gè)任務(wù)只需使用一次
Thread t=new Thread(new Runnable() {
@Override
public void run() {
for (int i = 1; i< 100; i++) {
System.out.println(Thread.currentThread().getName()+": "+i);
}
}
});
t.setName("子線程1");
t.start();
第三種方法:
//創(chuàng)建線程的同時(shí) 直接開啟線程
//不需要操作線程對(duì)象本身
new Thread(new Runnable() {
@Override
public void run() {
for (int i = 1; i< 100; i++) {
System.out.println(Thread.currentThread().getName()+": "+i);
}
}
}).start();
第四種方法:
//使用Lambda表達(dá)式瓢捉,但閱讀性太差
new Thread(()->{
for (int i = 1; i< 100; i++) {
System.out.println(Thread.currentThread().getName()+": "+i);
}
}).start();
-2.線程的同步
- 線程安全
多個(gè)線程操作同一個(gè)資源,線程無法確定自己什么時(shí)候被阻塞办成,容易導(dǎo)致數(shù)據(jù)錯(cuò)誤
- 同步代碼塊
synchronized (監(jiān)聽器/對(duì)象/鎖){
需要同步的代碼
}
public class MyClass {
public static void main(String[] args){
Ticket ticketCQ=new Ticket("重慶");
Thread t1=new Thread(ticketCQ);
t1.start();
}
}
class Ticket implements Runnable{
//定義所有車票的數(shù)量
public static int num=100;
String name;
public Ticket(String name){
this.name=name;
}
// static實(shí)現(xiàn)同步(代碼塊)
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 + "出票: " + num);
num--;
} else {
break;
}
}
}
}
}
- 同步方法 同步監(jiān)聽器是同一個(gè)對(duì)象
必須確保對(duì)象調(diào)用的同步操作時(shí)操作的統(tǒng)一對(duì)象
public synchronized void test() {
}
本質(zhì)都是同步代碼塊
等價(jià)于
synchronized (this){
}
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() {
test();
}
public synchronized void test() {
for (int i = 1; i <= 100; i++) {
//需要同步的代碼
//判斷有沒有票
if (num > 0) {
System.out.println(name + "出票: " + num);
num--;
} else {
break;
}
}
}
}
-3.主線程和子線程之間使用數(shù)據(jù)回調(diào)
public class MyClass {
public static void main(String[] args){
Person zs=new Person();
zs.needHouse();
}
}
class ZRThread implements Runnable{
@Override
public void run() {
for (int i = 1; i< 100; i++) {
System.out.println(Thread.currentThread().getName()+": "+i);
}
}
}
分別定義兩個(gè)類泡态,在主函數(shù)中實(shí)現(xiàn)回調(diào)
public class Person implements Agent.AgentInterface {
public void needHouse(){
Agent xw=new Agent();
xw.target=this;
xw.start();
}
@Override
public void callBack(String desc) {
System.out.println("我是小王,接收到你的數(shù)據(jù)了:"+desc);
}
}
public class Agent extends Thread{
AgentInterface target;
@Override
public void run() {
System.out.println(Thread.currentThread().getName());
System.out.println("開始找房子");
System.out.println("—————————");
System.out.println("房子找到了诈火,即將返回?cái)?shù)據(jù)");
target.callBack("房子在西南大學(xué)");
super.run();
}
public interface AgentInterface{
void callBack(String desc);
}
}
-4.兩個(gè)線程交替執(zhí)行
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();
}
}
class Ticket implements Runnable{
public static int num=100;
String name;
public Ticket(String name){
this.name=name;
}
static final Object obj=new Object();
static ReentrantLock lock=new ReentrantLock();
@Override
public void run() {
for (int i = 1; i <= 100; i++) {
synchronized (obj) {
//需要同步的代碼
//判斷有沒有票
if (num > 0) {
System.out.println(name + "出票: " + num);
num--;
try {
//通知其他線程執(zhí)行
obj.notify();
//當(dāng)前線程等待
obj.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
} else {
break;
}
}
}
}
}
-5.線程間的通信
-使用ReentrantLock同步
class Ticket implements Runnable{
//定義所有車票的數(shù)量
public static int num=100;
String name;
public Ticket(String name){
this.name=name;
}
// static實(shí)現(xiàn)同步(代碼塊)
//創(chuàng)建一個(gè)可重入的鎖
static ReentrantLock lock=new ReentrantLock();
@Override
public void run() {
for (int i = 1; i <= 100; i++) {
//加鎖
lock.lock();
//需要同步的代碼
//判斷有沒有票
if (num > 0) {
System.out.println(name + "出票: " + num);
num--;
} else {
break;
}
//解鎖
lock.unlock();
}
}
}