文章目錄
- (一)執行緒與行程
- (1)概述
- (2)由來
- (3)執行緒的調度
- (4)多行程與多執行緒的理解
- (5)Thread類(執行緒類)
- <1>構造方法:
- <2>靜態方法
- <3>實體方法
- <3>具體方法
- <4>常用方法詳解(實體演示)
- (6)多執行緒的創建方式
- <1>繼承Thread類
- <2>實作Runnable介面
- <3>兩種創建方式的區別
- (七)執行緒的休眠:
- (八)執行緒的中斷:
- (九)執行緒守護
- <10>執行緒安全
- 【1】執行緒不安全
- 【2】執行緒不安全原因
- 【3】解決方法
- {1}解決方法1:同步代碼塊
- {2}解決方法2:同步方法
- {3}解決方法3:顯示鎖LOCK
- (二)同步與異步
- (三)并發與并行
(一)執行緒與行程
(1)概述
行程:
是指一個記憶體中運行的應用程式,每個行程都有一個獨立的記憶體空間執行緒,
執行緒:
是行程中的一個執行路徑,共享一個記憶體空間,執行緒之間可以自由切換并發執行,一個行程最少有一個執行緒,
實際上是在行程基礎之上的進一步劃分,一個行程啟動之后,里面的若干執行路徑又可以劃分成若干個執行緒,



(2)由來
在早期的作業系統里,計算機都是單核處理器,只有一個核心,行程是執行程式的最小單位,任務調度采用時間片輪轉的搶占式方式進行行程調度,每個行程都有各自的一塊獨立的記憶體,保證行程彼此間的記憶體地址空間的隔離,
計算機病毒的誕生讓計算機程式的安全得到重視,在早期在只有一個核心的情況下,計算機一旦中病毒就記憶體有可能被占滿,cpu不會執行其他的操作,也無法進行鍵盤輸入滑鼠移動等操作,
隨著計算機技術的發展,行程出現了很多弊端
(1)行程的創建、撤銷和切換的開銷比較大
(2)由于對稱多處理機的出現,可以滿足多個運行單位,而多行程并行開銷過大,
執行緒的引入:
由于行程出現了很多弊端,這個時候就引入了執行緒的概念, 執行緒也叫輕量級行程,它是一個基本的CPU執行單元,也是程式執行程序中的最小單元,由執行緒ID、程式計數器、暫存器集合 和堆疊共同組成,執行緒的引入減小了程式并發執行時的開銷,提高了作業系統的并發性能, 執行緒沒有自己的系統資源,只擁有在運行時必不可少的資源,但執行緒可以與同屬與同一行程的其他執行緒共享行程所擁有的其他資源,
(3)執行緒的調度
分時調度
所有執行緒輪流使用 CPU 的使用權,平均分配每個執行緒占用 CPU 的時間,
搶占式調度
優先讓優先級高的執行緒使用 CPU,如果執行緒的優先級相同,那么會隨機選擇一個(執行緒隨機性),Java使用的為搶占式調度,
CPU使用搶占式調度模式在多個執行緒間進行著高速的切換,對于CPU的一個核新而言,某個時刻,只能執行一個執行緒,而 CPU的在多個執行緒間切換速度相對我們的感覺要快,看上去就是 在同一時刻運行, 其實,多執行緒程式并不能提高程式的運行速度,但能夠提高程式運行效率,讓CPU的 使用率更高
(4)多行程與多執行緒的理解
在此參考一下廖雪峰老師的網站
(5)Thread類(執行緒類)
<1>構造方法:

<2>靜態方法

<3>實體方法


<3>具體方法


<4>常用方法詳解(實體演示)
public static void main(String[] args) {
//輸出當前行程的名字:main
System.out.println(Thread.currentThread().getName());
//必須加.start();不然執行緒無法執行
new Thread(new MyRunnable(),"端午").start();
new Thread(new MyRunnable(),"重陽").start();
new Thread(new MyRunnable()).start();
new Thread(new MyRunnable()).start();
new Thread(new MyRunnable()).start();
//由運行結果可以看出執行緒不保證屬性,是搶占式
Thread t = new Thread(new MyRunnable());
t.setName("除夕");
t.start();
}
public static class MyRunnable implements Runnable{
@Override
public void run() {
//輸出當前任務行程的名字
System.out.println(Thread.currentThread().getName());
}
}

