線程創建
Thread、Runnable、Callable
繼承Thread類和 實現Runnable接口 為重點,實現Callable接口僅作了解
Thread
- 自定義線程類繼承Thread類
- 重寫run()方法,編寫線程執行體
- 創建線程對象,調用start()方法啟動線程
package com.company.dxc;
// 創建線程的方式:繼承Thread類 、重寫run()方法、 調用start開啟線程
// 總結:線程開啟不一定立即執行,由cpu進行調度執行
public class TestThread01 extends Thread {
@Override
public void run() {
// run方法線程體
for (int i = 0; i < 200; i++) {
System.out.println("正在執行線程----" + i);
}
}
public static void main(String[] args) {
// main主線程
// 創建一個線程對象
TestThread01 testThread01 = new TestThread01();
//調用start()方法開啟線程
testThread01.start();
for (int i = 0; i < 2000; i++) {
System.out.println("正在執行主方法******" + i);
}
}
}
Runnable
- 定義MyRunnable類實現Runnable接口
- 實現run()方法,編寫線程執行體
- 創建線程對象,調用start()方法啟動線程
package com.company.dxc;
// 創建線程方式2
// 實現Runnable接口,重寫run方法,執行線程需要丟入Runnable接口實現類 調用start執行
public class TestThread02 implements Runnable {
@Override
public void run() {
// run方法線程體
for (int i = 0; i < 200; i++) {
System.out.println("正在執行線程----" + i);
}
}
public static void main(String[] args) {
// main主線程
// 創建Runnable接口的實現類對象
TestThread01 testThread01 = new TestThread01();
// 創建線程對象,通過線程對象來開啟我們的線程,代理
// Thread tr = new Thread(testThread01);
// tr.start();
new Thread(testThread01).start();
for (int i = 0; i < 2000; i++) {
System.out.println("正在執行主方法******" + i);
}
}
}
Thread與Runnable比較
繼承Thread類 | 實現Runnable接口 |
---|---|
子類繼承Thread類具備多線程能力 | 實現接口Runnable具有多線程能力 |
啟動線程:子類對象.start(); | 啟動線程:傳入目標對象+Thread對象.start(); |
不建議使用:避免OOP單繼承局限性 | 推薦使用:避免單繼承局限性,靈活方便,方便同一個對象被多個線程使用 |
例子1. 購買火車票
通過本例子發現並發問題
package com.company.dxc;
// 多個線程同時操作同一個對象
// 買火車票的例子
// 問題:多個線程同時對一個資源進行操作 解決並發問題
public class TestThread03 implements Runnable {
// 票數
private int ticketNums = 10;
@Override
public void run() {
while (true){
if(ticketNums <= 0) break;
// 模擬演示
try {
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + " --> 拿到了NO:" + ticketNums-- + "票" );
}
}
public static void main(String[] args) {
TestThread03 ts = new TestThread03();
new Thread(ts, "stu").start();
new Thread(ts, "teacher").start();
new Thread(ts, "gangster").start();
}
}
例子2. 龜兔賽跑
package com.company.dxc;
// 模擬龜兔賽跑
public class race implements Runnable {
// 勝利者
private static String winner;
@Override
public void run() {
for (int i = 1; i <= 100; i++) {
// 模擬兔子休息
if (Thread.currentThread().getName().equals("rabbit") && i%10 == 0){
try {
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
// 判斷比賽是否結束
boolean flag = gameOver(i);
// 如果比賽結束 停止程序
if (flag) {break;}
System.out.println(Thread.currentThread().getName() + "-->跑了" + i + "steps");
}
}
//判斷是否完成比賽
private boolean gameOver(int steps) {
// 判斷是否有勝利者
if (winner != null) return true; // 已經存在勝利者
{
if (steps >= 100) {
winner = Thread.currentThread().getName();
System.out.println("winner is " + winner);
return true;
}
}
return false;
}
public static void main(String[] args) {
race r1 = new race();
new Thread(r1, "rabbit").start();
new Thread(r1, "tortoise").start();
}
}
Callable(僅了解)
- 實現Callable接口,需要返回值類型
- 重寫call方法,需要拋出異常
- 創建目標對象
- 創建執行服務
ExecutorService ser = Executors.newFixedThreadPood(1);
- 提交執行
Future<Boolean> result = ser.submit(t1);
- 獲取結果
boolean r1 = result.get();
- 關閉服務
ser.shutdownNow();
靜態代理
個人結婚與婚慶公司的例子
package com.company.dxc;
// 靜態代理模式:
// 真實對象和代理對象都要實現同一個接口
// 代理對象要代理真實角色
// 好處 :
// 1. 代理對象可以做很多真實對象做不了的事情
// 2. 真實對象專注做自己的事情
public class StaticProxy {
public static void main(String[] args) {
You you = new You(); // 真實對象
new Thread(() -> System.out.println("i love you")).start(); // lambda表達式
new WeddingCompany(new You()).HappyMarry();
// WeddingCompany weddingCompany = new WeddingCompany(you);
// weddingCompany.HappyMarry();
}
}
interface Marry{
void HappyMarry();
}
// 真實角色 去結婚
class You implements Marry{
@Override
public void HappyMarry() {
System.out.println("大喜之日結婚了!");
}
}
// 代理角色 起到幫助作用
class WeddingCompany implements Marry{
// 代理誰 --> 真實目標角色
private Marry target;
public WeddingCompany(Marry target){
this.target = target;
}
@Override
public void HappyMarry() {
before();
this.target.HappyMarry(); // 這是真實對象
after();
}
private void after() {
System.out.println("結婚之后,數錢");
}
private void before() {
System.out.println("結婚之前,布置場所");
}
}
Lambda表達式
為什么要使用lambda表達式
- 避免匿名內部類定義過多
- 可以讓代碼看起來更簡潔
- 去掉無意義代碼,留下核心邏輯
注:只有一行代碼的情況下才能簡化成一行;前提是接口為函數式接口
package com.company.dxc;
/*
* 推導lambda表達式
*/
public class TestLambda {
// 3. 靜態內部類
static class Like2 implements Ilike{
@Override
public void lambda() {
System.out.println("i like lambda2!");
}
}
public static void main(String[] args) {
Ilike like = new Like();
like.lambda();
like = new Like2();
like.lambda();
// 4. 局部內部類
class Like3 implements Ilike{
@Override
public void lambda() {
System.out.println("i like lambda3!");
}
}
like = new Like3();
like.lambda();
// 5. 匿名內部類,沒有類的名稱,必須借助接口或者父類
like = new Ilike() {
@Override
public void lambda() {
System.out.println("i like lambda4");
}
};
like.lambda();
// 6. 用lambda表達式
like = () -> {
System.out.println("i like lambda5");
};
like.lambda();
}
}
// 1. 定義一個函數式接口
interface Ilike{
void lambda();
}
// 2. 實現類
class Like implements Ilike{
@Override
public void lambda() {
System.out.println("i like lambda!");
}
}
線程狀態
創建狀態、阻塞狀態、死亡裝填、就緒狀態運行狀態
線程的方法
停止線程
- 不推薦使用JDK提供的
stop(); destroy();
方法 - 推薦線程自己停止
- 建議使用一個標志位進行終止變量
package com.company.dxc;
public class TestStop implements Runnable {
// 1. 設置標志位
private boolean flag = true;
@Override
public void run() {
int i = 0;
while(flag){
System.out.println("Thread is running...." + i++);
}
}
// 2. 設置一個公開的方法停止線程,轉換標志位
public void stop(){
this.flag = false;
}
public static void main(String[] args) {
TestStop testStop = new TestStop();
new Thread(testStop).start();
// main 先執行
for (int i = 0; i < 1000; i++) {
System.out.println("main is running..." + i);
if(i == 900){
//調用stop方法停止線程
testStop.stop();
System.out.println("Thread stop!");
}
}
}
}
線程休眠
- sleep(時間)指定當前線程阻塞的毫秒數
- sleep存在異常InterruptedException
- sleep時間到達后線程進入就緒狀態
- sleep可以模擬網絡延時,倒計時等
- 每一個對象都有一個鎖,sleep不會釋放鎖
package com.company.dxc;
import java.text.SimpleDateFormat;
import java.util.Date;
public class TestSleep {
public static void main(String[] args) throws InterruptedException {
// down();
// 打印當前系統時間
Date date = new Date(System.currentTimeMillis());
while (true){
try {
Thread.sleep(1000);
System.out.println(new SimpleDateFormat("HH:mm:ss").format(date));
date = new Date(System.currentTimeMillis()); // 更新當前時間
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public static void down() throws InterruptedException {
int num = 10;
while (true){
Thread.sleep(1000);
System.out.println(num --);
if(num <= 0) break;
}
}
}
線程禮讓
- 禮讓線程,讓當前正在執行的線程暫停,但不阻塞
- 將線程從運行狀態轉為就緒狀態
- 讓CPU重新調度,禮讓不一定成功,看CPU心情
package com.company.dxc;
// 測試不一定成功的線程禮讓
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合並線程,待此線程執行完成后再執行其他線程,其他線程阻塞
- 可以想象成插隊
package com.company.dxc;
public class TestJoin implements Runnable {
@Override
public void run() {
for (int i = 0; i < 500; i++) {
System.out.println("★ VIP Thread ☆" + i);
}
}
public static void main(String[] args) throws InterruptedException {
TestJoin testJoin = new TestJoin();
Thread thread = new Thread(testJoin);
thread.start();
// 主線程
for (int i = 0; i < 500; i++) {
if(i == 200){
thread.join(); // 插隊
}
System.out.println("main" + i);
}
}
}
線程優先級
- java提供一個線程調度器來監控程序中啟動后進入就緒狀態的所有線程,調度器按照優先級決定應該調度哪個線程來執行
- 線程的優先級用數字表示,范圍從1~10
Thread.MIN_PRIORITY = 1;
- 使用以下方式改變或獲取優先級
getPriority().setPriority(int xxx);
優先級低只是意味着獲得調度的概率低,並不是高優先級必然先調用 (性能倒置問題)
package com.company.dxc;
public class TestPriority extends Thread {
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);
Thread t5 = new Thread(myPriority);
// 先設置優先級 再啟動
t1.start();
t2.setPriority(1);
t2.start();
t3.setPriority(4);
t3.start();
t4.setPriority(Thread.MAX_PRIORITY); // Max 為最大 10
t4.start();
t5.setPriority(Thread.MIN_PRIORITY); // min 為最小 1
t5.start();
}
}
class myPriority implements Runnable{
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + "-->" + Thread.currentThread().getPriority());
}
}
守護線程(daemon)
- 線程分為用戶線程和守護線程
- 虛擬機必須確保用戶線程執行完畢
- 虛擬機不用等待守護線程執行完畢
- 如:后台記錄操作日志、監控內存、垃圾回收 etc.
package com.company.dxc;
// 測試守護線程 上帝守護人類為例
public class TestDaemon {
public static void main(String[] args) {
God god = new God();
Human human = new Human();
Thread thread = new Thread(god);
thread.setDaemon(true); // 默認false表示用戶線程,正常線程都是用戶線程
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("goodbye world!");
}
}
線程同步
並發:同一個對象被多個線程同時操作
處理多線程問題時,多線程訪問一個對象,並且某些線程還想修改這個對象,這時候就需要線程同步。線程同步是一種等待機制,多個需要同時訪問此對象的線程進入這個對象的等待池形成隊列,等待前面線程使用完畢再讓下一個線程使用
隊列與鎖
兩個不安全線程程序的例子
1. 不安全購票
package com.company.dxc;
import javax.swing.plaf.TableHeaderUI;
//不安全買票
// 線程不安全,有負數
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 ticketNum = 10;
boolean flag = true; // 外部停止方式
@Override
public void run() {
// 買票
while(true){
try {
buy();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
private void buy() throws InterruptedException {
// 判斷是否有票
if(ticketNum <= 0){
flag = false;
return;
}
//模擬延時
Thread.sleep(100);
// 買票
System.out.println(Thread.currentThread().getName() + "買到了" + ticketNum--);
}
}
2. 不安全取款
package com.company.dxc;
// 不安全取錢
// 兩個人去取錢
public class UnsafeBank {
public static void main(String[] args) {
// 賬戶
Account account = new Account(100, "存款金額");
Drawing you = new Drawing(account, 50, "你");
Drawing gf = new Drawing(account, 100, "對方");
you.start();
gf.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){
super(name);
this.account = account;
this.drawingMoney = drawingMoney;
}
//取錢操作
@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);
// 此時 Thread.currentThread().getName() = this.getName()
System.out.println(this.getName() + "手里的錢:" + nowMoney);
}
}
同步方法
-
由於我們可以通過private關鍵字來保證數據對象只能被方法訪問,所以我們只需要針對方法提出一套機制,這套機制就是 synchronized 關鍵字,它包括兩種用法:
synchronized
方法和synchronized
塊同步方法
public synchronized void method(int args){ }
-
synchronized方法控制對“對象”的訪問,每個對象對應一把鎖,每個synchronized方法都必須獲得調用該方法的對象的鎖才能執行,否則線程會阻塞,方法一旦執行,就獨占該鎖,直到該方法返回才釋放鎖,后面被阻塞的線程才能獲得這個鎖,繼續執行
package com.company.dxc;
import javax.swing.plaf.TableHeaderUI;
//不安全買票
// 線程不安全,有負數
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 ticketNum = 10;
boolean flag = true; // 外部停止方式
@Override
public void run() {
// 買票
while(true){
try {
buy();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
// *********此處進行同步方法************
// synchronized 同步方法,鎖的是this
private synchronized void buy() throws InterruptedException {
// 判斷是否有票
if(ticketNum <= 0){
flag = false;
return;
}
//模擬延時
Thread.sleep(100);
// 買票
System.out.println(Thread.currentThread().getName() + "買到了" + ticketNum--);
}
}
package com.company.dxc;
// 不安全取錢
// 兩個人去取錢
public class UnsafeBank {
public static void main(String[] args) {
// 賬戶
Account account = new Account(100, "存款金額");
Drawing you = new Drawing(account, 50, "你");
Drawing gf = new Drawing(account, 100, "對方");
you.start();
gf.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){
super(name);
this.account = account;
this.drawingMoney = drawingMoney;
}
//取錢操作
@Override
public void run() {
// ----------此時進行同步操作--------------
// synchronized 默認鎖的是this,所以此時用同步塊對account進行同步
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);
// 此時 Thread.currentThread().getName() = this.getName()
System.out.println(this.getName() + "手里的錢:" + nowMoney);
}
}
}
死鎖
產生死鎖的四個必要條件
- 互斥:一個資源每次只能被一個進程使用
- 請求與保持:一個進程因請求資源而阻塞時,對已獲得的資源保持不放
- 不剝奪:進程已獲得的資源,在未用完之前,不能強行剝奪
- 循環等待:若干進程之間形成一種頭尾相接的循環等待資源關系
package com.company.dxc;
// 死鎖:多個線程互相擁有對方需要的資源,形成僵持
public class DeadLock {
public static void main(String[] args) {
Makeup moore = new Makeup(0, "Moore");
Makeup dove = new Makeup(0, "Dove");
moore.start();
dove.start();
}
}
class Lipstick{
}
class Mirror{
}
class Makeup extends Thread{
// 需要的資源只有一份,用static來保證只有一份
static Lipstick lipstick = new Lipstick();
static Mirror mirror = new Mirror();
int choice; // 選擇
String girlName; // 使用化妝品的人
Makeup(int choice, String girlName){
this.choice = choice;
this.girlName = girlName;
}
@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.girlName + "獲得口紅的鎖");
Thread.sleep(1000);
}
synchronized (mirror){
// 獲得鏡子的鎖
System.out.println(this.girlName + "獲得鏡子的鎖");
}
}else{
synchronized (mirror){
// 獲得口紅的鎖
System.out.println(this.girlName + "獲得鏡子的鎖");
Thread.sleep(2000);
}
synchronized (lipstick){
// 獲得鏡子的鎖
System.out.println(this.girlName + "獲得口紅的鎖");
}
}
}
}
Lock 鎖
class A{
private final ReentrantLock lock = new ReenTrantLock();
public void m(){
lock.lock();
try{
// 保證線程安全的代碼
}
finally{
lock.unlock();
// 如果同步代碼有異常,要將unlock()寫入finally語句塊
}
}
}
synchronized 與 lock 的對比
- Lock是顯示鎖,需要手動開啟和關閉,synchronized為隱式鎖,出了作用域自動釋放
- lock只有代碼塊鎖,synchronized有代碼塊鎖和方法鎖
- 使用lock鎖,jvm將花費較少的時間來調度線程,性能更好。並且具有更好的擴展性
- 優先使用順序
- Lock > 同步代碼快(已經進入方法體,分配了相應資源)> 同步方法(在方法體之外)
線程協作 - 生產者消費者問題
線程同步問題,生產者和消費者共享同一個資源,並且生產者和消費者之間相互依賴,互為條件
- 對於生產者,沒有生產產品之前,要通知消費者等待,而生產了產品之后,有需要馬上通知消費者消費
- 對於消費者,在消費之后,要通知生產者已經結束消費,需要生產新的產品以供消費
- 在生產者消費者問題中,僅有synchronized是不夠的
- synchronized可組織並發更新同一個共享資源,實現了同步
- synchronized不能用來實現不同線程之間的消息傳遞(通信)
Java提供了幾個方法解決線程之間的通信問題
方法名 | 作用 |
---|---|
wait() | 表示線程一直鄧艾,直到其他線程通知,與sleep()不同,會釋放鎖 |
wait(long timeout) | 指定等待的毫秒數 |
notify() | 喚醒一個處於等待狀態的線程 |
notifyAll() | 喚醒同一個對象上所有調用wait()方法的線程,優先級別高的線程優先調度 |
注意: 均是Object類的方法,都只能在同步方法或者同步代碼快中使用,否則會拋出異常 IllegalMonitorStateException
管程法
- 生產者:負責生產數據的模塊(可能是方法、對象、線程、進程)
- 消費者:負責處理數據的模塊(可能是方法、對象、線程、進程)
- 緩沖區:消費者不能直接使用生產者的數據,利用中間“緩沖區”
package com.company.dxc;
// 測試 生產者消費者模型 --> 利用緩沖區解決:管程法
public class TestPC {
public static void main(String[] args) {
SynBuffer synBuffer = new SynBuffer();
new Producer(synBuffer).start();
new Consumer(synBuffer).start();
}
}
// 生產者
class Producer extends Thread{
SynBuffer buffer;
public Producer(SynBuffer buffer){
this.buffer = buffer;
}
// 生產
@Override
public void run() {
for (int i = 0; i < 100; i++) {
System.out.println("生產了" + i +"只雞");
try {
buffer.push(new Chicken(i));
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
// 消費者
class Consumer extends Thread{
SynBuffer buffer;
public Consumer(SynBuffer buffer){
this.buffer = buffer;
}
// 消費
@Override
public void run() {
for (int i = 0; i < 100; i++) {
try {
System.out.println("消費了-->" + buffer.pop().id +"只雞");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
// 產品
class Chicken{
int id; // 產品編號
public Chicken(int id) {
this.id = id;
}
}
// 緩沖區
class SynBuffer{
//容器大小
Chicken[] chickens = new Chicken[10];
// 容器計數器
int count = 0;
// 生產者放入產品
public synchronized void push(Chicken chicken) throws InterruptedException {
// 如果容器滿了,需要等待消費者消費
if(count == chickens.length){
// 通知消費者消費,生產等待
this.wait();
}
// 如果沒有滿,需要丟入產品
chickens[count] = chicken;
count ++;
// 可以通知消費者消費了
this.notifyAll();
}
// 消費者消費產品
public synchronized Chicken pop() throws InterruptedException {
// 判斷能否消費
if(count == 0){
/// 等待生產者生產,消費者等待
this.wait();
}
// 如果可以消費
count --;
Chicken chicken = chickens[count];
// 吃完了,通知生產者生產
this.notifyAll();
return chicken;
}
}
信號燈法
package com.company.dxc;
public class TestPC2 {
public static void main(String[] args) {
TV tv = new TV();
new Player(tv).start();
new Watcher(tv).start();
}
}
// 生產者 --> 演員
class Player extends Thread{
TV tv;
public Player(TV tv){
this.tv = tv;
}
@Override
public void run() {
for (int i = 0; i < 20; i++) {
if (i % 2 == 0){
this.tv.play("節目一:新聞聯播");
}else{
this.tv.play("節目二:法治在線");
}
}
}
}
// 消費者 --> 觀眾
class Watcher extends Thread{
TV tv;
public Watcher(TV tv){
this.tv = tv;
}
@Override
public void run() {
for (int i = 0; i < 20; i++) {
tv.watch();
}
}
}
// 產品 --> 節目
class TV{
// 演員表演,觀眾等待 T
// 觀眾觀看,演員等待 F
String voice; // 表演的節目
boolean flag = true;
// 表演
public synchronized void play(String voice){
if (!flag){
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("演員表演了:" + voice);
// 通知觀眾觀看
this.notifyAll(); // 通知喚醒
this.voice = voice;
this.flag = !this.flag;
}
// 觀看
public synchronized void watch(){
if (flag){
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("觀看了:" + voice);
// 通知演員表演
this.notifyAll();
this.flag = !this.flag;
}
}
線程池
使用線程池
package com.company.dxc;
import java.util.concurrent.Executor;
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());
service.execute(new MyThread());
// 2. 關閉連接
service.shutdown();
}
}
class MyThread implements Runnable{
@Override
public void run() {
System.out.println(Thread.currentThread().getName());
}
}
總結
package com.company.dxc;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;
public class summary {
public static void main(String[] args) throws ExecutionException, InterruptedException {
new MyThread1().start();
new Thread(new MyThread2()).start();
FutureTask<Integer> futureTask = new FutureTask<Integer>(new MyThread3());
new Thread(futureTask).start();
Integer integer = futureTask.get();
System.out.println(integer);
}
}
// 1. 繼承Thread類
class MyThread1 extends Thread{
@Override
public void run() {
System.out.println("My Thread1");
}
}
// 2. 實現Runnable接口
class MyThread2 implements Runnable{
@Override
public void run() {
System.out.println("My Thread2");
}
}
// 3. 實現Callable接口
class MyThread3 implements Callable<Integer>{
@Override
public Integer call() throws Exception {
System.out.println("My Thread3");
return 100;
}
}