java 多執行緒 總結 案例
學完東西后,要學會總結,學會記錄筆記,這樣才會有更大的收穫
首先我們瞭解執行緒和程序的基本概念
一、概念(程式 程序 執行緒)
1、程式:指令集 靜態概念
2、程序:作業系統 排程程式 動態概念
3:執行緒:在程序內多條執行路徑 真正的多執行緒是指多個cpu
二、建立
1.1 繼承Thread +run()
啟動:建立類物件+物件.start()
package com.org.pc; /** * 模擬龜兔賽跑 * @author lyy * 建立多執行緒 繼承Thread 重寫run(執行緒體) * 使用執行緒建立子類物件,呼叫物件.start() */ public class Rabbit extends Thread{ @Override public void run() { for (int i = 0; i <100; i++) { System.out.println("兔子跑了"+ i + "步"); } } public static void main(String[] args) { Thread t1 = new Rabbit(); Thread t2 = new Tortoise(); t1.start(); t2.start(); } } class Tortoise extends Thread{ @Override public void run() { for (int i = 0; i <100; i++) { System.out.println("烏龜跑了"+ i + "步"); } } }
1.2 實現Runable+run()
啟動:使用靜態代理
1、建立真實角色
2、建立代理角色 Thread+引用
3、代理角色.start()
/** * 使用Runable 建立 執行緒 * 1、類實現Runable介面 +重寫run() 真實角色類 * 2、啟動多執行緒 使用靜態代理 * 1)建立真實角色 * 2)建立代理角色+真實角色引用 * 3)呼叫.start() * @author lyy * */ public class Programer implements Runnable{ @Override public void run() { for (int i = 0; i < 1000; i++) { System.out.println("一邊敲程式碼。。。。。"); } } public static void main(String[] args) { // 1)建立真實角色 Programer pro = new Programer(); // 2)建立代理角色+真實角色引用 Thread proxy = new Thread(pro); // 3)呼叫.start() proxy.start(); for (int i = 0; i < 1000; i++) { System.out.println("一邊聊QQ。。。。。"); } } }
1.3 實現Callable(瞭解)
通過Callable介面實現多執行緒
優點:可以獲取返回值
三:執行緒執行示意圖package com.org.pc; import java.util.concurrent.Callable; import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutorCompletionService; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future; /** * 使用Callable建立執行緒 * @author lyy * */ public class Call { public static void main(String[] args) throws InterruptedException, ExecutionException { //建立執行緒 ExecutorService ser = Executors.newFixedThreadPool(2); Race tor = new Race("千年王八",1000); Race rabbit = new Race("小兔子",500); //獲取值 Future<Integer> result1 = ser.submit(tor); Future<Integer> result2 = ser.submit(rabbit); Thread.sleep(2000);//休眠2秒 tor.setFlag(false);//停止執行緒體中的迴圈 rabbit.setFlag(false);//停止執行緒體中的迴圈 int num1 = result1.get(); int num2 = result2.get(); System.out.println("大烏龜跑了----->"+num1+"步"); System.out.println("小兔子跑了----->"+num2+"步"); //停止服務 ser.shutdown(); } } class Race implements Callable<Integer>{ private String name;//名稱 private long time;//延時時間 private boolean flag = true; private int step = 0;//步數 public Race(){ } public Race(String name) { super(); this.name = name; } public Race(String name, int time) { super(); this.name = name; this.time = time; } @Override public Integer call() throws Exception { while(flag){ Thread.sleep(time);//延時 step++; } return step; } public String getName() { return name; } public void setName(String name) { this.name = name; } public long getTime() { return time; } public void setTime(long time) { this.time = time; } public boolean isFlag() { return flag; } public void setFlag(boolean flag) { this.flag = flag; } public int getStep() { return step; } public void setStep(int step) { this.step = step; } }
新生-->就緒-->執行-->阻塞-->終止
四:執行緒的終止(重點)
1、自然終止:執行緒體正常執行完畢
2、外部干涉
1)執行緒類中 定義 執行緒體使用的標識
2)執行緒提供使用該標識
3) 對外提供方法改變該標識
4) 外部根據條件呼叫該方法
package com.org.status;
public class StopDemo1 {
public static void main(String[] args) {
Study stu = new Study();
new Thread(stu).start();
//外部改變該標識
for (int i = 0; i < 100; i++) {
if(i==50){//外部干涉
stu.stop();
}
System.out.println("main......"+i);
}
}
}
class Study implements Runnable{
//1)執行緒類中 定義 執行緒體使用的標識
private boolean flag =true;
@Override
public void run() {
//2)執行緒提使用該標識
while(flag){
System.out.println("Study thread .....");
}
}
//3)、對外提供方法改變該標識
public void stop(){
this.flag = false;
}
}
五:阻塞
IsAlive() 判斷執行緒是否還活著,既執行緒是還未終止
getPriority()獲得執行緒的優先順序數值
setPriority() 設定執行緒的優先順序數值
setName()給執行緒一個名字
getName()取得執行緒的名字
currentThread()取得當前正在執行的執行緒物件也就是取得自己本身
阻塞:join yield sleep(重點)
1、Join:合併執行緒
package com.org.status;
/**
* join:合併執行緒
* @author lyy
*
*/
public class JoinDemo01 extends Thread {
public static void main(String[] args) throws InterruptedException {
JoinDemo01 join = new JoinDemo01();
Thread t = new Thread(join);//新增
t.start();//就緒
//cpu排程執行
for (int i = 0; i < 100; i++) {
if(50 == i){
t.join();//main阻塞
}
System.out.println("main......"+i);
}
}
@Override
public void run() {
for (int i = 0; i < 100; i++) {
System.out.println("join......"+i);
}
}
}
2、Yield:暫停自己的執行緒
package com.org.status;
import javax.sound.midi.Synthesizer;
public class YieldDemo01 extends Thread{
public static void main(String[] args) {
YieldDemo01 yield = new YieldDemo01();
Thread t = new Thread(yield);//新生
t.start();//就緒
//cpu排程執行
for (int i = 0; i < 100; i++) {
if(i%20 == 0){
//暫停本執行緒main
Thread.yield();
}
System.out.println("main......."+i);
}
}
@Override
public void run() {
for (int i = 0; i < 100; i++) {
System.out.println("yield......."+i);
}
}
}
3、Sleep():休眠,不釋放鎖
1)、時間相關(倒計時)
package com.org.status;
import java.text.SimpleDateFormat;
import java.util.Date;
/**
* 倒計時
* 1、倒數10個數,一秒內列印一個
* 2、倒計時
* @author lyy
*
*/
public class SleepDemo01 {
public static void main(String[] args) throws InterruptedException {
Date endTime = new Date(System.currentTimeMillis() + 10*1000);
long end = endTime.getTime();
while(true){
//輸出
System.out.println(new SimpleDateFormat("mm:ss").format(endTime));
//構建下一秒的時間
endTime = new Date(endTime.getTime() - 1000);
//等待一秒時間
Thread.sleep(1000);
//如果在十秒以內繼續否則退出
if(end -10000 > endTime.getTime()){
System.out.println("ending");
break;
}
}
}
public static void test1() throws InterruptedException{
int num = 10;
while(true){
System.out.println(num--);
Thread.sleep(1000);//暫停
if(num <= 0){
break;
}
}
}
}
2)、模擬網路延時
package com.org.status;
/**
* Sleep模擬 網路延時 執行緒不安全的
* @author lyy
*
*/
public class SLeepDemo02 {
public static void main(String[] args) {
//真實角色
Web12306 web = new Web12306();
//代理角色
Thread t1 = new Thread(web,"工程師");
Thread t2 = new Thread(web,"黃牛已");
Thread t3 = new Thread(web,"路人甲");
//啟動執行緒
t1.start();
t2.start();
t3.start();
}
}
class Web12306 implements Runnable{
private int num = 80;
@Override
public void run() {
while(true){
if(num <= 0){
break;//跳出迴圈
}
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"搶到了"+num--);
}
}
}
同步:併發多個執行緒訪問同一份資源確保資源安全 ==> 執行緒安全
Synchronized --> 同步
一、同步塊
Synchronized(引用型別|this|類.class){
}
package com.org.syn;
public class SynDemo01 {
public static void main(String[] args) {
//真實角色
Web123 web = new Web123();
//代理角色
Thread t1 = new Thread(web,"工程師");
Thread t2 = new Thread(web,"黃牛已");
Thread t3 = new Thread(web,"路人甲");
//啟動執行緒
t1.start();
t2.start();
t3.start();
}
}
class Web123 implements Runnable{
private int num = 10;
private boolean flag = true;
@Override
public void run() {
while(flag){
test3();
}
}
//執行緒不安全 鎖定資源不正確
public void test6(){
//a b c
if(num <= 0){
flag =false;//跳出迴圈
return;
}
synchronized (this) {
try {
Thread.sleep(500);//模擬 延時
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"搶到了"+num--);
}
}
//執行緒不安全 鎖定資源不正確
public void test5(){
//a b c
synchronized ((Integer)num) {
if(num <= 0){
flag =false;//跳出迴圈
return;
}
try {
Thread.sleep(500);//模擬 延時
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"搶到了"+num--);
}
}
//鎖定範圍不正確
public void test4(){
//a b c
synchronized (this) {
if(num <= 0){
flag =false;//跳出迴圈
return;
}
try {
Thread.sleep(500);//模擬 延時
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"搶到了"+num--);
}
}
//執行緒安全,鎖定正確
public void test3(){
//a b c
synchronized (this) {
if(num <= 0){
flag =false;//跳出迴圈
return;
}
try {
Thread.sleep(500);//模擬 延時
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"搶到了"+num--);
}
}
public synchronized void test2(){
if(num <= 0){
flag =false;//跳出迴圈
return;
}
try {
Thread.sleep(500);//模擬 延時
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"搶到了"+num--);
}
//執行緒不安全
public void test1(){
if(num <= 0){
flag =false;//跳出迴圈
return;
}
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"搶到了"+num--);
}
}
二、同步方法
Synchronized
執行緒安全的效率慢,保證資源的正確
執行緒不安全的效率快
三、死鎖:過多的同步容易造成死鎖
package com.org.syn;
/**
* 過多的方法可能造成死鎖
* @author lyy
*
*/
public class SncDemo3 {
public static void main(String[] args) {
Object g = new Object();
Object m = new Object();
Test t1 = new Test(g,m);
Test t2 = new Test(g,m);
Thread proxy1 = new Thread(t1);
Thread proxy2 = new Thread(t2);
proxy1.start();
proxy2.start();
}
}
class Test implements Runnable{
Object goods;
Object money;
public Test(Object goods, Object money) {
this.goods = goods;
this.money = money;
}
@Override
public void run() {
while(true){
test();
}
}
public void test(){
synchronized (goods) {
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
synchronized (money) {
// System.out.println("一手給貨");
}
}
}
class test2 implements Runnable{
Object goods;
Object money;
public test2(Object goods, Object money) {
this.goods = goods;
this.money = money;
}
@Override
public void run() {
while(true){
test();
}
}
public void test(){
synchronized (money) {
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
synchronized (goods) {
System.out.println("一手給貨");
}
}
}
解決方法:生產者消費者模式
先生產,在消費
package com.org.pro;
/**
* 一個場景,一個共同的資源
* 生產者和消費者模式訊號燈法
* wait() 等待 釋放鎖 sleep 不釋放鎖
* notify()/notifyAll() 喚醒
* 與 synchronized
* @author lyy
*
*/
public class Movie {
private String pic;
//訊號燈
//flag --> T 生產者生產 消費者消費 生產完成後 通知消費
//flag --> f 消費者消費 生產者等待 消費完成後喚醒生產
private boolean flag = true;
public synchronized void play(String pic){
if(!flag){//生產者等待
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//開始生產
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("生產了:"+pic);
//生產完畢
this.pic = pic;
//通知消費
this.notify();
//生產者停下
this.flag=false;
}
public synchronized void watch(){
if(flag){//消費者等待
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
try {
//開始消費
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("消費了:"+pic);
//消費完畢
//通知生產
this.notify();
//消費停止
this.flag = true;
}
}
}
package com.org.pro;
/**
* 生產者
* @author lyy
*/
public class Player implements Runnable{
private Movie m;
public Player(Movie m) {
super();
this.m = m;
}
@Override
public void run() {
for (int i = 0; i < 100; i++) {
if(0 == i%2){
m.play("左青龍");
}else{
m.play("右白虎");
}
}
}
}
package com.org.pro;
/**
* 消費者
* @author lyy
*
*/
public class Watcher implements Runnable{
private Movie m;
public Watcher(Movie m) {
super();
this.m = m;
}
@Override
public void run() {
for (int i = 0; i < 100; i++) {
m.watch();
}
}
}
package com.org.pro;
public class App {
public static void main(String[] args) {
//共同的資源
Movie m = new Movie();
//多執行緒
Player p = new Player(m);
Watcher w = new Watcher(m);
new Thread(p).start();
new Thread(w).start();
}
}
新生--> start -->就緒-->執行-->阻塞-->終止
執行緒是一個好用但是也有點複雜的技術,博主會的也只是一點點皮毛,希望有機會能和各位多多學習!有什麼不對的地方,請大家多多包涵!
想要更加熟練的運用執行緒還需要多多深入瞭解!