分析: 此程式運用的常用方法有
| 變數和型別 | 方法 | 描述 |
|---|---|---|
| void | run() | 如果此執行緒是使用單獨的Runnable運行物件構造的,則呼叫該Runnable物件的run方法; 否則,此方法不執行任何操作并回傳, |
| void | start() | 導致此執行緒開始執行; Java虛擬機呼叫此執行緒的run方法, |
| static Thread | currentThread() | 回傳對當前正在執行的執行緒物件的參考, |
| void | setName?(String name) | 將此執行緒的名稱更改為等于引數 name , |
| 構造器 | Thread?(Runnable target, String name) | 分配新的 Thread物件, |
| 構造器 | Thread?(Runnable target) | 分配新的 Thread物件, |
(6)多執行緒的創建方式
<1>繼承Thread類
public static void main(String[] args) {
MyThread myThread = new MyThread();
//不用呼叫run,用start
myThread.start();
for(int i = 0;i<5;i++) {
System.out.println("處處蚊子咬"+i);
}
}
//繼承Thread類
public class MyThread extends Thread{
@Override
public void run() {
for (int i = 0;i<5;i++)
System.out.println("春眠不覺曉"+i);
}
}
多次運行結果不一致


可以看出Java是搶占式分配,
程式運行圖示:


<2>實作Runnable介面
public static void main(String[] args) {
//1.創建一個任務物件
MyRunnable r = new MyRunnable();
//2.創建一個執行緒,并分配一個任務物件
Thread k = new Thread(r);
//3.執行執行緒
k.start();
for(int i =0;i<5;i++){
System.out.println("處處蚊子咬"+i);
}
}
//實作Runnable介面
public class MyRunnable implements Runnable{
@Override
public void run() {
//執行緒執行的任務
for(int i =0;i<5;i++){
System.out.println("春眠不覺曉"+i);
}
}
}


<3>兩種創建方式的區別
實作Runnable 與 繼承Thread 相比有
如下優勢:
1.通過創建任務,然后給執行緒分配的方式來實作的多執行緒,更適合多個執行緒同時執行相同任務的情況,
2.可以避免單繼承所帶來的局限性,
3.任務與執行緒本身是分離的,提高了程式的健壯性,
4.執行緒池技術只接受Runnable型別的任務,不接受Thread型別的任務,
然而不代表繼承Thread方法就不能用,下面運用內部類的方法就很方便,
public static void main(String[] args) {
//創建一個匿名內部類,任務只用執行一次不需要呼叫
new Thread(){
@Override
public void run() {
for(int i =0;i<5;i++){
System.out.println("春眠不覺曉"+i);
}
}
}.start();
for(int i =0;i<5;i++){
System.out.println("處處蚊子咬"+i);
}
}

(七)執行緒的休眠:
public static void main(String[] args) throws InterruptedException {
for(int i =0;i<5;i++){
System.out.println(i);
//休眠1000毫秒(1)秒,sleep是靜態方法
Thread.sleep(1000);
}
}
運行結果:

(八)執行緒的中斷:
public static void main(String[] args) throws InterruptedException {
Thread t = new Thread(new MyRunnable());
t.start();
for(int i =0;i<5;i++){
System.out.println(Thread.currentThread().getName()+i);
try{
Thread.sleep(1000);
}catch (InterruptedException e){
e.printStackTrace();
}
}
//main執行緒已經結束,呼叫t執行緒中斷,t執行緒進入例外return跳出執行緒
t.interrupt();
}
public static class MyRunnable implements Runnable{
@Override
public void run() {
for(int i =0;i<10;i++){
System.out.println(Thread.currentThread().getName()+i);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
System.out.println("發現中斷標記,執行緒自殺");
return;
}
}
}
}
運行結果:

