【Java多執行緒】輕鬆搞定Java多執行緒(二)
輕鬆搞定Java多執行緒(二)
Java 多執行緒詳解(二)
1、執行緒狀態
1.1 執行緒狀態
1.2 執行緒方法
方法 | 說明 |
---|---|
setPriority(int newPriority) | 更改執行緒的優先順序 |
static void sleep(long millis) | 在指定的毫秒級內讓當前正在執行的執行緒休眠 |
void join() | 等待該執行緒終止 |
static void yield() | 暫停當前正在執行的執行緒物件,並執行其他執行緒 |
void interrupt() | 中斷執行緒(不建議使用) |
boolean isAlive() | 測試執行緒是否處於活動狀態 |
停止執行緒
- 不推薦使用JDK提供的stop()、destroy()方法。(已廢棄)
- 推薦執行緒自己停下來
- 建議使用一個標誌位進行終止變數,當flag=false,則終止執行緒執行
/**
* 測試stop
* 1.建議執行緒正常停止--->利用次數,不建議死迴圈
* 2.建議使用標誌位--->設定一個標誌位
* 3.不要使用stop()或destroy()等過時,或者JDk不建議使用的方法
*/
public class TestStop implements Runnable {
/**
* 1.設定一個標誌位
*/
private boolean flag = true;
@Override
public void run() {
int i = 0;
while (flag) {
System.out.println("run...Thread" + i++);
}
}
/**
* 2.設定一個公開的方法停止執行緒,轉換標誌位
*/
public void stop() {
this.flag = false;
}
public static void main(String[] args) {
TestStop testStop = new TestStop();
new Thread(testStop).start();
for (int i = 0; i < 1000; i++) {
System.out.println("main" + i);
if (i == 900) {
/**
* 呼叫stop()方法,切換標誌位,讓執行緒停止
*/
testStop.stop();
System.out.println("執行緒停止了");
}
}
}
}
執行緒休眠
- sleep(時間)指定當前執行緒阻塞的毫秒數;
- sleep存在異常InterruptedException;
- sleep時間達到後執行緒進入就緒狀態;
- sleep可以模擬網路延時,倒數計時等;
- 每一個物件都有一個鎖,sleep不會釋放鎖。
模擬網路延時
/**
* 模擬網路延時:放大問題的發生性
*/
public class TestSleep implements Runnable {
/**
* 票數
*/
private int ticketsNums = 20;
@Override
public void run() {
while (true) {
if (ticketsNums <= 0) {
break;
}
/**
* 模擬延時
*/
try {
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "拿到了" + ticketsNums-- + "票");
}
}
public static void main(String[] args) {
TestSleep ticket = new TestSleep();
new Thread(ticket, "小明").start();
new Thread(ticket, "老師").start();
new Thread(ticket, "黃牛").start();
}
}
模擬倒數計時
/**
* 模擬倒數計時
*/
public class TestSleep2 {
public static void main(String[] args) {
try {
tenDown();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public static void tenDown() throws InterruptedException {
int num = 10;
while (true) {
Thread.sleep(1000);
System.out.println(num--);
if (num <= 0) {
break;
}
}
}
}
執行緒禮讓
- 禮讓執行緒,讓當前正在執行的執行緒暫停,但不阻塞
- 將執行緒從執行狀態轉為就緒狀態
- 讓CPU重新排程,禮讓不一定成功!看CPU心情
/**
* 測試禮讓執行緒
* 禮讓不一定成功
*/
public class TestYield {
public static void main(String[] args) {
MyYield myYield = new MyYield();
new Thread(myYield, "a").start();
new Thread(myYield, "b").start();
}
}
class MyYield implements Runnable {
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + "執行緒開始執行");
/**
* 禮讓
*/
Thread.yield();
System.out.println(Thread.currentThread().getName() + "執行緒停止執行");
}
}
Join
- Join合併執行緒,待此執行緒執行完成後,再執行其他執行緒,其他執行緒阻塞
- 可以想象成插隊
/**
* 測試join方法
*/
public class TestJoin implements Runnable {
@Override
public void run() {
for (int i = 0; i < 100; i++) {
System.out.println("執行緒vip來了" + i);
}
}
public static void main(String[] args) throws InterruptedException {
/**
* vip執行緒
*/
TestJoin testJoin = new TestJoin();
Thread thread = new Thread(testJoin);
thread.start();
/**
* 主執行緒
*/
for (int i = 0; i < 1000; i++) {
if (i == 200) {
thread.join();
}
System.out.println("main" + i);
}
}
}
1.3 執行緒狀態觀測
/**
* 觀察測試執行緒的狀態
*/
public class TestState {
public static void main(String[] args) throws InterruptedException {
Thread thread = new Thread(() -> {
for (int i = 0; i < 5; i++) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("///");
});
/**
* 觀察狀態
*/
Thread.State state = thread.getState();
System.out.println(state);
/**
* 啟動執行緒
*/
thread.start();
state = thread.getState();
System.out.println(state);
/**
* 只要執行緒不終止,就一直輸出狀態
*/
while (state != Thread.State.TERMINATED) {
Thread.sleep(100);
state = thread.getState();
System.out.println(state);
}
}
}
1.4 執行緒優先順序
/**
* 測試執行緒優先順序
*/
public class TestPriority {
public static void main(String[] args) {
/**
* 主執行緒預設優先順序
*/
System.out.println(Thread.currentThread().getName() + "-->" + Thread.currentThread().getPriority());
MyPriority myPriority = new MyPriority();
Thread t1 = new Thread(myPriority);
Thread t2 = new Thread(myPriority);
Thread t3 = new Thread(myPriority);
Thread t4 = new Thread(myPriority);
/**
* 先設定優先順序,再啟動
*/
t2.setPriority(1);
t3.setPriority(4);
t4.setPriority(Thread.MAX_PRIORITY);
t1.start();
t2.start();
t3.start();
t4.start();
}
}
class MyPriority implements Runnable {
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + "-->" + Thread.currentThread().getPriority());
}
}
1.5 守護執行緒
- 執行緒分為使用者執行緒和守護執行緒
- 虛擬機器必須確保使用者執行緒執行完畢
- 虛擬機器不用等待守護執行緒執行完畢
- 如:後臺記錄操作日誌,監控記憶體,垃圾回收等等…
/**
* 測試守護執行緒
* 上帝守護
*/
public class TestDaemon {
public static void main(String[] args) {
God god = new God();
Human human = new Human();
Thread thread = new Thread(god);
/**
* 預設false表示使用者執行緒,正常的執行緒都是使用者執行緒
*/
thread.setDaemon(true);
thread.start();
/**
* 使用者執行緒啟動
*/
new Thread(human).start();
}
}
/**
* 上帝
*/
class God implements Runnable {
@Override
public void run() {
while (true) {
System.out.println("上帝守護");
}
}
}
/**
* 人
*/
class Human implements Runnable {
@Override
public void run() {
for (int i = 0; i < 36500; i++) {
System.out.println("活著");
}
System.out.println("======離開======");
}
}
2、執行緒同步
多個執行緒操作同一個資源
2.1 併發
- 併發:同一個物件被多個執行緒同時操作
2.2 執行緒同步
- 處理多執行緒問題時,多個執行緒訪問同一個人物件(併發問題),並且某些執行緒還想修改這個物件。這個時候我們就需要執行緒同步。
- 執行緒同步其實就是一種等待機制,多個需要同時訪問此物件的執行緒進入這個物件的等待池形成佇列,等待前面執行緒使用完畢,下一個執行緒再使用。
- 形成條件:佇列和鎖
- 由於同一程式的多個執行緒共享同一塊儲存空間,在帶來方便的同時,也帶來了訪問衝突問題,為了保證資料在方法中被訪問的正確性,在訪問時加入鎖機制(synchronized),當一個執行緒獲得物件的排它鎖,獨佔資源,其他執行緒必須等待,使用後釋放鎖即可。存在以下問題:
- 一個執行緒持有鎖會導致其他所有需要此鎖的執行緒掛起
- 在多執行緒競爭下,加鎖,釋放鎖會導致比較多的上下文切換和排程延時,引起效能問題
- 如果一個優先順序高的執行緒等待一個優先順序低的執行緒釋放鎖,會導致優先順序倒置,引起效能問題
不安全的買票
/**
* 不安全的買票
*/
public class UnsafeBuyTicket {
public static void main(String[] args) {
BuyTicket station = new BuyTicket();
new Thread(station, "小明").start();
new Thread(station, "小紅").start();
new Thread(station, "小黑").start();
}
}
class BuyTicket implements Runnable {
/**
* 票
*/
private int ticketNums = 10;
/**
* 外部停止方式
*/
boolean flag = true;
@Override
public void run() {
/**
* 買票
*/
while (flag) {
buy();
}
}
private void buy() {
/**
* 判斷是否有票
*/
if (ticketNums <= 0) {
flag = false;
return;
}
/**
* 模擬延遲
*/
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "拿到" + ticketNums--);
}
}
不安全的取錢
/**
* 不安全的取錢
* 兩個人用同一賬號去銀行取錢
*/
public class UnsafeBank {
public static void main(String[] args) {
Account account = new Account(100, "錢");
Drawing Jack = new Drawing(account, 50, "Jack");
Drawing Rose = new Drawing(account, 100, "Rose");
Jack.start();
Rose.start();
}
}
/**
* 賬戶
*/
class Account {
int money;
String name;
public Account(int money, String name) {
this.money = money;
this.name = name;
}
}
/**
* 銀行:模擬取款
*/
class Drawing extends Thread {
/**
* 賬戶
*/
Account account;
/**
* 取了多少錢
*/
int drawingMoney;
/**
* 現在手裡有多少錢
*/
int nowMoney;
public Drawing(Account account, int drawingMoney, String name) {
this.account = account;
this.drawingMoney = drawingMoney;
super.setName(name);
}
/**
* 取錢
*/
@Override
public void run() {
/**
* 判斷有沒有錢
*/
if (account.money - drawingMoney < 0) {
System.out.println(Thread.currentThread().getName() + "餘額不足");
return;
}
/**
* 模擬延時
*/
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
/**
* 卡內餘額
*/
account.money = account.money - drawingMoney;
/**
* 手裡的錢
*/
nowMoney = nowMoney + drawingMoney;
System.out.println(account.name + "餘額為:" + account.money);
System.out.println(this.getName() + "手裡的錢:" + nowMoney);
}
}
執行緒不安全的集合
/**
* 執行緒不安全的集合
*/
public class UnsafeList {
public static void main(String[] args) {
List<String> list = new ArrayList<>();
for (int i = 0; i < 10000; i++) {
new Thread(() -> {
list.add(Thread.currentThread().getName());
}).start();
}
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(list.size());
}
}
同步方法
- 由於我們可以通過private關鍵字來保證資料物件只能被方法訪問,所以我們只需要針對方法提出一套機制,這套機制就是synchronized關鍵字,它包括兩種用法:
- synchronized方法
- synchronized塊
/**
* 同步方法
*/
public synchronized void method(int args) {}
- synchronized方法控制對“物件”的訪問,每一個物件對應一把鎖,每個synchronized方法都必須獲得呼叫該方法的物件的鎖才能執行,否則執行緒會阻塞,方法一旦執行,就獨佔該鎖,直到該方法返回才釋放鎖,後面被阻塞的執行緒才能獲得這個鎖,繼續執行
同步方法的弊端:
- 方法裡面需要修改的內容才需要鎖,鎖得太多,浪費資源
- 若將一個大的方法宣告為synchronized將會影響效率
安全的買票
/**
* 安全的買票
*/
public class SafeBuyTicket {
public static void main(String[] args) {
BuyTicket station = new BuyTicket();
new Thread(station, "小明").start();
new Thread(station, "小紅").start();
new Thread(station, "小黑").start();
}
}
class BuyTicket implements Runnable {
/**
* 票
*/
private int ticketNums = 10;
/**
* 外部停止方式
*/
boolean flag = true;
@Override
public void run() {
/**
* 買票
*/
while (flag) {
/**
* 模擬延遲
*/
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
buy();
}
}
/**
* synchronized 同步方法,鎖的是this
*/
private synchronized void buy() {
/**
* 判斷是否有票
*/
if (ticketNums <= 0) {
flag = false;
return;
}
/**
* 模擬延遲
*/
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "拿到" + ticketNums--);
}
}
同步塊
- 同步塊:synchronized(Obj) {}
- Obj稱之為同步監視器
- Obj可以是任何物件,但是推薦使用共享資源作為同步監視器
- 同步方法中無需指定同步監視器,因為同步方法的同步監視器就是this,就是這個物件本身,或者是class
- 同步監視器的執行過程
- 第一個執行緒訪問,鎖定同步監視器,執行其中程式碼
- 第二個執行緒訪問,發現同步監視器被鎖定,無法訪問
- 第一個執行緒訪問完畢,解鎖同步監視器
- 第二個執行緒訪問,發現同步監視器沒有鎖,然後鎖定訪問
安全的取錢
/**
* 安全的取錢
* 兩個人用同一賬號去銀行取錢
*/
public class SafeBank {
public static void main(String[] args) {
Account account = new Account(100, "錢");
Drawing Jack = new Drawing(account, 50, "Jack");
Drawing Rose = new Drawing(account, 100, "Rose");
Jack.start();
Rose.start();
}
}
/**
* 賬戶
*/
class Account {
int money;
String name;
public Account(int money, String name) {
this.money = money;
this.name = name;
}
}
/**
* 銀行:模擬取款
*/
class Drawing extends Thread {
/**
* 賬戶
*/
Account account;
/**
* 取了多少錢
*/
int drawingMoney;
/**
* 現在手裡有多少錢
*/
int nowMoney;
public Drawing(Account account, int drawingMoney, String name) {
this.account = account;
this.drawingMoney = drawingMoney;
super.setName(name);
}
/**
* 取錢
* synchronized預設鎖this
*/
@Override
public void run() {
/**
* 同步塊
* 鎖的物件就是變化的量,需要增刪改的物件
*/
synchronized (account) {
/**
* 判斷有沒有錢
*/
if (account.money - drawingMoney < 0) {
System.out.println(Thread.currentThread().getName() + "餘額不足");
return;
}
/**
* 模擬延時
*/
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
/**
* 卡內餘額
*/
account.money = account.money - drawingMoney;
/**
* 手裡的錢
*/
nowMoney = nowMoney + drawingMoney;
System.out.println(account.name + "餘額為:" + account.money);
System.out.println(this.getName() + "手裡的錢:" + nowMoney);
}
}
}
執行緒安全的集合
/**
* 執行緒安全的集合
*/
public class SafeList {
public static void main(String[] args) {
List<String> list = new ArrayList<>();
for (int i = 0; i < 10000; i++) {
new Thread(() -> {
/**
* 同步塊
*/
synchronized (list) {
list.add(Thread.currentThread().getName());
}
}).start();
}
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(list.size());
}
}
JUC安全型別的集合
import java.util.concurrent.CopyOnWriteArrayList;
/**
* 測試JUC安全型別的集合
*/
public class TestJUC {
public static void main(String[] args) {
CopyOnWriteArrayList<String> list = new CopyOnWriteArrayList<>();
for (int i = 0; i < 10000; i++) {
new Thread(() -> {
list.add(Thread.currentThread().getName());
}).start();
}
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(list.size());
}
}
2.3 死鎖
- 多個執行緒各自佔有一些共享資源,並且互相等待其他執行緒佔有的資源才能執行,而導致兩個或者多個執行緒都在等待對方釋放資源,都停止執行的情形。某一個同步塊同時擁有“兩個以上物件的鎖”時,就可能會發生“死鎖”的問題。
/**
* 死鎖:多個執行緒互相抱著對方需要的資源,然後形成僵持
*/
public class DeadLock {
public static void main(String[] args) {
Makeup g1 = new Makeup(0, "一號");
Makeup g2 = new Makeup(1, "二號");
g1.start();
g2.start();
}
}
/**
* 口紅
*/
class Lipstick {}
/**
* 鏡子
*/
class Mirror {}
class Makeup extends Thread {
/**
* 需要的資源只有一份,用static來保證只有一份
*/
static Lipstick lipstick = new Lipstick();
static Mirror mirror = new Mirror();
int choice; // 選擇
String name; // 使用化妝品的人
Makeup(int choice, String name) {
this.choice = choice;
this.name = name;
}
@Override
public void run() {
try {
makeup();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
/**
* 化妝,互相持有對方的鎖,就是需要拿到對方的資源
*/
private void makeup() throws InterruptedException {
if (choice == 0) {
/**
* 獲得口紅的鎖
*/
synchronized (lipstick) {
System.out.println(this.name + "獲得口紅的鎖");
Thread.sleep(1000);
/**
* 一秒鐘後想獲得鏡子
*/
synchronized (mirror) {
System.out.println(this.name + "獲得鏡子的鎖");
}
}
} else {
/**
* 獲得鏡子的鎖
*/
synchronized (mirror) {
System.out.println(this.name + "獲得鏡子的鎖");
Thread.sleep(2000);
/**
* 兩秒鐘後想獲得口紅
*/
synchronized (lipstick) {
System.out.println(this.name + "獲得口紅的鎖");
}
}
}
}
}
死鎖避免方法
- 產生死鎖的四個必要條件:
- 互斥條件:一個資源每次只能被一個程式使用。
- 請求與保持條件:一個程式因請求資源而阻塞時,對已獲得的資源保持不放。
- 不剝奪條件:程式已獲得的資源,在未使用完之前,不能強行剝奪。
- 迴圈等待條件:若干程式之間形成一種頭尾相接的迴圈等待資源關係。
2.4 鎖
- 從JDK5.0開始,Java提供了更強大的執行緒同步機制——通過顯式定義同步鎖物件來實現同步。同步鎖使用Lock物件充當。
- java.util.concurrent.locks.Lock介面是控制多個執行緒對共享資源進行訪問的工具。鎖提供了對共享資源的獨佔訪問,每次只能有一個執行緒對Lock物件加鎖,執行緒開始訪問共享資源之前應先獲得Lock物件。
- ReentrantLock(可重入鎖)類實現了Lock,它擁有與synchronized相同的併發性和記憶體語義,在實現執行緒安全的控制中,比較常用的是ReentrantLock,可以顯式加鎖、釋放鎖。
import java.util.concurrent.locks.ReentrantLock;
/**
* 測試Lock鎖
*/
public class TestLock {
public static void main(String[] args) {
TestLock2 testLock2 = new TestLock2();
new Thread(testLock2).start();
new Thread(testLock2).start();
new Thread(testLock2).start();
}
}
class TestLock2 implements Runnable {
int ticketNums = 10;
/**
* 定義lock鎖
*/
private final ReentrantLock lock = new ReentrantLock();
@Override
public void run() {
while (true) {
lock.lock();
try {
if (ticketNums > 0) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(ticketNums--);
} else {
break;
}
} finally {
lock.unlock();
}
}
}
}
synchronized與Lock的對比
- Lock是顯式鎖(手動開啟和關閉鎖),synchronized是隱式鎖,出了作用域自動釋放。
- Lock只有程式碼塊鎖,synchronized有程式碼塊鎖和方法鎖。
- 使用Lock鎖,JVM將花費較少的時間來排程執行緒,效能更好。並且具有更好的擴充套件性(提供更多的子類)。
- 優先使用順序:
- Lock > 同步程式碼塊(已經進入了方法體,分配了相應的資源) > 同步方法(在方法體之外)
3、執行緒協作
3.1 生產者消費者問題
- 應用場景:
- 假設倉庫中只能存放一件產品,生產者將生產出來的產品放入倉庫,消費者將倉庫中產品取走消費。
- 如果倉庫中沒有產品,則生產者將產品放入倉庫,否則停止生產並等待,直到倉庫中的產品被消費者取走為止。
- 如果倉庫中放有產品,則消費者可以將產品取走消費,否則停止消費並等待,直到倉庫中再次放入產品為止。
這是一個執行緒同步問題,生產者和消費者共享同一個資源,並且生產者和消費者之間相互依賴,互為條件。
在生產者消費者問題中,僅有synchronized是不夠的
- synchronized可阻止併發更新同一個共享資源,實現了同步
- synchronized不能用來實現不同執行緒之間的訊息傳遞(通訊)
3.2 執行緒通訊
- Java提供了幾個方法解決執行緒之間的通訊問題
方法名 | 作用 |
---|---|
wait() | 表示執行緒一直等待,直到其他執行緒通知,與sleep不同,會釋放鎖 |
wait(long timeout) | 指定等待的毫秒數 |
notify() | 喚醒一個處於等待狀態的執行緒 |
notifyAll() | 喚醒同一個物件上所有呼叫wait()方法的執行緒,優先順序高的執行緒優先排程 |
注意:
均是Object類的方法,都只能在同步方法或者同步程式碼塊中使用,否則會丟擲異常IllegalMonitorStateException
3.3 管理法
/**
* 測試:生產者消費者模型-->利用緩衝區解決:管理法
*/
public class TestPC {
public static void main(String[] args) {
SynContainer container = new SynContainer();
new Producer(container).start();
new Consumer(container).start();
}
}
/**
* 生產者
*/
class Producer extends Thread {
SynContainer container;
public Producer(SynContainer container) {
this.container = container;
}
/**
* 生產
*/
@Override
public void run() {
for (int i = 0; i < 100; i++) {
System.out.println("生產了第" + i + "只雞");
container.push(new Chicken(i));
}
}
}
/**
* 消費者
*/
class Consumer extends Thread {
SynContainer container;
public Consumer(SynContainer container) {
this.container = container;
}
/**
* 消費
*/
@Override
public void run() {
for (int i = 0; i < 100; i++) {
System.out.println("消費了第" + i + "只雞");
container.pop();
}
}
}
/**
* 產品
*/
class Chicken {
/**
* 產品編號
*/
int id;
public Chicken(int id) {
this.id = id;
}
}
/**
* 緩衝區
*/
class SynContainer {
/**
* 需要一個容器大小
*/
Chicken[] chickens = new Chicken[10];
/**
* 容器計數器
*/
int count = 0;
/**
* 生產者生產產品
*/
public synchronized void push(Chicken chicken) {
/**
* 如果容器滿了,就需要等待消費者消費
*/
if (count == chickens.length) {
/**
* 通知消費者消費,生產等待
*/
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
/**
* 如果沒有滿,就放入產品
*/
chickens[count] = chicken;
count++;
/**
* 通知消費者消費
*/
this.notifyAll();
}
/**
* 消費者消費產品
*/
public synchronized Chicken pop() {
/**
* 判斷能否消費
*/
if (count == 0) {
/**
* 等待生產者生產,消費者等待
*/
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
/**
* 如果可以消費
*/
count--;
Chicken chicken = chickens[count];
/**
* 消費完通知生產者生產
*/
this.notifyAll();
return chicken;
}
}
3.4 訊號燈法
/**
* 測試生產者消費者問題:訊號燈法
*/
public class TestPC2 {
public static void main(String[] args) {
Film film = new Film();
new Player(film).start();
new Watcher(film).start();
}
}
/**
* 生產者-->演員
*/
class Player extends Thread {
Film film;
public Player(Film film) {
this.film = film;
}
@Override
public void run() {
for (int i = 0; i < 200; i++) {
if (i%2 == 0) {
this.film.play("盜夢空間");
} else {
this.film.play("星際穿越");
}
}
}
}
/**
* 消費者-->觀眾
*/
class Watcher extends Thread {
Film film;
public Watcher(Film film) {
this.film = film;
}
@Override
public void run() {
for (int i = 0; i < 20; i++) {
film.watch();
}
}
}
/**
* 產品-->電影
*/
class Film {
/**
* 演員演戲,觀眾等待 T
* 觀眾觀看,演員等待 F
*/
/**
* 表演的電影
*/
String name;
boolean flag = true;
public synchronized void play(String name) {
if (!flag) {
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("演員表演了:"+name);
/**
* 通知觀眾觀看
*/
this.notifyAll();
this.name = name;
this.flag = !this.flag;
}
/**
* 觀眾觀看
*/
public synchronized void watch() {
if(flag) {
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("觀看了:"+name);
/**
* 通知演員演戲
*/
this.notifyAll();
this.flag = !this.flag;
}
}
4、執行緒池
- 背景:經常建立和銷燬、使用量特別大的資源,比如併發情況下的執行緒,對效能影響很大。
- 思路:提前建立好多個執行緒,放入執行緒池中,使用時直接獲取,使用完返回池中。可以避免頻繁建立銷燬、實現重複利用。類似生活中的公共交通工具。
- 好處:
- 提高響應速度(減少了建立新執行緒的時間)
- 降低資源消耗(重複利用執行緒池中執行緒,不需要每次都建立)
- 便於執行緒管理
- corePoolSixe:核心池的大小
- maximumPoolSize:最大執行緒數
- keepAliveTime:執行緒沒有任務時最多保持多長時間後會終止
使用執行緒池
- JDK5.0起提供了執行緒池相關API:ExecutorService和Executors
- ExecutorService:真正的執行緒池介面。常見子類ThreadPoolExecutor
- void execute(Runnable command):執行任務/命令,沒有返回值,一般用來執行Runnable
- < T >Future< T > submit(Callable< T > task):執行任務,有返回值,一般用來執行Callable
- void shutdown():關閉執行緒池
- Executors:工具類、執行緒類的工廠類,用於建立並返回不同型別的執行緒池
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
/**
* 測試執行緒池
*/
public class TestPool {
public static void main(String[] args) {
/**
* 1.建立服務
* newFixedThreadPool 引數為:執行緒池大小
*/
ExecutorService service = Executors.newFixedThreadPool(10);
/**
* 執行
*/
service.execute(new MyThread());
service.execute(new MyThread());
service.execute(new MyThread());
service.execute(new MyThread());
/**
* 2.關閉服務
*/
service.shutdown();
}
}
class MyThread implements Runnable {
@Override
public void run() {
System.out.println(Thread.currentThread().getName());
}
}
相關文章
- 【java多執行緒】(二)執行緒停止Java執行緒
- Java多執行緒-執行緒中止Java執行緒
- java——多執行緒Java執行緒
- java多執行緒Java執行緒
- Java - 多執行緒Java執行緒
- java 多執行緒Java執行緒
- Java多執行緒之執行緒中止Java執行緒
- Java多執行緒-執行緒狀態Java執行緒
- Java多執行緒-執行緒通訊Java執行緒
- java 多執行緒守護執行緒Java執行緒
- Java多執行緒(2)執行緒鎖Java執行緒
- java多執行緒9:執行緒池Java執行緒
- Java多執行緒學習(一)Java多執行緒入門Java執行緒
- Java多執行緒(一)多執行緒入門篇Java執行緒
- Java多執行緒(二):Thread類Java執行緒thread
- Java多執行緒的使用(二)Java執行緒
- Java多執行緒之二(Synchronized)Java執行緒synchronized
- 【Java多執行緒】執行緒安全的集合Java執行緒
- 【Java】【多執行緒】執行緒池簡述Java執行緒
- Java多執行緒-執行緒池的使用Java執行緒
- java 多執行緒CountDownLatchJava執行緒CountDownLatch
- java 多執行緒-3Java執行緒
- java 多執行緒-2Java執行緒
- java 多執行緒 –同步Java執行緒
- java使用多執行緒Java執行緒
- Java--多執行緒Java執行緒
- java 多執行緒 --同步Java執行緒
- java多執行緒原理Java執行緒
- Java多執行緒學習——執行緒通訊Java執行緒
- Java多執行緒學習(2)執行緒控制Java執行緒
- Java 多執行緒基礎(四)執行緒安全Java執行緒
- java多執行緒之執行緒的基本使用Java執行緒
- 【Java】【多執行緒】執行緒的生命週期Java執行緒
- Java多執行緒詳解——一篇文章搞懂Java多執行緒Java執行緒
- Java多執行緒之守護執行緒實戰Java執行緒
- 深入淺出Java多執行緒(十二):執行緒池Java執行緒
- Java 多執行緒基礎(八)執行緒讓步Java執行緒
- java多執行緒:執行緒池原理、阻塞佇列Java執行緒佇列