1.程序:指指令和數據的有序集合,其自己沒有任何意義,是一個靜態的概念
2.進程:指執行程序的一次執行過程,是一個動態的概念。是系統資源分配的單位(注意:不少多線程是模擬出來的,真正的多線程是指有多個cpu,即多核,如服務器。即在一個cpu的狀況下,在同一時間點,cpu只能執行一個代碼,由於切換的很快,因此就有同時執行的錯覺)
3.線程:一般一個進程中能夠包含若干個線程,一個進程中至少有一個線程。線程是cpu調度和執行的單位
4.並行:指在同一時刻,有多條指令在多個處理器上同時執行。因此不管從微觀仍是從宏觀來看,兩者都是一塊兒執行的。
5.併發:指在同一時刻只能有一條指令執行,但多個進程指令被快速的輪換執行,使得在宏觀上具備多個進程同時執行的效果,但在微觀上並非同時執行的,只是把時間分紅若干段,使多個進程快速交替的執行。java
核心概念:web
三種建立方式:express
//建立方式一:繼承Thread類,重寫run方法,調用start開啓線程 public class TestThread1 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線程,主線程 //建立一個線程對象 TestThread1 testThread1 = new TestThread1(); //調用start方法,開啓線程 testThread1.start(); for (int i = 0; i < 200; i++) { System.out.println("我在打飛機遊戲-------"+i); } } }
最後會發現「我在打遊戲」和「我在打飛機遊戲」會交替執行。
注意:線程不必定當即執行,由cpu安排調度;編程
推薦使用Runnable,避免單繼承的侷限性,方便同一個對象被多個線程使用安全
//票數 //多個線程同時操做同一個對象 //買火車票 //發現問題,多個線程操做同一個資源的狀況下,線程不安全,數據紊亂 public class TestThread04 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()+"拿到了第"+ticketNums--+"票"); } } public static void main(String[] args) { TestThread04 ticket = new TestThread04(); new Thread(ticket,"小明").start(); new Thread(ticket,"老師").start(); new Thread(ticket,"黃牛").start(); } }
老師拿到了第10票 小明拿到了第9票 黃牛拿到了第9票 老師拿到了第8票 小明拿到了第6票 黃牛拿到了第7票 黃牛拿到了第5票 老師拿到了第4票 小明拿到了第4票 黃牛拿到了第2票 小明拿到了第1票 老師拿到了第3票
能夠發現,多個線程操做同一資源可能存在併發性問題服務器
龜兔賽跑案例網絡
//模擬龜兔賽跑 public class Race implements Runnable{ //勝利者 private static String winner; @Override public void run() { for (int i = 0; i <= 100; i++) { //模擬兔子休息 if (Thread.currentThread().getName().equals("兔子") && i%10==0){ try { Thread.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); } } //判斷比賽是否結束 boolean flag = gameOver(i); //若是比賽結束了,就中止程序 if (flag){ break; } System.out.println(Thread.currentThread().getName()+"跑了"+i+"步"); } } //判斷是否完成比賽 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 race = new Race(); new Thread(race,"兔子").start(); new Thread(race,"烏龜").start(); } }
下載案例多線程
//線程建立方式三:實現callable接口 public class TestCallable implements Callable<Boolean> { private String url;//網絡圖片地址 private String name;//保存文件名 public TestCallable(String url, String name) { this.url = url; this.name = name; } //下載圖片的線程執行體 @Override public Boolean call() { WebDownLoader webDownLoader = new WebDownLoader(); webDownLoader.downLoader(url,name); System.out.println("下載了文件名爲"+name); return true; } public static void main(String[] args) throws ExecutionException, InterruptedException { TestCallable t1 = new TestCallable("https://i.guancha.cn/bbs/2020/07/10/20200710142345269.jpg?imageView2/2/w/500/format/jpg","1.jpg"); TestCallable t2 = new TestCallable("https://i.guancha.cn/bbs/2020/07/10/20200710142345269.jpg?imageView2/2/w/500/format/jpg","2.jpg"); TestCallable t3 = new TestCallable("https://i.guancha.cn/bbs/2020/07/10/20200710142345269.jpg?imageView2/2/w/500/format/jpg","3.jpg"); //建立執行服務 ExecutorService ser= Executors.newFixedThreadPool(3); //提交執行 Future<Boolean> r1 = ser.submit(t1); Future<Boolean> r2 = ser.submit(t2); Future<Boolean> r3 = ser.submit(t3); //獲取結果 boolean rs1 = r1.get(); boolean rs2 = r2.get(); boolean rs3 = r3.get(); System.out.println(rs1); System.out.println(rs2); System.out.println(rs3); //關閉服務 ser.shutdownNow(); } //下載器 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異常,downloader方法出現問題"); } } } }
(params)->expression[表達式] (params)->statement[語句] (params)->{statements} a->System.out.println("i like lambda-->"+a); new Thread(()->System.out.println("多線程學習。。。")).start();
爲何要使用lambda表達式併發
函數式接口的定義app
任何接口,若是隻包含惟一一個抽象方法,那麼他就是一個函數式接口。
public interface Runnable{ public abstract void run(); }
推到lambda表達式
/* 推到lambda表達式 */ public class TestLambda1 { //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簡化 ILike like1 = ()->{ System.out.println("i like lambda6"); }; like1.lambda(); } } //1.定義一個函數式接口 interface ILike{ void lambda(); } //2.實現類 class Like implements ILike{ @Override public void lambda() { System.out.println("i like lambda"); } }
案列二
public class TestLambda2 { static class Love implements ILove{ @Override public void love(int a) { System.out.println("i love u"+a); } } public static void main(String[] args) { //1.lambda表示簡化 ILove love = (int a) ->{ System.out.println("i love u"+a); }; //簡化1,去掉參數類型 love = (a)->{ System.out.println("i love u"+a); }; //簡化2,簡化括號 love = a->{ System.out.println("i love u"+a); }; //簡化3,去掉花括號 love = a -> System.out.println("i love u"+a); //總結: //lambda表達式只能有一行代碼的狀況下才能簡化成爲一行,若是有多行,那麼就用代碼塊包裹 //前提接口爲函數式接口 //多個參數也能夠去掉參數類型,要去掉就都去掉,必須加上括號 love.love(521); } } interface ILove{ void love(int a); }
案列
public class StaticProxy { public static void main(String[] args) { You you =new You();//你要結婚 new Thread( ()-> System.out.println("我愛你")).start(); 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 after() { System.out.println("結婚以後,收尾款"); } private void before() { System.out.println("結婚以前,佈置現場"); } }
五大狀態
setPriority(int newPriority): 更改線程的優先級
1.不推薦使用JDK提供的stop(),destroy()方法。【已廢棄】
2.推薦線程本身中止下來
3.建議使用一個標誌位進行終止變量當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方法模擬倒計時和打印當前時間
//模擬倒計時 public class TestSleep2 { public static void main(String[] args) { //打印當前系統時間 Date startTime = new Date(System.currentTimeMillis());//獲取系統當前時間 while (true){ try { Thread.sleep(1000); System.out.println(new SimpleDateFormat("HH:mm:ss").format(startTime)); startTime = new Date(System.currentTimeMillis()); } 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心情 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方法//想象爲插隊 public class TestJoin implements Runnable{ @Override public void run() { try { Thread.sleep(200); } catch (InterruptedException e) { e.printStackTrace(); } for (int i = 0; i < 100; i++) { System.out.println("線程vip來了"+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); } } }
Thread.State
線程狀態。線程能夠處於如下狀態之一:
一個線程能夠在給定的時間點處於一個狀態。這些狀態是不反映任何操做系統線程狀態的虛擬機狀態。
//觀察測試線程的狀態 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);//NEW //觀察啓動後 thread.start(); state = thread.getState(); System.out.println(state);//Runnable while (state != Thread.State.TERMINATED){//只要線程不終止,就一直輸出狀態 Thread.sleep(100); state = thread.getState();//更新線程狀態 System.out.println(state); } } }
線程的優先級用數字表示,範圍從1~10
使用如下方式改變或獲取優先級
優先級的設定建議再start()調度前
優先級低知識意味着得到調度的機率低,並非優先級低就不會被調用了,這都是看cpu的調度。
//測試守護線程 //上帝守護你 public class TestDaemon { public static void main(String[] args) { God god = new God(); You you = new You(); Thread thread = new Thread(god); thread.setDaemon(true);//默認是false表示是用戶線程,正常的線程都是用戶線程 thread.start(); new Thread(you).start();//你 用戶線程啓動。。。。 } } //上帝 class God implements Runnable{ @Override public void run() { while (true){ System.out.println("上帝保佑着你"); } } } //你 class You implements Runnable{ @Override public void run() { for (int i = 0; i < 36500; i++) { System.out.println("你一輩子都開心的活着"); } System.out.println("======goodbye!world============"); } }
多個線程操做同一個資源
併發:同一個對象被多個線程同時操做
線程同步:
造成條件:隊列+鎖
鎖:
因爲同一進程的多個線程共享同一塊存儲空間,在帶來方便的同時,也帶來了訪問衝突問題,爲了保證數據在方法中被訪問時的正確性,在訪問時加入鎖機制synchronized,當一個線程得到對象的排它鎖,獨佔資源,其餘線程必須等待,使用後釋放鎖便可,存在如下問題:
買票不安全
//不安全的買票 //線程不安全,有負數 public class UnsafeByyTicket { 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; private boolean flag = true;//外部中止方式 @Override public void run() { //買票 while (flag){ try { buy(); } catch (InterruptedException e) { e.printStackTrace(); } } } private void buy() throws InterruptedException { //判斷是否有票 if (ticketNums <= 0){ flag = false; return; } //模擬延時 Thread.sleep(100); //買票 System.out.println(Thread.currentThread().getName()+"拿到"+ticketNums--); } }
取錢不安全
//不安全的取錢 //兩我的去銀行取錢,帳戶 public class UnsafeBank { public static void main(String[] args) { Account account = new Account(100,"結婚基金"); Drawing you = new Drawing(account,50,"你"); Drawing girlFriend = new Drawing(account,100,"girlFriend"); you.start(); girlFriend.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; } //sleep能夠放大問題的發生性 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()+Thread.currentThread().getName()); System.out.println(this.getName()+"手裏的錢"+nowMoney); } }
不安全的集合
//線程不安全的集合 public class UnsafeList { public static void main(String[] args) throws InterruptedException { List<String> list = new ArrayList<String>(); for (int i = 0; i < 10000; i++) { new Thread(()->{ list.add(Thread.currentThread().getName()); }).start(); } Thread.sleep(5000); System.out.println(list.size()); } }
synchronized方法控制對「對象」的訪問,每一個對象對應一把鎖,每一個synchronized方法都必須得到調用該方法的對象的鎖才能執行,不然線程會阻塞,方法一旦執行,就獨佔該鎖,直到該方法返回才釋放鎖,後面被阻塞的線程才能得到這個鎖,繼續執行
缺陷:若將一個大的方法申明爲synchronized將會影響效率
同步塊
Obj稱之爲同步監視器
同步監視器的執行過程
### 安全的案例
//不安全的取錢 //兩我的去銀行取錢,帳戶 public class UnsafeBank { public static void main(String[] args) { Account account = new Account(1000,"結婚基金"); Drawing you = new Drawing(account,50,"你"); Drawing girlFriend = new Drawing(account,100,"girlFriend"); you.start(); girlFriend.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; } //取錢 //synchronized默認鎖的是this @Override public void run() { synchronized (account){ //判斷有沒有錢 if (account.money-drawingMoney <0){ System.out.println(Thread.currentThread().getName()+"錢不夠,取不了"); return; } //sleep能夠放大問題的發生性 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()+Thread.currentThread().getName()); System.out.println(this.getName()+"手裏的錢"+nowMoney); } } }
//不安全的買票 //線程不安全,有負數 public class UnsafeByyTicket { 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; private boolean flag = true;//外部中止方式 @Override public void run() { //買票 while (flag){ try { buy(); } catch (InterruptedException e) { e.printStackTrace(); } } } //synchronized同步方法,鎖的是this private synchronized void buy() throws InterruptedException { //判斷是否有票 if (ticketNums <= 0){ flag = false; return; } //買票 System.out.println(Thread.currentThread().getName()+"拿到"+ticketNums--); } }
//線程不安全的集合 public class UnsafeList { public static void main(String[] args) throws InterruptedException { List<String> list = new ArrayList<String>(); for (int i = 0; i < 10000; i++) { new Thread(()->{ synchronized (list){ list.add(Thread.currentThread().getName()); } }).start(); } Thread.sleep(3000); System.out.println(list.size()); } }
//測試JUC安全類型的集合 public class TestJUC { public static void main(String[] args) { CopyOnWriteArrayList<String> list = new CopyOnWriteArrayList<String>(); 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()); } }
死鎖避免方法
產生死鎖的四個必要條件:
上面列出了死鎖的四個必要條件,咱們只要想辦法破解其中的任意一個或多個條件就能夠避免死鎖發生
優先使用順序
//測試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 = 1000; //定義lock鎖 private final ReentrantLock lock = new ReentrantLock(); @Override public void run() { while (true){ try { lock.lock();//加鎖 if (ticketNums > 0){ /* try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); }*/ System.out.println(Thread.currentThread().getName()+" "+ticketNums--); }else { break; } }finally { lock.unlock(); } } } }
應用場景:生產者和消費者問題
這是一個線程同步問題,生產者和消費者共享同一個資源,而且生產者和消費者之間相互依賴,互爲條件
在生產者消費者問題中,僅有synchronized是不夠的
方法名
做用
wait()
表示線程一直等待,直到其餘線程通知,與sleep不一樣,會釋放鎖
wait(long timeout)
指定等待的毫秒數
notify()
喚醒一個處於等待狀態的線程
notifyAll()
喚醒同一個對象上全部調用wait()方法的線程,優先級別高的線程優先調度
均是Object類的方法,都只能在同步方法或者同步代碼塊中使用,不然會拋出異常IllegalMonitorStateException
併發協做模型「生產者/消費者模式」—>管程法
//測試:生產者消費者模型--》利用緩衝區解決:管程法 //生產者,消費者,產品,緩衝區 public class TestPC { public static void main(String[] args) { SynContainer container = new SynContainer(); new Productor(container).start(); new Consumer(container).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 Chicken(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 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; } }
package com.kuang.gaoji; //測試生產者消費者問題2:信號燈法,標誌位解決 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{ //演員表演時,觀衆等待 //觀衆觀看,演員等待 String voice;//表演的節目 boolean flag = true; //表演 public synchronized void play(String voice){ if (flag!= true){ 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 == true){ try { this.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } System.out.println("觀衆觀看了"+voice); //通知演員表演 this.notifyAll(); this.flag = !this.flag; } }
好處:
便於線程管理(…)
ExecutorService:真正的線程池接口。常見子類ThreadPoolExecutor
//測試線程池 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()); } }
//回顧總結線程的建立 public class ThreadNew { public static void main(String[] args) { new MyThread1().start(); new Thread(new MyThread2()).start(); FutureTask<Integer> futureTask = new FutureTask<Integer>(new MyThread3()); new Thread(futureTask).start(); try { Integer integer = futureTask.get(); System.out.println(integer); } catch (InterruptedException e) { e.printStackTrace(); } catch (ExecutionException e) { e.printStackTrace(); } } } //1.繼承Thread類 class MyThread1 extends Thread{ @Override public void run(){ System.out.println("MyThread1"); } } //2.實現Runnable接口 class MyThread2 implements Runnable{ @Override public void run() { System.out.println("MyThread2"); } } //3.實現Callable接口 class MyThread3 implements Callable<Integer>{ @Override public Integer call() throws Exception { System.out.println("MyThread3"); return 100; } }
感謝你看到這裏,看完有什麼的不懂的能夠在評論區問我,以爲文章對你有幫助的話記得給我點個贊,天天都會分享java相關技術文章或行業資訊,歡迎你們關注和轉發文章!