(九)執行緒守護
執行緒的分類: 守護執行緒和用戶執行緒
用戶執行緒:當一個行程不包含任何的存活的用戶執行緒時,行程結束,
守護執行緒:守護用戶執行緒的,當最后一個用戶執行緒結束時,所以守護執行緒自動死亡,
public static void main(String[] args) throws InterruptedException {
Thread t = new Thread(new MyRunnable());
//設定守護執行緒
t.setDaemon(true);
t.start();
for(int i =0;i<5;i++){
System.out.println(Thread.currentThread().getName()+":"+i);
try{
Thread.sleep(1000);
}catch (InterruptedException e){
e.printStackTrace();
}
}
//main方法還需要結束,守護執行緒還沒有死,再輸出一次
}
public static class MyRunnable implements Runnable{
@Override
public void run() {
for(int i =0;i<10;i++){
System.out.println(Thread.currentThread().getName()+":"+i);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
運行結果:

<10>執行緒安全
【1】執行緒不安全
public static void main(String[] args) {
Runnable t = new Ticket();
new Thread(t).start();
new Thread(t).start();
new Thread(t).start();
}
public static class Ticket implements Runnable{
private int count = 5;
@Override
public void run() {
while(count>0){
System.out.println("正在準備出票");
try {
Thread.sleep(1000);
}catch (InterruptedException e){
e.printStackTrace();
}
count--;
System.out.println("出票成功,還剩票數為:"+count);
}
}
}
運行結果:

可以看出不應該出現負數,三個執行緒執行同一個任務造成了執行緒不安全,
【2】執行緒不安全原因
在判斷和使用資料直接間隔了幾行代碼,在中間執行的時候被其他執行緒插足,把資料改變了,造成了資料不符合預期,
【3】解決方法
一個執行緒在間隔區執行時不讓其他執行緒插足,排隊執行,
{1}解決方法1:同步代碼塊
public static void main(String[] args) {
//只創建了一個new Ticket物件,所以是同一個o,同一把鎖才能排隊
Runnable t = new Ticket();
new Thread(t).start();
new Thread(t).start();
new Thread(t).start();
}
public static class Ticket implements Runnable {
private int count = 5;
private Object o = new Object();
@Override
public void run() {
while (true) {
//開始上鎖
synchronized (o) {
if (count > 0) {
System.out.println("正在準備出票");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
count--;
System.out.println(Thread.currentThread().getName()+"出票成功,還剩票數為:" + count);
}else{
break;
}
}
}
}
}
運行結果:

執行緒0第一個搶到鎖,在回圈執行結束鎖標志被清除,此時距離鎖位置最近,反手又打上標記,所以搶到的幾率最大,程式效率不高,
{2}解決方法2:同步方法
public static void main(String[] args) {
Runnable t = new Ticket();
new Thread(t).start();
new Thread(t).start();
new Thread(t).start();
}
public static class Ticket implements Runnable {
private int count = 10;
@Override
public void run() {
while (true) {
//在回圈中呼叫sale方法
boolean flag = sale();
if (!flag) {
//退出死回圈
break;
}
}
}
//給方法上鎖
public synchronized boolean sale() {
if (count > 0) {
System.out.println("正在準備出票");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
count--;
System.out.println(Thread.currentThread().getName() + "出票成功,還剩票數為:" + count);
return true;
} else {
return false;
}
}
}
運行結果:

{3}解決方法3:顯示鎖LOCK
同步方法塊和同步方法屬于隱示鎖
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class Demo6 {
public static void main(String[] args) {
Runnable t = new Ticket();
new Thread(t).start();
new Thread(t).start();
new Thread(t).start();
}
public static class Ticket implements Runnable {
private int count = 5;
//創建鎖物件
private Lock l = new ReentrantLock();
@Override
public void run() {
while (true) {
//上鎖
l.lock();
if (count > 0) {
System.out.println("正在準備出票");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
count--;
System.out.println("出票成功,還剩票數為:" + count);
} else {
break;
}//if陳述句運行結束,解鎖
l.unlock();
}
}
}
}
運行結果:

(二)同步與異步
同步: 排隊執行 , 效率低但是安全.
異步: 同時執行 , 效率高但是資料不安全.
(三)并發與并行
并發: 指兩個或多個事件在同一個時間段內發生,
并行: 指兩個或多個事件在同一時刻發生(同時發生)
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/256741.html
標籤:其他
上一篇:MongoDB安裝
