java學習day18~19---多執行緒
一.執行緒和程序(單核)
程序:CPU中有多個程式“同時”在使用,CPU在一個時間點上只能執行一件事情,而CPU分片段執行不同程式,
但是切換速率十分快,給人感覺是在同時使用
程序的作用不是提高執行速度,而是提高CPU的使用率
執行緒:一個程序中的執行場景,一個程序有多個執行緒,如淘寶搶購,都在使用百度搜索
執行緒的作用不是提高執行速度,而是提高應用程式的使用率
執行緒的存在:
執行緒之間的堆記憶體和方法區記憶體是共享的,但不同執行緒都是不同的棧記憶體,
二.執行緒的建立和啟動
方式1:子類繼承Thread,重寫裡面的run方法,呼叫start方法啟動(也可以不用子類,直接new一個Thread,用匿名內部類的方式)
注意:run方法不能丟擲異常
public class ThreadWork { public static void main(String[] args) { Thread t = new Processer(); t.start(); //這個程式碼告訴JVM給t執行緒分配一個新的棧,啟動之後自動呼叫run方法 for(int i = 0; i < 100; i++) { System.out.println("main---" + i); } } } class Processer extends Thread { public void run() { for(int i = 0; i < 100; i++) { System.out.println("thread---" + i); } } }
//匿名內部類的方式
public class ThreadWork { public static void main(String[] args) { Thread t = new Thread() { public void run() { for(int i = 0; i < 100; i++) { System.out.println("thread---" + i); } } }; t.start(); for(int i = 0; i < 100; i++) { System.out.println("main---" + i); } } }
//拉姆達表示式簡化後
public class ThreadWork
{
public static void main(String[] args) {
new Thread(() -> {
for(int i = 0; i < 100; i++) {
System.out.println("thread---" + i);
}
}).start();
for(int i = 0; i < 100; i++) {
System.out.println("main---" + i);
}
}
}
//執行結果:兩個迴圈隨機互動執行
//結論:main方法結束之後只是主執行緒結束,但是其他執行緒還有棧幀,所以main方法結束,程式可能還在執行
//注意:如果只調用run方法,那麼這就只是普通的方法呼叫,整個程式只有一個主執行緒,等到run方法執行結束之後,才會執行main方法中的迴圈
方式2:子類繼承Runnable介面,實現裡面的run方法(推薦使用)
public class ThreadWork
{
public static void main(String[] args) {
Thread t = new Thread(new Processer()); //Thread t = new Thread(Runnable介面);
t.start(); //這個程式碼告訴JVM給t執行緒分配一個新的棧,啟動之後自動呼叫run方法
for(int i = 0; i < 100; i++) {
System.out.println("main---" + i);
}
}
}
class Processer implements Runnable
{
public void run() {
for(int i = 0; i < 100; i++) {
System.out.println("thread---" + i);
}
}
}
三.執行緒的生命週期
進入就緒狀態之後,執行緒有權利去搶奪CPU時間片,這個時間片就是執行權,搶到之後就可以進入執行狀態,當時間
用完而執行還沒有結束之後,就會繼續返回到就緒狀態,繼續搶奪時間片,然後繼續接著執行run方法,直至方法執行完畢
用第一個建立執行緒的例子來講,得到的結果是兩個迴圈交替輸出,這就是因為兩個執行緒搶奪時間片,先搶到的先執行,
搶到的時間不夠返回來繼續搶奪執行,直至執行緒結束
四.常用的方法
getName(); 獲取執行緒名字
currentThread(); 獲取當前的執行緒
sleep(); 讓當前執行緒休眠一段指定時間,阻塞執行緒,讓CPU騰出時間片,讓其他執行緒執行,是一個靜態方法
getPriority(); 獲取執行緒優先順序
join(); 等待某個執行緒執行結束
interrupt() 可以打斷正在等待的執行緒(包括sleep, join的等待)
yield() 讓位,給同一個優先順序的執行緒讓位,但是讓位時間不固定,也是靜態方法
不推薦使用的方法
stop() 讓執行緒停止
suspend() 讓執行緒暫停
resume() 讓執行緒繼續
sleep()詳解
public class ThreadWork
{
public static void main(String[] args) throws Exception{
Thread t = new Thread(() -> {
for(int i = 0; i < 30; i++) {
System.out.println(Thread.currentThread().getName() + "---" + i);
}
});
t.setName("t1"); //將執行緒命名為t1
t.start(); //讓執行緒執行
t.sleep(5000); //此時呼叫sleep方法會讓t執行緒休眠5秒嗎
System.out.println("hello");
}
}
解析:不會,因為sleep是靜態方法,與物件無關,t.start()相當於是Thread.start(), 而Thread當前是主執行緒
所以t執行緒不會休眠,而是主執行緒休眠
//interrupt()詳解
public class ThreadWork
{
public static void main(String[] args) throws Exception{
Thread t = new Thread(() -> {
try {
Thread.sleep(1000000000000L); //很長時間段的休眠
}
catch (Exception e) {
e.printStackTrace();
}
for(int i = 0; i < 30; i++) {
System.out.println("thread" + i);
}
});
t.start(); //讓執行緒執行
Thread.sleep(5000); //主執行緒休眠五秒
t.interrupt(); //五秒後打斷t執行緒的休眠
System.out.println("hello");
}
}
//結果:五秒後t執行緒執行語句,因為休眠被打斷
五.執行緒同步
同步程式設計模型:t1和t2執行緒必須等待一個執行完畢之後,再執行另一個
非同步程式設計模型:t1和t2執行緒各自執行各自的,誰也不等誰引入執行緒同步的原因:為了資料安全
使用執行緒同步的條件:
1.多執行緒環境 2.共享一個數據 3.共享的資料涉及到修改操作
public class ThreadWork
{
public static void main(String[] args) {
Account a = new Account("張三", 5000);
Thread t1 = new Thread(new Processor(a));
Thread t2 = new Thread(new Processor(a));
t1.start();
t2.start();
}
}
class Processor implements Runnable
{
Account a;
public Processor(Account a) {
this.a = a;
}
public void run() {
a.withdraw(200);
System.out.println("取了200,還剩" + a.getBalance());
}
}
//賬戶類
class Account
{
private String actno;
private int balance;
public Account(String actno, int balance) {
this.actno = actno;
this.balance = balance;
}
public void setActno(String actno) {
this.actno = actno;
}
public String getActno() {
return actno;
}
public void setBalance(int balance) {
this.balance = balance;
}
public int getBalance() {
return balance;
}
//提供一個取款方法
public void withdraw(int money) {
int after = balance - money;
this.setBalance(after);
}
}
//執行結果:隨機輸出4600,4800
//因為兩個執行緒互動了,一個取款完畢之後,還沒有更新,第二個有可能也已經執行,所以餘額有問題
此時需要兩個執行緒分別執行,引入執行緒同步
//其他程式碼不變,修改取款操作
public void withdraw(int money) {
synchronized(this) { //用鎖將取款的具體操作鎖住
try{
Thread.sleep(1000);
} catch(Exception e) {
}
int after = balance - money;
this.setBalance(after);
}
}
用synchronized(this)鎖住,那麼程式執行的時候,就回去尋找this物件鎖,找到則執行,執行完畢歸還物件鎖,否則等待,直到獲取物件鎖
六.類鎖
關於類鎖:類鎖只有一把,且與物件無關
雖然t1和t2是兩個不同的執行緒,但是公用一個myClass物件,而該類中的方法用的是類鎖,類鎖只有一把,與物件沒有關係
public class ThreadWork {
public static void main(String[] args) throws Exception {
myClass mc = new myClass();
Processor p1 = new Processor(mc);
Processor p2 = new Processor(mc);
Thread t1 = new Thread(p1);
Thread t2 = new Thread(p2);
t1.setName("t1");
t2.setName("t2");
t1.start();
Thread.sleep(1000); //為了保證t1執行緒先執行
t2.start();
}
}
class Processor implements Runnable {
myClass mc;
Processor(myClass mc) {
this.mc = mc;
}
public void run() {
if(Thread.currentThread().getName().equals("t1")) {
mc.m1();
}
if(Thread.currentThread().getName().equals("t1")) {
mc.m2();
}
}
}
class myClass
{
public synchronized static void m1() {
try{
Thread.sleep(5000);
} catch (Exception e) {
e.printStackTrace();
}
System.out.println("m1...");
}
public synchronized static void m2() {
System.out.println("m2...");
}
}
七.死鎖
兩個執行緒互相鎖住,如兩個執行緒t1,t2,兩個物件o1,o2,t1執行緒先鎖住o1物件,然後鎖o2物件,t2執行緒先鎖o2物件,再鎖o1物件,但是因為兩個執行緒誰都不肯先放,所以進入了僵持狀態,形成死鎖
public class ThreadWork
{
public static void main(String[] args) {
Object o1 = new Object();
Object o2 = new Object();
Thread t1 = new Thread(new T1(o1, o2));
Thread t2 = new Thread(new T2(o1, o2));
t1.start();
t2.start();
}
}
class T1 implements Runnable
{
Object o1;
Object o2;
T1(Object o1, Object o2) {
this.o1 = o1;
this.o2 = o2;
}
public void run() {
synchronized(o1) {
try {
Thread.sleep(1000);
} catch(Exception e) {
e.printStackTrace();
}
synchronized(o2) {}
}
}
}
class T2 implements Runnable
{
Object o1;
Object o2;
T2(Object o1, Object o2) {
this.o1 = o1;
this.o2 = o2;
}
public void run() {
synchronized(o2) {
try {
Thread.sleep(1000);
} catch(Exception e) {
e.printStackTrace();
}
synchronized(o1) {}
}
}
}