多執行緒
任務(Task)、行程(Process)、執行緒(Thread)
行程的創建
1.繼承Thread類
-
自定義執行緒類繼承
Thread類 -
重寫
run()方法,撰寫執行緒執行體 -
創建執行緒物件,呼叫
start()方法啟動執行緒
public class DemoThread1 extends Thread {
@Override
public void run() {
//run方法執行緒提
for (int i = 0; i < 1000; i++) {
System.out.println("子執行緒第"+i+"個");
}
}
//main執行緒,主執行緒
public static void main(String[] args) {
//創建一個執行緒物件
DemoThread1 demoThread1 = new DemoThread1();
//呼叫start()方法
demoThread1.start();
for (int i = 0; i < 1000; i++) {
System.out.println("主執行緒第-"+i+"-個");
}
}
}
//多執行緒下載圖片
public class DemoThread2 extends Thread {
private String url;
private String name;
public DemoThread2(String url, String name) {
this.url = url;
this.name = name;
}
@Override
public void run() {
//run方法執行緒提
WebDownloader webDownloader = new WebDownloader();
webDownloader.downloader(url, name);
System.out.println("下載了檔案:" + name);
}
//main執行緒,主執行緒
public static void main(String[] args) {
//創建一個執行緒物件
DemoThread2 demoThread1 = new DemoThread2("https://commons.apache.org/proper/commons-io/images/commons-logo.png", "1.png");
DemoThread2 demoThread2 = new DemoThread2("https://commons.apache.org/proper/commons-io/images/commons-logo.png", "2.png");
DemoThread2 demoThread3 = new DemoThread2("https://commons.apache.org/proper/commons-io/images/commons-logo.png", "3.png");
//呼叫start()方法
demoThread1.start();
demoThread2.start();
demoThread3.start();
}
}
class WebDownloader {
public void downloader(String url, String name) {
try {
FileUtils.copyURLToFile(new URL(url), new File(name));
} catch (IOException e) {
e.printStackTrace();
System.out.println("IO例外");
}
}
}
不建議使用,避免OPP單繼承的局限性
2.實作Runnable介面(推薦)
- 自定義類實作Runnable介面
- 實作run()方法,撰寫執行緒執行體
- 創建執行緒物件,呼叫start()方法啟動執行緒
public class DemoRunneralbe implements Runnable {
@Override
public void run() {
//run方法執行緒提
for (int i = 0; i < 1000; i++) {
System.out.println("子執行緒第"+i+"個");
}
}
//main執行緒,主執行緒
public static void main(String[] args) {
//創建一個執行緒物件
DemoRunneralbe demoRunneralbe = new DemoRunneralbe();
//創建執行緒物件,代理
new Thread(demoRunneralbe).start();
for (int i = 0; i < 1000; i++) {
System.out.println("主執行緒第-"+i+"-個");
}
}
}
3.實作Callable介面
public class DemoCallable implements Callable<Boolean> {
private String url;
private String name;
public DemoCallable(String url, String name) {
this.url = url;
this.name = name;
}
@Override
public Boolean call() throws Exception {
//call方法執行緒提
WebDownloader webDownloader = new WebDownloader();
webDownloader.downloader(url, name);
System.out.println("下載了檔案:" + name);
return true;
}
//main執行緒,主執行緒
public static void main(String[] args) throws ExecutionException, InterruptedException {
//創建一個執行緒物件
DemoCallable demoThread1 = new DemoCallable("https://commons.apache.org/proper/commons-io/images/commons-logo.png", "1.png");
DemoCallable demoThread2 = new DemoCallable("https://commons.apache.org/proper/commons-io/images/commons-logo.png", "2.png");
DemoCallable demoThread3 = new DemoCallable("https://commons.apache.org/proper/commons-io/images/commons-logo.png", "3.png");
//創建執行服務創建池子,里面有3個執行緒
ExecutorService ser = Executors.newFixedThreadPool(3);
//提交執行
Future<Boolean> submit = ser.submit(demoThread1);
Future<Boolean> submit1 = ser.submit(demoThread2);
Future<Boolean> submit2 = ser.submit(demoThread3);
//獲取結果
Boolean aBoolean1 = submit.get();
Boolean aBoolean2 = submit1.get();
Boolean aBoolean3 = submit2.get();
//關閉服務
ser.shutdown();
}
}
callable可以定義回傳值,可以定義例外
4、靜態代理
真實物件和代理物件都要實作同一個介面
代理物件要代理真實角色
好處:
? 代理物件可以做很多真實物件做不了的事情
? 真實物件只要關注自己的事情
public class DemoStacticProxy {
public static void main(String[] args) {
WeddingCompany weddingCompany = new WeddingCompany(new You());
weddingCompany.HappyMarry();
//同new WeddingCompany(new You()).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 before() {
System.out.println("結婚之前");
}
private void after() {
System.out.println("結婚之后");
}
}
5、Lamda運算式
避免匿名內部類定義過多
其實質屬于函式式編程
(params)->expression[運算式]
(params)->statement[陳述句]
(params)->{statement}
-
函式式介面
-
任何介面,如果只包含唯一一個抽象方法,那么他就是一個函式式介面,
public interface Runnable{ public abstract void run(); } -
對于函式式介面,我們可以使用lamda運算式來創建該介面的物件
-
Lamda推導
public class DemoLamda {
//3靜態內部類
static class Like2 implements ILike {
@Override
public void lamda() {
System.out.println("i like lamda2-靜態內部類");
}
}
public static void main(String[] args) {
ILike like = new Like1();
like.lamda();
Like2 like2 = new Like2();
like2.lamda();
//4區域內部類
class Like3 implements ILike {
@Override
public void lamda() {
System.out.println("i like lamda3-區域內部類");
}
}
Like3 like3 = new Like3();
like3.lamda();
//5匿名內部類,沒有類的名稱,必須將借助介面或者父類
ILike like4 = new ILike() {
@Override
public void lamda() {
System.out.println("i like lamda4-匿名內部類");
}
};
like4.lamda();
//6用lamda簡化
ILike like5 = () -> {
System.out.println("i like lamda5-lamda簡化");
};
like5.lamda();
}
}
//1定義一個函式式介面
interface ILike {
void lamda();
}
//2實作類
class Like1 implements ILike {
@Override
public void lamda() {
System.out.println("i like lamda1-實作類");
}
}
Lamda運算式簡化
public class DemoLamda2 {
public static void main(String[] args) {
Ilove love = null;
love = (int a) -> {
System.out.println("lovelovelovelove" + a);
};
//簡化1,去掉引數型別
love = (a) -> {
System.out.println("lovelovelovelove" + a);
};
//簡化2,去掉括號,引數個數只能有一個
love = a -> {
System.out.println("lovelovelovelove" + a);
};
//簡化2,去掉花括號,只能有一行代碼才能簡化
love = a -> System.out.println("lovelovelovelove" + a);
love.love(1231);
}
}
interface Ilove {
void love(int a);
}
6、執行緒的狀態
創建狀態,就緒狀態,運行狀態,阻塞狀態,死亡狀態
| 方法 | 說明 |
|---|---|
| setPriority(int newPriority) | 更改執行緒的優先級 |
| static void sleep(long milis) | 在指定的毫秒數內讓當前正在執行的執行緒休眠 |
| void jion() | 等待該執行緒終止 |
| static void yeild() | 暫停當前正在執行的執行緒物件,并執行其他執行緒 |
| void interrupt() | 中斷執行緒(不推薦,建議使用標志位來停止執行緒) |
| boolean isAlive() | 測驗執行緒是否處于活動狀態 |
執行緒的停止
public class DemoStop implements Runnable {
//標志位
private boolean flag = true;
@Override
public void run() {
int i = 0;
while (flag) {
System.out.println("run---Thread" + i++);
}
}
//設定方法轉換標志位
public void stop() {
this.flag = false;
}
public static void main(String[] args) {
DemoStop demoStop = new DemoStop();
new Thread(demoStop).start();
for (int i = 0; i < 1000; i++) {
System.out.println("main" + i);
if (i == 900) {
demoStop.stop();
System.out.println("執行緒停止");
}
}
}
}
執行緒的休眠
sleep(時間)指定當前執行緒祖代的毫秒數- sleep存在例外IntrruptedException
- sleep時間達到后執行緒進入就緒狀態
- sleep可以模擬網路延時(放大問題的發生性),倒計時等
- 每一個物件都有一個鎖,sleep不會釋放鎖
執行緒禮讓
- 禮讓執行緒,讓當前正在執行的執行緒暫停,但不阻塞
- 將執行緒從運行狀態轉為就緒狀態
- 禮讓不一定成功
public class DemoYield {
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合并執行緒,要等此執行緒執行完之后,在執行其他執行緒,其他執行緒處于阻塞狀態
public class DemoJoin {
public static void main(String[] args) throws InterruptedException {
MyJoin myJoin = new MyJoin();
Thread thread = new Thread(myJoin);
thread.start();
for (int i = 0; i < 1000; i++) {
if (i==300) {
//執行緒插隊
thread.join();
}
System.out.println("main--"+i);
}
}
}
class MyJoin implements Runnable{
@Override
public void run() {
for (int i = 0; i < 1000; i++) {
System.out.println("MyJoin--"+i);
}
}
}
7、執行緒守護(setDaemon)
- 執行緒分為用戶執行緒和守護執行緒
- 虛擬機必須確保用戶執行緒執行完畢
- 虛擬機不用等待守護執行緒執行完畢
- 如后臺記錄操作日志,監控記憶體,垃圾回收等
8、執行緒同步
多個執行緒操作同一個資源 ,執行緒同步其實就是一種等待機制,多個執行緒訪問統一資源,就要進入一個物件的等待池,形成佇列,等待前面的執行緒使用完畢,下一個執行緒再去使用
佇列和鎖解決安全性,
在訪問資源的時候,加入鎖機制(synchronized),當一個執行緒獲得資源的排它鎖,獨占資源,其他的執行緒就必須等待,使用完成之后釋放鎖即可,
同步方法
同步方法的同步監視是this,就是則個物件本身,或者是class,鎖物件應是變化的量(增刪改)
- synchronized方法(影響效率)
- synchronized塊
- synchronized(Obj){}
public class UnsafeBuyTicket {
public static void main(String[] args) {
BuyTicket buyTicket = new BuyTicket();
new Thread(buyTicket, "瑪麗").start();
new Thread(buyTicket, "小二").start();
new Thread(buyTicket, "小民").start();
}
}
class BuyTicket implements Runnable {
private int ticketnum = 10;
@Override
public void run() {
synchronized ((Integer) ticketnum) {
while (true) {
if (ticketnum <= 0) {
break;
}
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "買到了第" + ticketnum-- + "票");
}
}
}
}
擴充(CopyOnWriteArrayList)
CopyOnWriteArrayList是的List變成一個執行緒安全的list
public class DemoJUC {
public static void main(String[] args) {
CopyOnWriteArrayList<String> list = new
CopyOnWriteArrayList<String>();
for (int i = 0; i < 1000; i++) {
new Thread(()->{
list.add(Thread.currentThread().getName());
}).start();
}
try {
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(list.size());
}
}
9、死鎖
某一個同步塊同時擁有兩個以上物件的鎖,就可能產生死鎖,
Lock(鎖)
JDK5開始,JAVA提供了更強大的執行緒同步機智---通過顯示的定義同步鎖物件來實作同步,同步鎖使用Lock物件充當
java.util.concurrent.locks.Lock介面是控制多個執行緒物件共享資源進行訪問的工具,鎖提供了物件共享資源的獨占空間,每一次只能有一個執行緒對Lock物件加鎖,執行緒開始訪問共享資源之前就要先獲得Lock物件
ReentrantLock類實作了Lock,他擁有與synchronized相同的并發性和記憶體語意,在實作執行緒安全的控制中,比較常用的是ReentrantLock,可以顯示加鎖、釋放鎖,
public class DemoLock {
public static void main(String[] args) {
BuyTicket buyTicket = new BuyTicket();
new Thread(buyTicket, "瑪麗").start();
new Thread(buyTicket, "小二").start();
new Thread(buyTicket, "小民").start();
}
}
class BuyTicket1 implements Runnable {
private int ticketnum = 10;
//d定義lock鎖
private final ReentrantLock lock = new ReentrantLock();
@Override
public void run() {
while (true) {
try {
lock.lock();
if (ticketnum <= 0) {
break;
}
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "買到了第" + ticketnum-- + "票");
} finally {
lock.unlock();
}
}
}
}
10、執行緒通信
Java提供了幾個方法解決執行緒之間的通信問題,
| 方法名 | 作用 |
|---|---|
| wait() | 表示執行緒一致等待,直到其他執行緒通知,與sleep不同,會釋放鎖 |
| wait(long timeout) | 指定等待的毫秒數 |
| notify() | 喚醒一個處于等待的執行緒 |
| notifyAll() | 喚醒同一個物件上所有呼叫wait()方法的執行緒,優先級別高的執行緒優先調度 |
均是Object類的方法,都只能在同步方法或者同步代碼塊中使用,否則會拋出例外IIIelgalMonitorStateException
生產者/消費者模式
管程法
//生產者、消費者、產品、緩沖區
public class DemoPC {
public static void main(String[] args) {
SynContainer synContainer = new SynContainer();
new Productor(synContainer).start();
new Consumer(synContainer).start();
}
}
//生產者
class Productor extends Thread {
SynContainer container;
public Productor(SynContainer container) {
this.container = container;
}
//生產
@Override
public void run() {
for (int i = 0; i < 100; i++) {
container.push(new Pencel(i));
System.out.println("生產了" + 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("消費了第--" + container.pop().id+ "只筆");
}
}
}
//產品
class Pencel {
int id;//產品編號
public Pencel(int id) {
this.id = id;
}
}
//緩沖區
class SynContainer {
Pencel[] pencels = new Pencel[10];
int count = 0;
//生產者放入產品
public synchronized void push(Pencel pencel) {
//如果容器滿了,就需要等待消費者消費
if (count == pencels.length) {
//通知消費者,生產者等待
try {
this.wait();//System.out.println("-----------等待消費者------------");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//如果沒有滿,就丟入產品
pencels[count] = pencel;
count++;
//通知消費者消費
this.notifyAll();
}
//消費者消費產產品
public synchronized Pencel pop() {
//判斷能否消費
if (count == 0) {
//等待生產者
try {
this.wait();
//System.out.println("-----------等待生產者------------");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
count--;
Pencel pencel = pencels[count];
//通知生產者生產
this.notifyAll();
return pencel;
}
}
信號燈法
//生產者、消費者、產品、緩沖區
public class DemoPC2 {
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++) {
this.tv.whatch();
}
}
}
//產品-->節目
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 whatch() {
if (flag) {
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("觀看了:" + voice);
//通知演員表演
this.notifyAll();
this.flag = !this.flag;
}
}
11、執行緒池
提前創好多個執行緒,放入執行緒池中,使用時直接獲取,使用完放回池子中,可以避免頻繁的創建銷毀物件、實作了重復利用
- 提高了形影速度(減少了創建新縣城的時間)
- 降低資源消耗(重復利用執行緒池中的執行緒,不需要每次都創建)
- 便于執行緒管理
- corePollSize:核心池大小
- maximunPoolSize:最大執行緒數
- keepAliveTime:執行緒沒有任務時最多保持多長時間后會終止
- 執行緒池相關的API:ExecutorService和Executors
- ExecutorService:真正的執行緒池介面,常見子類ThreadPoolExecutor
- void execute(Runnable command)執行任務/命令,沒有回傳值,一般用來執行Runnable
Future submit(Callable task)執行任務,有回傳值,一般用來執行Callable - void shotdown()關閉連接池
- Executors:工具類、執行緒池的工廠類,用于創建并回傳不同型別的執行緒池
public class DemoPool {
public static void main(String[] args) {
//創建執行緒池
//newFixedThreadPool 引數為執行緒池大小
ExecutorService service = Executors.newFixedThreadPool(10);
service.execute(new MyThread());
service.execute(new MyThread());
service.execute(new MyThread());
service.execute(new MyThread());
//關閉
service.shutdown();
}
}
class MyThread implements Runnable{
@Override
public void run() {
System.out.println(Thread.currentThread().getName());
}
}
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/250443.html
標籤:Java
上一篇:Go基礎及語法(二)
