03-Java核心類庫_多執行緒
目錄
四,多執行緒
1,執行緒與程式
1.1 執行緒與程式
程式:
- 是指一個記憶體中執行的應用程式,每個程式都有一個獨立的記憶體空間
執行緒:
- 是程式中的一個執行路徑,共享一個記憶體空間,執行緒之間可以自由切換,併發執行. 一個程式最少有一個執行緒
- 執行緒實際上是在程式基礎之上的進一步劃分,一個程式啟動之後,裡面的若干執行路徑又可以劃分成若干個執行緒
1.2 執行緒排程
目的是為了更合理的利用CPU
分時排程
- 所有執行緒輪流使用 CPU 的使用權,平均分配每個執行緒佔用 CPU 的時間。
搶佔式排程
- 優先讓優先順序高的執行緒使用 CPU,如果執行緒的優先順序相同,那麼會隨機選擇一個(執行緒隨機性),Java使用的為搶佔式排程。
- CPU使用搶佔式排程模式在多個執行緒間進行著高速的切換。對於CPU的一個核新而言,某個時刻,只能執行一個執行緒,而 CPU的在多個執行緒間切換速度相對我們的感覺要快,看上去就是 在同一時刻執行。 其實,多執行緒程式並不能提高程式的執行速度,但能夠提高程式執行效率,讓CPU的 使用率更高。
2,同步與非同步&併發與並行
2.1 同步與非同步
同步:排隊執行 , 效率低但是安全.
非同步:同時執行 , 效率高但是資料不安全
2.2 併發與並行
併發:指兩個或多個事件在同一個時間段內發生。
並行:指兩個或多個事件在同一時刻發生(同時發生)。
3,繼承Thread
3.1 程式例項
3.2 時序圖
3.3 補充
每個執行緒都有自己的棧空間,共用一份堆記憶體
4,實現Runnable
4.1 使用方法
另一種實現多執行緒的方法
- 建立自定義類實現Runnable介面,並重寫run方法;
- 用自定義類建立一個物件r;
- 用Thread類建立一個物件t,並將r作為t構造方法的引數;
4.2 實現Runnable與繼承Thread
1)實現Runnable與繼承Thread相比有如下優勢
- 1,通過建立任務,然後給執行緒分配任務的方式實現多執行緒,更適合多個執行緒同時執行任務的情況;
- 2,可以避免單繼承所帶來的侷限性(Java允許實現多個介面,但不允許繼承多個父類);
- 3,任務與執行緒是分離的,提高了程式的健壯性;
- 4,後期學習的執行緒池技術,接受Runnable型別的任務,不接受Thread型別的執行緒;
2)Thread也有一定的好處
5,Thread類
5.1 常用構造方法
5.2 常用其他方法
停止執行緒的方法:宣告一個變數,執行緒不斷監控這個變數,一旦變數達到某種條件呼叫return即可
所有的使用者執行緒結束,程式才能結束。守護執行緒是為了守護使用者執行緒,使用者執行緒可以自動結束,所有使用者執行緒結束後,守護執行緒便會像沒有一樣。
6,設定和獲取執行緒名稱
7,執行緒休眠sleep
8,執行緒的中斷
過時的stop方法可以直接中斷執行緒,但是如果執行緒來不及釋放資源,會造成一部分垃圾無法回收;
這裡採用新增中斷標記的方法:呼叫interrupt方法,子執行緒執行時捕獲中斷異常,並在catch塊中,新增處理釋放資源的程式碼;
9,守護執行緒
9.1 概述
執行緒分為守護執行緒和使用者執行緒;
- 使用者執行緒:當一個程式不包含任何存活的使用者執行緒時,程式結束;
- 守護執行緒:守護使用者執行緒,當最後一個使用者執行緒結束後,所有守護執行緒自動死亡;
直接建立的都是使用者執行緒;
設定守護執行緒:執行緒物件.setDaemon(true);
9.2 例項
1)不設定守護執行緒
2)設定為守護執行緒
10,執行緒安全1-同步程式碼塊
10.1 執行緒不安全的原因
多個執行緒爭搶同一個資料,使得資料在判斷和使用時出現不一致的情況。解決方法,保證一段資料同時只能被一個執行緒使用(排隊使用)。
解決方案一:同步程式碼塊
格式:
synchronize(鎖物件){ }
10.2 程式碼例項
1,不加鎖
2,加鎖後
package com.kaikeba;
import javax.xml.namespace.QName;
import java.io.*;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Properties;
import java.util.Scanner;
import java.util.SimpleTimeZone;
public class Demo1 {
public static void main(String[] args) throws InterruptedException {
Object o = new Object();
//執行緒不安全
//解決方案1 同步程式碼塊
//格式:synchronized(鎖物件){
//
//
// }
Runnable run = new Ticket();
new Thread(run).start();
new Thread(run).start();
new Thread(run).start();
}
static class Ticket implements Runnable{
//總票數
private int count = 10;
private Object o = new Object();
@Override
public void run() {
//Object o = new Object(); //這裡不是同一把鎖,所以鎖不住
while (true) {
synchronized (o) {
if (count > 0) {
//賣票
System.out.println("正在準備賣票");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
count--;
System.out.println(Thread.currentThread().getName()+"賣票結束,餘票:" + count);
}else {
break;
}
}
}
}
}
}
11,執行緒安全2-同步方法
同步程式碼塊粒度較細,可以給一行程式碼單獨加鎖,同步方法顧名思義,是給方法加鎖;
package com.kaikeba;
import javax.xml.namespace.QName;
import java.io.*;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Properties;
import java.util.Scanner;
import java.util.SimpleTimeZone;
public class Demo1 {
public static void main(String[] args) throws InterruptedException {
Object o = new Object();
//執行緒不安全
//解決方案2 同步方法
Runnable run = new Ticket();
new Thread(run).start();
new Thread(run).start();
new Thread(run).start();
}
static class Ticket implements Runnable{
//總票數
private int count = 10;
@Override
public void run() {
while (true) {
boolean flag = sale();
if(!flag){
break;
}
}
}
public synchronized boolean sale(){
if (count > 0) {
//賣票
System.out.println("正在準備賣票");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
count--;
System.out.println(Thread.currentThread().getName()+"賣票結束,餘票:" + count);
return true;
}
return false;
}
}
}
給方法上鎖,對應的鎖物件就是this, 如果是靜態修飾方法的話,鎖物件為類名.class(比如這裡sale方法若被修飾為靜態方法的話,鎖物件為Ticket.class,也就是位元組碼檔案物件)
針對以上程式碼來說 ,鎖物件如下:
12,執行緒安全3-顯式鎖Lock
同步方法和同步程式碼塊都屬於隱式鎖,顯式鎖則是程式設計師手動加鎖、解鎖;
package thread;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
//同步程式碼塊和同步方法都屬於隱式鎖
//執行緒同步lock
public class Demo10 {
public static void main(String[] args) {
Object o = new Object();
//執行緒不安全
//解決方案1 顯示鎖 Lock 子類 ReentrantLock
Runnable run = new Ticket();
new Thread(run).start();
new Thread(run).start();
new Thread(run).start();
}
static class Ticket implements Runnable{
//總票數
private int count = 10;
//引數為true表示公平鎖 預設是false 不是公平鎖
private Lock l = new ReentrantLock(true);
@Override
public void run() {
while (true) {
l.lock();
if (count > 0) {
//賣票
System.out.println("正在準備賣票");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
count--;
System.out.println(Thread.currentThread().getName()+"賣票結束,餘票:" + count);
}else {
break;
}
l.unlock();
}
}
}
}
13,公平鎖與非公平鎖
13.1 區別
公平鎖:先來先得,遵循排隊;
非公平鎖:大家一起搶(同步程式碼塊,同步方法,顯式鎖都屬於非公平鎖);
13.2 實現方法
在顯式鎖例項化時,傳入引數true()
14,多執行緒通訊問題
主要藉助於wait和notify函式實現
15,生產者與消費者
15.1 前提條件
廚師cook為生產者執行緒,服務員waiter為消費者執行緒,食物為生產與消費的物品;
假設目前只有一個廚師,一個服務員,一個盤子。理想狀態是:廚師生產一份飯菜,服務員端走一份,且飯菜的屬性未發生錯亂;
廚師可以製作兩種口味的飯菜,製作100次;
服務員可以端走飯菜100次;
15.2 問題一:飯菜的屬性錯亂
1)實驗程式碼
package com.kaikeba;
public class Demo1 {
public static void main(String[] args) {
//多執行緒通訊 生產者與消費者問題
Food f = new Food();
new Cook(f).start();
new Waiter(f).start();
}
//廚師
static class Cook extends Thread{
private Food f;
public Cook(Food f) {
this.f = f;
}
@Override
public void run() {
for (int i = 0; i < 100; i++) {
if(i%2==0){// 設計兩種菜色
f.setNameAndTaste("老乾媽小米粥","香辣味");
}else {
f.setNameAndTaste("煎餅果子","甜辣味");
}
}
}
}
//服務員
static class Waiter extends Thread{
private Food f;
public Waiter(Food f) {
this.f = f;
}
@Override
public void run() {
for (int i = 0; i < 100; i++) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
f.get();
}
}
}
//食物
static class Food{
private String name;
private String taste;
public void setNameAndTaste(String name,String taste){// 生產
this.name = name;
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
this.taste = taste;
}
public void get(){ // 消費
System.out.println("服務員端走的菜的名稱是:"+name+",味道是:"+taste);
}
}
}
2)錯誤現象
3)錯誤原因
15.3 問題二:一次性消費/生產多個菜品
1)實驗程式碼
為了防止在生產過程中setNameAndTaste出現時間片切換,可以用synchronized修飾此方法;
public synchronized void setNameAndTaste(String name,String taste){// 生產
this.name = name;
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
this.taste = taste;
}
public synchronized void get(){ // 消費
System.out.println("服務員端走的菜的名稱是:"+name+",味道是:"+taste);
}
2)執行效果
3)原因分析
synchronized只是確保了方法內部不會發生執行緒切換,但並不能保證生產一個消費一個的邏輯關係;
15.4 解決方法
廚師做完飯後喊醒服務員,自己睡著。服務員送完飯後喊醒廚師,自己睡著;
主要修改的部分為setNameAndTaste與get方法:
public synchronized void setNameAndTaste(String name,String taste){// 生產
if(flag) {
this.name = name;
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
this.taste = taste;
flag = false; // 表示飯菜生產完畢
this.notifyAll(); // 叫醒服務員
try {
this.wait(); // 睡著
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public synchronized void get(){ // 消費
if(!flag){
System.out.println("服務員端走的菜的名稱是:"+name+",味道是:"+taste);
flag = true;
this.notifyAll();
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
可以看出飯菜是交替產生並消費的;
16,執行緒的六種狀態
17,帶返回值的執行緒Callable
新的建立執行緒的方式。之前的建立執行緒方式:實現Thread的子類、實現Runnable介面,可以看成是和主執行緒併發執行的;
這裡要講的執行緒更像是主執行緒指派的一個任務,主執行緒可以獲得其返回值;
17.1 Runnable 與 Callable
1)介面定義
介面定義
//Callable介面
public interface Callable<V> {
V call() throws Exception;
}
//Runnable介面
public interface Runnable {
public abstract void run();
}
2)相同點
都是介面
都可以編寫多執行緒程式
都採用Thread.start()啟動執行緒
3)不同點
Runnable沒有返回值;Callable可以返回執行結果
Callable介面的call()允許丟擲異常;Runnable的run()不能丟擲
17.2 Callable使用步驟
1. 編寫類實現Callable介面 , 實現call方法
class XXX implements Callable<T> { @Override public <T> call() throws Exception { return T; } }
2. 建立FutureTask物件 , 並傳入第一步編寫的Callable類物件
FutureTask<Integer> future = new FutureTask<>(callable);
3. 通過Thread,啟動執行緒
new Thread(future).start();
17.3 常用方法
1)方法介紹
2)程式碼示例
package com.kaikeba;
import java.util.concurrent.Callable;
import java.util.concurrent.FutureTask;
public class Demo1 {
public static void main(String[] args) {
Callable<Integer> c = new MyCallable();
FutureTask<Integer> f = new FutureTask<>(c);
new Thread(f).start();
for(int i = 0; i < 10; i++) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + i);
}
}
static class MyCallable implements Callable<Integer> {
@Override
public Integer call() throws Exception {
// Thread.sleep(100);// 睡眠過後 給出結果
for(int i = 0; i < 10; i++) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + i);
}
return 100;
}
}
}
未使用get方法時,主執行緒和另一個執行緒交替執行
使用get方法
18,執行緒池概述
18.1 為什麼需要執行緒池
如果併發的執行緒數量很多,並且每個執行緒都是執行一個時間很短的任務就結束了,這樣頻繁建立執行緒 就會大大降低 系統的效率,因為頻繁建立執行緒和銷燬執行緒需要時間.
執行緒池就是一個容納多個執行緒的容 器,池中的執行緒可以反覆使用,省去了頻繁建立執行緒物件的操作,節省了大量的時間和資源。
Java中的四種執行緒池(物件均為ExecutorService)
18.2 快取執行緒池
1)概述
/*** 快取執行緒池.
* (長度無限制)
* 執行流程:
* 1. 判斷執行緒池是否存在空閒執行緒
* 2. 存在則使用
* 3. 不存在,則建立執行緒 並放入執行緒池, 然後使用
*/
ExecutorService service = Executors.newCachedThreadPool();
//向執行緒池中 加入 新的任務
service.execute(new Runnable() {
@Override
public void run() {
System.out.println("執行緒的名稱:"+Thread.currentThread().getName());
}
});
service.execute(new Runnable() {
@Override
public void run() {
System.out.println("執行緒的名稱:"+Thread.currentThread().getName());
}
});
service.execute(new Runnable() {
@Override
public void run() {
System.out.println("執行緒的名稱:"+Thread.currentThread().getName());
}
});
2)程式碼示例
package com.kaikeba;
import java.util.concurrent.*;
public class Demo1 {
public static void main(String[] args) throws ExecutionException, InterruptedException {
/*** 快取執行緒池.
* (長度無限制)
* 執行流程:
* 1. 判斷執行緒池是否存在空閒執行緒
* 2. 存在則使用
* 3. 不存在,則建立執行緒 並放入執行緒池, 然後使用
*/
ExecutorService service = Executors.newCachedThreadPool();
// 指揮執行緒池執行新的任務
service.execute(new Runnable() {// 匿名內部類
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + "滴滴滴");
}
});
service.execute(new Runnable() {// 匿名內部類
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + "滴滴滴");
}
});
service.execute(new Runnable() {// 匿名內部類
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + "滴滴滴");
}
});
}
}
睡眠一段時間後,新增新的任務,檢視是否能利用執行緒池中已存在的執行緒:
18.3 定長執行緒池
1)概述
/**
* 定長執行緒池.
* (長度是指定的數值)
* 執行流程:
* 1. 判斷執行緒池是否存在空閒執行緒
* 2. 存在則使用
* 3. 不存在空閒執行緒,且執行緒池未滿的情況下,則建立執行緒 並放入執行緒池, 然後使用
* 4. 不存在空閒執行緒,且執行緒池已滿的情況下,則等待執行緒池存在空閒執行緒
*/
ExecutorService service = Executors.newFixedThreadPool(2);
service.execute(new Runnable() {
@Override
public void run() {
System.out.println("執行緒的名稱:"+Thread.currentThread().getName());
}
});
service.execute(new Runnable() {
@Override
public void run() {
System.out.println("執行緒的名稱:"+Thread.currentThread().getName());
}
});
2)程式碼示例
設定執行緒池大小為2,即執行緒池中最多隻允許存在兩個執行緒;
前兩個執行緒執行時,均sleep三秒鐘;
第三個任務由於執行緒池已滿,不能開闢新的執行緒,所以必須等執行緒池中有空閒執行緒出現才可以執行;
package com.kaikeba;
import java.util.concurrent.*;
public class Demo1 {
/*定長執行緒池
長度是指定的執行緒池
加入任務後的執行流程
1 判斷執行緒池是否存在空閒執行緒
2 存在則使用
3 不存在空閒執行緒 且執行緒池未滿的情況下 則建立執行緒 並放入執行緒池中 然後使用
4 不存在空閒執行緒 且執行緒池已滿的情況下 則等待執行緒池的空閒執行緒
**/
public static void main(String[] args) {
ExecutorService service = Executors.newFixedThreadPool(2);
service.execute(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"鋤禾日當午");
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
service.execute(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"鋤禾日當午");
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
service.execute(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"鋤禾日當午");
}
});
}
}
18.4 單執行緒執行緒池
1)概述
效果與定長執行緒池 建立時傳入數值1 效果一致.
/**
* 單執行緒執行緒池.
* 執行流程:
* 1. 判斷執行緒池 的那個執行緒 是否空閒
* 2. 空閒則使用
* 3. 不空閒,則等待 池中的單個執行緒空閒後 使用
*/
ExecutorService service = Executors.newSingleThreadExecutor();
service.execute(new Runnable() {
@Override
public void run() {
System.out.println("執行緒的名稱:"+Thread.currentThread().getName());
}
});
service.execute(new Runnable() {
@Override
public void run() {
System.out.println("執行緒的名稱:"+Thread.currentThread().getName());
}
});
2)程式碼示例
package com.kaikeba;
import java.util.concurrent.*;
public class Demo1 {
/*單執行緒執行緒池
執行流程
1 判斷執行緒池的那個執行緒是否空閒
2 空閒則使用
3 不空閒則等待它空閒後再使用
**/
public static void main(String[] args) {
ExecutorService service = Executors.newSingleThreadExecutor();
service.execute(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"鋤禾日當午");
}
});
service.execute(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"鋤禾日當午");
}
});
service.execute(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"鋤禾日當午");
}
});
}
}
18.5 週期定長執行緒池
1)概述
public static void main(String[] args) {
/**
* 週期任務 定長執行緒池.
* 執行流程:
* 1. 判斷執行緒池是否存在空閒執行緒
* 2. 存在則使用
* 3. 不存在空閒執行緒,且執行緒池未滿的情況下,則建立執行緒 並放入執行緒池, 然後使用
* 4. 不存在空閒執行緒,且執行緒池已滿的情況下,則等待執行緒池存在空閒執行緒
*
* 週期性任務執行時:
* 定時執行, 當某個時機觸發時, 自動執行某任務 .
*/
ScheduledExecutorService service = Executors.newScheduledThreadPool(2);
/**
* 定時執行
* 引數1. runnable型別的任務
* 引數2. 時長數字
* 引數3. 時長數字的單位
*/
/*
service.schedule(new Runnable() {
@Override
public void run() {
System.out.println("倆人相視一笑~ 嘿嘿嘿");
}
},5,TimeUnit.SECONDS);
*/
/**
* 週期執行
* 引數1. runnable型別的任務
* 引數2. 時長數字(延遲執行的時長)
* 引數3. 週期時長(每次執行的間隔時間)
* 引數4. 時長數字的單位
*/
service.scheduleAtFixedRate(new Runnable() {
@Override
public void run() {
System.out.println("倆人相視一笑~ 嘿嘿嘿");
}
},5,2,TimeUnit.SECONDS);
}
2)程式碼示例
package com.kaikeba;
import java.util.concurrent.*;
public class Demo1 {
/*週期任務 定長執行緒池
執行流程
1 判斷執行緒池是否存在空閒執行緒
2 存在則使用
3 不存在空閒執行緒 且執行緒池未滿的情況下 則建立執行緒 並放入執行緒池中 然後使用
4 不存在空閒執行緒 且執行緒池已滿的情況下 則等待執行緒池的空閒執行緒
週期性任務執行時
定時執行 當某個任務觸發時 自動執行某任務
**/
public static void main(String[] args) {
ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(2);
//定時執行一次
//引數1:定時執行的任務
//引數2:時長數字
//引數3:2的時間單位 Timeunit的常量指定
/* scheduledExecutorService.schedule(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"鋤禾日當午");
}
},5, TimeUnit.SECONDS); //5秒鐘後執行*/
/*
週期性執行任務
引數1:任務
引數2:延遲時長數字(第一次執行延遲的時間)
引數3:週期時長數字(每隔多久執行一次)
引數4:時長數字的單位
* **/
scheduledExecutorService.scheduleAtFixedRate(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"鋤禾日當午");
}
},5,1,TimeUnit.SECONDS);
}
}
19,Lambda表示式
19.1 為什麼要用lambda表示式
對於某些應用場景,我們更注重於結果,如果能用一個方法解決,那麼通過建立物件、呼叫方法的方式可能會更加繁瑣;
1)冗餘的Runnable方法
public class Demo1 {
/**
* lambda表示式
* 函數語言程式設計思想(注重結果,而物件導向則是通過建立物件,解決問題)
* @param args
*/
public static void main(String[] args) {
// 冗餘的Runnable程式碼
Runnable r = new MyRunnable();
Thread t = new Thread(r);
t.start(); // 寫了這麼多 只為了完成一個簡單的任務
}
static class MyRunnable implements Runnable{
@Override
public void run() {
System.out.println("任務完成!");
}
}
}
2)通過匿名內部類簡化程式碼
public class Demo1 {
/**
* lambda表示式
* 函數語言程式設計思想(注重結果,而物件導向則是通過建立物件,解決問題)
* @param args
*/
public static void main(String[] args) {
// 冗餘的Runnable程式碼
Thread t = new Thread(new Runnable() {
@Override
public void run() {
System.out.println("任務完成!");
}
});
t.start(); // 寫了這麼多 只為了完成一個簡單的任務
}
}
19.2 使用例項
1)不使用lambda
package com.kaikeba;
import java.util.concurrent.*;
public class Demo1 {
/**
* lambda表示式
* 函數語言程式設計思想(注重結果,而物件導向則是通過建立物件,解決問題)
* @param args
*/
public static void main(String[] args) {
print(new MyMath() {
@Override
public int sum(int x, int y) {
return x + y;
}
}, 100, 200);
}
public static void print(MyMath m, int x, int y){
int num = m.sum(x, y);
System.out.println(num);
}
static interface MyMath{
int sum(int x, int y);
}
}
2)使用lambda
不需要實現介面、例項化物件;
package com.kaikeba;
import java.util.concurrent.*;
public class Demo1 {
/**
* lambda表示式
* 函數語言程式設計思想(注重結果,而物件導向則是通過建立物件,解決問題)
* @param args
*/
public static void main(String[] args) {
print((int x, int y) -> {
return x + y;
}, 100, 200);
}
public static void print(MyMath m, int x, int y){
int num = m.sum(x, y);
System.out.println(num);
}
static interface MyMath{
int sum(int x, int y);
}
}
相關文章
- 03-Java核心類庫_列舉、註解與反射Java反射
- 多執行緒核心技術(1)-執行緒的基本方法執行緒
- 多執行緒-以前的執行緒安全的類回顧執行緒
- 多執行緒-匿名內部類的方式實現多執行緒程式執行緒
- JAVA重點類 多執行緒Java執行緒
- 核心執行緒執行緒
- 多執行緒和多執行緒同步執行緒
- 多執行緒【執行緒池】執行緒
- 多執行緒--執行緒管理執行緒
- Java多執行緒——執行緒Java執行緒
- 執行緒與多執行緒執行緒
- Java多執行緒(二):Thread類Java執行緒thread
- java多執行緒之Thread類Java執行緒thread
- Java多執行緒Thread類使用Java執行緒thread
- VC多執行緒 C++ 多執行緒執行緒C++
- 多執行緒-執行緒控制之休眠執行緒執行緒
- 多執行緒-執行緒控制之加入執行緒執行緒
- 多執行緒-執行緒控制之禮讓執行緒執行緒
- 多執行緒-執行緒控制之中斷執行緒執行緒
- 多執行緒之初識執行緒執行緒
- Java多執行緒-執行緒中止Java執行緒
- Java多執行緒——執行緒池Java執行緒
- 多執行緒-執行緒概述等執行緒
- 多執行緒程式設計的核心思想執行緒程式設計
- 多執行緒系列(1),多執行緒基礎執行緒
- 多執行緒系列(二):多執行緒基礎執行緒
- 多執行緒------執行緒與程式/執行緒排程/建立執行緒執行緒
- 多執行緒系列(二)之Thread類執行緒thread
- Java多執行緒同步工具類之SemaphoreJava執行緒
- Java多執行緒同步工具類之CountDownLatchJava執行緒CountDownLatch
- Java多執行緒同步工具類之CyclicBarrierJava執行緒
- 使用 ThreadPoolExecutor 建立多執行緒工具類thread執行緒
- CountDownLatch 多執行緒同步輔助類用法CountDownLatch執行緒
- 多執行緒-執行緒控制之守護執行緒執行緒
- 多執行緒程式設計進階——Java類庫中的鎖執行緒程式設計Java
- a、多執行緒執行緒
- java 多執行緒守護執行緒Java執行緒
- Java多執行緒-執行緒通訊Java執行緒