-
程式是指執行中的程式
-
程式是程式的一次執行過程,或是正在執行的一個程式。是動態過程:有它自身的產生、存在和消亡的過程
解釋:電腦開啟一個程式,程式一執行就是程式,程式會佔用記憶體空間,關閉程式。記憶體釋放
什麼是執行緒
-
執行緒時有程式建立的,是程式的一個實體
-
一個程式可以擁有多個執行緒
解釋:開啟迅雷是一個程式,迅雷同時下載多個資源,為多個執行緒
什麼是單執行緒和多執行緒
-
單執行緒:同一時刻,只允許執行一個執行緒
-
解釋:迅雷同時下載多個資源,為多個執行緒---多執行緒
什麼是併發和並行
-
併發:同一時刻,多個任務交替執行。也就是單核CPU實現的多工就是併發
-
並行:同一時刻,多個任務同時執行,多核CPU可以實現並行
解釋
-
併發:一個CPU來回執行多個程式
-
並行:兩個CPU同時各自實現自己的一個程式
-
併發和並行同時存在:兩個CPU同時各自實現自己的多個程式
擴充套件:
獲取自己電腦上有幾個CPU
Runtime runtime = Runtime.getRuntime();
int cpunums = runtime.availableProcessors();
建立執行緒
-
繼承Thread類,重寫run方法
-
實現Runnable介面,重寫run()方法
入門案例1---繼承Thread類
要求:1. 編寫程式,開啟一個執行緒,該執行緒每個1秒。在控制檯輸出“喵喵,我是小貓咪”,當輸出80次後,結束該程式
package threaduse;
//通過繼承Thread類,建立執行緒
public class Thread01 {
public static void main(String[] args) {
cat cat = new cat();
cat.start();
}
}
class cat extends Thread {
多執行緒機制
-
執行程式碼:開啟程式
-
執行main方法:開啟main執行緒
-
main方法執行執行緒類的start方法:開啟執行緒類的執行緒
-
main執行緒啟動了執行緒類,不會發生阻塞,會和執行緒類的執行緒交替執行
需知點
-
main執行緒會和執行緒類執行緒同時執行
-
main執行緒可以繼續開其他執行緒,執行緒類也可以開其他執行緒
-
main執行緒結束、執行緒類執行緒不會結束
-
程式中的全部執行緒結束,程式才會消失
為什麼呼叫是start方法,而不是run方法
-
呼叫start方法是啟動執行緒---》最終會呼叫執行緒類的run方法
-
直接呼叫run方法的話,run方法其實就是一個普通的方法,沒有真正的啟動執行緒,會把run方法執行完,才會先下執行
-
真正實現多執行緒的效果是start0方法,而不run方法
步驟是:
-
start方法
-
start0方法 start0()是本地方法,由JVM呼叫
-
實際上是:在start0方法裡面用多執行緒的機制來呼叫run方法
入門案例---實現Runnable介面
說明:java是單繼承的,在某些情況下,一個繼承了某個父類,就不能再繼承Thread類,這就用到了介面
需求:請編寫程式,該程式可以每隔1秒,在控制檯輸出hi,輸出10次後,自動退出,用Runnable介面方式實現
package threaduse;
public class Runnable01 {
public static void main(String[] args) {
Dog dog = new Dog();
Thread thread = new Thread(dog);
thread.start();
}
}
class Dog implements Runnable{
需知點:
-
因為Dog類沒辦法呼叫start方法
-
所以建立了Thread類,把dog物件放入Thread物件中,就可以呼叫run方法了,因為這裡底層使用設計模式【代理模式】
程式碼模擬 瞭解代理模式
解釋為啥可以把dog物件放入Thread物件中,就可以呼叫run方法了
package threaduse;
public class Runnable01 {
public static void main(String[] args)
Tiger tiger = new Tiger();
ThreadProxy threadProxy = new ThreadProxy(tiger);
threadProxy.start();//1.
}
}
class Animal{}
class Tiger extends Animal implements Runnable{
入門案例---多執行緒執行
需求:編寫一個程式,建立兩個執行緒,一個執行緒每隔1秒輸出“HelloWorld”,輸出10次,退出。一個執行緒每隔1秒輸出“Hi",輸出5次退出
package threaduse;
public class Thread02 {
public static void main(String[] args) {
T t = new T();
TT tt = new TT();
Thread thread1 = new Thread(t);
Thread thread2 = new Thread(tt);
thread1.start();
thread2.start();
}
}
class T implements Runnable{
執行緒終止
-
當執行緒完成任務後,會自動退出
-
通過使用變數來控制run方法退出的方式停止執行緒,即通知方式
package threaduse;
public class ThreadExit {
public static void main(String[] args) throws InterruptedException {
T1 t1 = new T1();
Thread thread = new Thread(t1);
thread.start();
Thread.sleep(10*1000);
//如果希望main執行緒去控制t1 執行緒的終止,必須修改a的值
t1.setA(false);
}
}
class T1 implements Runnable{
int b = 0;
private boolean a = true;
執行緒常用方法
-
常用方法1
-
setName 設定執行緒名稱,使之與引數的name相同
-
getName 返回執行緒的名稱
-
start 使執行緒開始執行;java虛擬機器底層呼叫該執行緒的start0方法
-
run 呼叫執行緒物件run方法
-
setPriority 更改執行緒的優先順序
-
getPriority 獲取執行緒的優先順序
-
sleep 在指定的毫秒數內讓當前正在執行的執行緒休眠(暫停執行)
-
interrupt 中斷執行緒
-
-
常用方法1注意點
-
start底層會建立新的執行緒,呼叫run,run就是一個簡單的方法呼叫,不會啟動新執行緒
-
執行緒的優先順序範圍 MAX_PRIORITY 10 MIN_PRIORITY 1 NORM_PRIORITY 5
-
interrupt,中斷執行緒,但並沒有真正的結束執行緒。一般用於中斷正在休眠的執行緒
-
sleep:執行緒的靜態方法,使當前執行緒休眠
-
-
常用方法1程式碼實現
package threaduse;
public class ThreadMethod01 {
public static void main(String[] args) throws InterruptedException {
T2 t2 = new T2();
t2.setName("常世超");
t2.setPriority(Thread.MIN_PRIORITY);
t2.start();//啟動子執行緒
//讓主執行緒列印5次,中斷子執行緒的休眠
for (int i = 0; i <5 ; i++) {
Thread.sleep(1000);
System.out.println("hi"+i);
}
t2.getPriority();
t2.interrupt();//當執行到這裡 就會中斷t2執行緒的休眠
}
}
class T2 extends Thread{
-
常用方法2
-
yield :執行緒的禮讓,讓其他執行緒執行,但禮讓的時間不確定,所以不一定禮讓成功
-
join :執行緒插隊,插隊的執行緒一旦插隊成功,則肯定先執行完插入的執行緒所有任務
-
-
常用方法2程式碼實現
package threaduse;
public class ThreadMethod02 {
public static void main(String[] args) throws InterruptedException {
T3 t3 = new T3();
Thread thread = new Thread(t3);
thread.start();
for (int i = 1; i <= 20; i++) {
Thread.sleep(1000);
System.out.println("main正在進行第" + i + "次衝刺");
if (i == 10) {
thread.yield();//執行緒的禮讓
// thread.join();//執行緒插隊
}
}
}
}
class T3 implements Runnable {
-
常用方法3
-
使用者執行緒:也叫工作執行緒,當執行緒的任務執行完或者通知方式結束
-
守護執行緒:一般是為工作執行緒服務的,當所有的使用者執行緒結束,守護執行緒自動結束
-
常見的守護執行緒:垃圾回收機制
-
-
程式碼實現
package threaduse;
public class ThreadMethod03 {
public static void main(String[] args) throws InterruptedException {
A2 a2 = new A2();
a2.setDaemon(true);
a2.start();
for (int i = 1; i <=10 ; i++) {
System.out.println("我是使用者執行緒"+i);
Thread.sleep(1000);
}
}
}
class A2 extends Thread{
@Override
public void run() {
for ( ; ;) {
System.out.println("我是守護執行緒");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
常用方法練習題
package threaduse;
/*1. 主執行緒每個1秒,輸出hi,一共10次
* 2.當輸出到 hi 5時 ,啟動一個子執行緒(要求實現Runnable介面),每個1s輸出hello,等該執行緒輸出10次後,退出
* 3.主執行緒繼續輸出hi,直到執行緒結束*/
public class ThreadMethodTest {
public static void main(String[] args) throws InterruptedException {
A a = new A();
Thread thread = new Thread(a);
for (int i = 1;i<=10;i++){
Thread.sleep(1000);
System.out.println("hi"+i);
if(i==5){
thread.start();
thread.join();
}
}
}
}
class A implements Runnable{
@Override
public void run() {
for (int i = 1; i <=10 ; i++) {
System.out.println("hello"+i);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
執行緒的生命週期
-
-
-
Ready----就緒狀態
-
Running----執行狀態
-
-
-
-
-
售票案例引出執行緒同步鎖
出現的問題:
-
問題1.重賣:一張票賣給了多個人
-
問題2.超賣:出現了票數為0甚至是負數的情況
package threaduse;
public class Mai {
public static void main(String[] args) {
Csc csc = new Csc();
Thread thread1 = new Thread(csc);
Thread thread2 = new Thread(csc);
Thread thread3 = new Thread(csc);
thread1.start();
thread2.start();
thread3.start();
}
}
class Csc implements Runnable{
int a = 100;
@Override
public void run() {
while (true){
if(a<=0) {
System.out.println("售票結束");
break;
}
try {
//讓程式休眠後出現的兩個問題:
//問題1.重賣:一張票賣給了多個人
//問題2.超賣:出現了票數為0甚至是負數的情況
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("視窗"+Thread.currentThread().getName()+"\t"+(--a));
}
}
}
同步鎖
-
判斷程式有沒有出現執行緒安全問題:
在多執行緒程式中 + 有共享資料 + 多條語句操作共享資料
-
同步與非同步:
-
同步:體現了排隊的效果,同一時刻只能有一個執行緒佔用資源,其他沒有許可權的執行緒排隊
壞處:效率低,
好處:安全
-
非同步:體現了多執行緒搶佔資源的效果,執行緒間互想不等待,互相搶佔資源
壞處:有安全隱患
好處:效率高
-
-
同步鎖關鍵字:synchronized
-
實現同步具體方法
-
同步程式碼塊
synchronized (鎖物件){//使用的鎖物件型別任意,但注意:必須唯一!
//需要同步的程式碼(也就是可能出現問題的操作共享資料的多條語句);
} -
同步方法
public synchronized void m(){
//需要同步的程式碼
}
-
-
使用同步鎖的前提
-
同步需要兩個或者兩個以上的執行緒(單執行緒無需考慮多執行緒安全問題)
-
多個執行緒間必須使用同一個鎖(我上鎖後其他人也能看到這個鎖,不然我的鎖鎖不住其他人,就沒有了上鎖的效果)
-
-
鎖物件
-
(非靜態的)同步方法的鎖可以是this,也可以是其他物件(要求是同一物件)
public synchronized void aVoid(){
System.out.println("我愛你");
}
//解釋(非靜態的)同步方法的鎖可以是this,也可以是其他物件(要求是同一物件)
public void a(){
Object o = new Object();
synchronized (this){
// synchronized (o){
System.out.println("我愛你");
}
} -
(靜態的)同步方法的鎖物件是當前類本身
public static synchronized void a(){
System.out.println("我愛你");
}
//解釋靜態的同步方法鎖物件時類本身
public static void a(){
synchronized (Csc.class){
System.out.println("我愛你");
}
} -
同步程式碼塊的鎖:
@Override
public void run() {
Object o = new Object();
synchronized (o) {}
}
@Override
public void run() {
synchronized (this) {}
}
-
-
執行緒的死鎖
-
介紹
多個執行緒都佔用了對方的鎖資源、但不肯想讓,導致了死鎖,在程式設計是一定要避免死鎖的發生
-
應用案例
媽媽:你先寫作業,我讓你玩手機
兒子:你先讓我玩手機,我就寫作業
-
程式碼實現
package threaduse;
public class DeadLock {
public static void main(String[] args) {
DeadLockDemo A = new DeadLockDemo(true);
DeadLockDemo B = new DeadLockDemo(false);
A.setName("執行緒A");
B.setName("執行緒B");
A.start();
B.start();
}
}
class DeadLockDemo extends Thread{
static Object o1 = new Object();
static Object o2 = new Object();
boolean flag ;
public DeadLockDemo(boolean flag) {
this.flag = flag;
}
@Override
public void run() {
/*下面有業務邏輯
* 1.如果flag 為T ,執行緒A 就會先得到/持有 o1 物件鎖,然後嘗試去獲取o2 物件鎖
* 2.如果執行緒A 得不到 o2 物件鎖,就會Blocked
* 3.如果flag 為F , 執行緒B就會就會先得到/持有 o2 物件鎖,然後嘗試去獲取o1 物件鎖
* 4.如果執行緒B 得不到 o1 物件鎖,就會Blocked*/
if(flag) {
synchronized (o1) {
System.out.println(Thread.currentThread().getName()+"進入狀態1");
synchronized (o2) {
System.out.println(Thread.currentThread().getName()+"進入狀態2");
}
}
}else {
synchronized (o2) {
System.out.println(Thread.currentThread().getName()+"進入狀態3");
synchronized (o1) {
System.out.println(Thread.currentThread().getName()+"進入狀態4");
}
}
}
}
}
釋放鎖
-
下面操作會釋放鎖
-
當前執行緒的同步方法、同步程式碼塊執行結束
案例:上廁所,完事出來
-
當前執行緒在同步方法、同步程式碼塊中遇到break、return。
案例:沒有正常完事,經理叫他修BUG,不得不出來
-
當前執行緒在同步方法、同步程式碼塊中出現了未處理的Error或Exception,導致異常結束
案例:沒有正常完事,發現為帶紙,不得不出來
-
當前執行緒在同步方法、同步程式碼塊中執行了wait()方法,當前執行緒暫停,並釋放鎖
案例:沒有正常完事,覺得需要醞釀一下,所以出來等會再進去
-
-
下面操作不會釋放鎖
-
執行緒執行同步方法、程式碼塊時,程式呼叫了Thread.sleep()、Thread.yield()方法暫停當前執行緒的執行、不會釋放鎖
案例:上廁所,眯一會
-
執行緒執行同步方法、程式碼塊時,其他執行緒呼叫了suspend()方法將執行緒掛起,該執行緒不會釋放鎖
提示:應儘量避免使用suspend()和resume()來控制執行緒,方法不再推薦使用
-
-