文章目錄
- 多執行緒技術概述
- 執行緒和行程
- 執行緒調度
- 同步與異步
- 并發與并行
- 兩種創建方式
- Thread
- Runnable
- 執行緒常用方法
- getName()與setName()
- sleep()
- 執行緒阻塞
- 執行緒中斷
- 守護執行緒
- 執行緒安全問題
- synchronized(執行緒同步)
- 1、同步代碼塊
- 2、同步方法
- 3、顯示鎖(Lock)
- 公平鎖和非公平鎖
- 執行緒死鎖
- 多執行緒通信問題
- 執行緒的六種狀態
- 特殊的創建方法
- Callable和Runnable的區別
- Runnable與Callable的相同點
- Runnable與Callable的不同點
- 執行緒池概述
- 執行緒池Executors
- 執行緒池的好處
- 執行緒池原理圖
- Java中的四種執行緒池 .ExecutorService
- 1、快取執行緒池
- 2、定長執行緒池
- 3、單執行緒執行緒池
- 4、周期性任務定長執行緒池
- Lambda運算式
多執行緒技術概述
執行緒和行程
行程:
- 是指一個記憶體中運行的應用程式,每個行程都有一個獨立的記憶體空間,并且行程互不共享記憶體空間,除非通過特殊手段,程式就是行程,例如:電腦的各個圖示
執行緒:
- 是行程中的一個執行路徑,共享一個記憶體空間,執行緒之間可以自由切換,并發執行,一個行程最少有一個執行緒,
- 執行緒實際上是在行程基礎之上的進一步劃分,一個行程啟動之后,里面的若干執行路徑又可以劃分成若干個執行緒,
- 用到哪一個哪一個被喚醒,例如:和多人聊天 sleep 生產者和消費者
執行緒和行程的關系:一個行程由多個執行緒支撐運行,
執行緒調度
分時調度
- 所有執行緒輪流使用CPU的使用權,平均分配每個執行緒占用CPU的時間,
搶占式調度
-
優先讓優先級高的執行緒使用CPU,如果執行緒的優先級相同,那么會隨機選擇一個(執行緒隨機性),Java使用的為搶占式呼叫,
-
CPU使用搶占式調度模式在多個執行緒間進行著高速的切換,對于CPU的一個核心而言,某個時刻,只能執行一個執行緒,而CPU在多個執行緒間切換的速度相對于我們的感覺要快,看上去就是在同一時刻運行,其實,多執行緒程式并不能提高程式的運行速度,但能提高程式的運行效率,讓CPU的使用率更高,
同步與異步
同步:排隊執行,效率低但是安全,
異步:同時執行,效率高但是資料不安全,
并發與并行
并發:指兩個或多個事件在同一個時間段內發生,
并行:指兩個或多個事件在同一時刻發生(同時發生),
兩種創建方式
Thread
public static void main(String[] args){
MyThread m = new MyThread();
m.start();
for(int i=0;i<10;i++){
System.out.println("汗滴禾下土"+i);
}
}
class MyThread extends Thread{
//run方法就是執行緒要執行的任務方法
@Override
public void run(){
//這里的代碼 就是一條新的執行路徑
//這個執行路徑的觸發方式,不是呼叫run方法,而是通過thread物件的start來啟動任務
for(int i=0;i<10;i++){
System.out.println("鋤禾日當午"+i);
}
}
}
//結果主程式和執行緒的輸出陳述句同時執行,
執行流程圖如下所示:

注意:每個執行緒都擁有自己的堆疊空間,共用一份堆記憶體,
Runnable
public static void main(String[] args){
//實作Runnable
//1.創建一個任務物件
MyRunnable r = new MyRunnable();
//2.創建一個執行緒,并為其分配一個任務
Thread t = new Thread(r);
//3.執行這個執行緒
t.start();
for(int i=0;i<10;i++){
System.out.println("疑是地上霜"+i);
}
}
//這是用于給執行緒執行的任務
class MyRunnable implements Runnable{
@Override
public void run(){
//執行緒的任務
for(int i=0;i<10;i++){
System.out.println("床前明月光"+i);
}
}
}
實作Runnable與繼承Thread相比有如下優勢:
- 通過創建任務,然后給執行緒分配的方式來實作的多執行緒,更適合多個執行緒同時執行相同任務的情況;
- 可以避免單繼承所帶來的局限性;
- 任務與執行緒本身是分離的,提高了程式的健壯性;
- 執行緒池技術,只接受Runnable型別的任務,不接受Thread型別的執行緒;
public static void main(String[] args){
new Thread(){//通過匿名內部類的方式創建一個執行緒
@Override
public void run(){
for(int i=0;i<10;i++){
System.out.println("一二三四五")
}
}
}.start();
for(int i=0;i<10;i++){
System.out.println("六七八九十"+i);
}
}
執行緒常用方法
getName()與setName()
獲取執行緒名稱與設定執行緒名稱:
public static void main(String[] args){
System.out.println(Thread.currentThread().getName());//輸出:main
new Thread(new MyRunnable()).start();//輸出:Thread-0
new Thread(new MyRunnable()).start();//輸出:Thread-1
new Thread(new MyRunnable()).start();//輸出:Thread-2
new Thread(new MyRunnable(),"鋤禾日當午").start;//輸出:鋤禾日當午 注意,這個方法是給執行緒命名
Thread t = new Thread(new MyRunnable());//另一種命名的方法
t.setName("鋤禾日當午");
t.start();
}
static class MyRunnable implements Runnable{
@Override
public void run(){
Sysmte.out.println(Thread.currentThread().getName());
}
}
sleep()
執行緒的休眠:
public static void main(String[] args){
for(int i=0;i<10;i++){
System.out.println(i);
Thread.sleep(1000);//每隔1秒輸出一個數
}
}
執行緒阻塞
執行緒阻塞不光指的是執行緒休眠,執行緒是一條執行路徑,比如說:執行緒在執行代碼時,它的執行路徑有100行,從第一行到第一百行是它的整體執行路徑,這100行中某10行可能是為了讀取某個檔案,這檔案讀取可能耗時1秒鐘,那么這1秒鐘也是阻塞的,停在那讀檔案,后面讀完才會執行,可以把阻塞理解成所有消耗時間的操作,就像上面的讀取檔案,它會使執行緒等待在那個位置,直到讀取完畢,不會往下執行,除非檔案讀完,就像控制臺等待用戶輸入,用戶不輸入,程式就不會繼續往下執行,這就是執行緒阻塞,我們也稱其為耗時操作,
執行緒中斷
一個執行緒是一個獨立的執行路徑,它是否應該結束,應該由其自身決定,執行緒啟動程序中可能會使用N多資源,也涉及到N多需要釋放的資源,那么這時如果由外部直接把執行緒掐死,那么極有可能導致執行緒占用的資源來不及釋放而一直占用,從而產生記憶體垃圾,這個垃圾是你不能回收的垃圾,也有可能占用一些硬體資源,導致硬體資源來不及釋放,其他軟體沒有辦法再去使用,
執行緒中斷不用stop方法,因為此方法是直接把執行緒掐死,我們應該使用interrupt方法,通過該方法給執行緒打上一個標記,在特殊情況下執行緒會檢查iterrupt的狀態,如果檢查到就是報例外,就是告訴執行緒該自殺了,執行緒在捕獲例外陳述句塊里呼叫return方法就會中斷執行緒,
public static void main(String[] args){
Thread t1 = new Thread(new MyRunnable());
t1.start();
for(int i=1;i<=5;i++){
System.out.println(Thread.currentThread().getName()+":"+i);
try{
Thread.sleep(1000);
} catch(InterruptedException e) {
e.printStackTrace();
}
}
//給執行緒t1添加中斷標記
t1.interrupt();
}
static class MyRunnable implements Runnable{
@Override
public void run(){
for(int i=0;i<10;i++){
System.out.println(Thread.currentThread().getName()+":"+i);
try{
Thread.sleep(1000);
} catch(InterruptedException e) {
System.out.println("發現了中斷標記,我們這個執行緒自殺");
return;
}
}
}
}

守護執行緒
執行緒:分為守護執行緒和用戶執行緒;
用戶執行緒:當一個行程不包含任何存活的用戶執行緒時,行程結束;
守護執行緒:守護用戶執行緒的,當最后一個用戶執行緒結束時,所有守護執行緒自動死亡,
public static void main(String[] args){
Thread t1 = new Thread(new MyRunnable());
t1.setDaemon(true);//設定t1為守護執行緒
t1.start();
for(int i=1;i<=5;i++){
System.out.println(Thread.currentThread().getName()+":"+i);
try{
Thread.sleep(1000);
} catch(InterruptedException e) {
e.printStackTrace();
}
}
}
static class MyRunnable implements Runnable{
@Override
public void run(){
for(int i=0;i<10;i++){
System.out.println(Thread.currentThread().getName()+":"+i);
try{
Thread.sleep(1000);
} catch(InterruptedException e) {
e.printStackTrace();
}
}
}
}

執行緒安全問題
public static void main(String[] args){
//執行緒不安全
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(){
wile(count>0){
System.out.println("正在準備賣票");
try{
Thread.sleep(1000);
} catch(InterruptedException e) {
e.printStackTrace();
}
count--;
System.out.println("出票成功,余票:"+count);
}
}
}//三個執行緒會有可能會同時進入回圈,同時呼叫count,從而造成count<0的情況發生

synchronized(執行緒同步)
執行緒同步有三種方式,三種加鎖的方式,
1、同步代碼塊
格式:synchronized(鎖物件){ }
Java中任何物件都可以作為鎖物件存在,多個執行緒應該搶一把鎖,而不是一個執行緒一把鎖,這樣就不會出現同時操作造成資料混亂的情況了,因為都是排隊執行代碼塊里的代碼,
public static void main(String[] args){
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;
Object o = new Object();
@Override
public void run(){
//Object o = new Object();//注意:如果鎖宣告在這里,那三個執行緒呼叫run方法都會重新定義一把鎖,那就還是同時執行,不是排隊執行了,
while(true){
synchronized(o){//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;
}
}
}
}
}

2、同步方法
如果不是靜態的(static)的同步方法,它的鎖物件為this;如果是靜態的同步方法,它的鎖物件為類名.class,比如下方的例子中,如果是靜態的,則鎖物件為Ticket.class
public static void main(String[] args){
Runnable run = new Ticket();
new Thread(run).start();
new Thread(run).start();
new Thread(run).start();
//new Thread(new Ticket()).start()//這里不是同一把鎖,所以是同時執行(異步),不是排隊執行(同步)了,
}
static class Ticket implements Runnable{
private int count = 10;
@Override
public void run(){
//synchronized(){ }//如果這里同步代碼塊和同步方法同時用了一把鎖,那么一個執行緒執行同步方法活同步代碼塊時,其他執行緒對同步方法和同步代碼塊都不能執行,就好比商店里的試衣間,大門和試衣間的門用的是同一把鎖,那么只要有一個人(執行緒)在使用其中的一個試衣間,那么其他人連大門都進不去,只能等待里面的人用完試衣間把鎖打開,
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;
} else {
return false;
}
}
}

3、顯示鎖(Lock)
同步代碼塊和同步方法都屬于隱式鎖,
顯示鎖Lock子類ReentrantLock
public static void main(String[] args){
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 Lock l = new ReentrantLock();//顯示鎖
@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();
}
}
}

公平鎖和非公平鎖
公平鎖就是排隊時誰先來,誰就先用這個鎖,
非公平鎖就是排隊時搶著來,誰先搶到誰就用,上述Java執行緒同步的三種方式就是非公平鎖,
在顯示鎖中定義時,建構式的第一個引數設為true就是公平鎖,false就是不公平鎖,例如:
private Lock l = new ReentrantLock(true)//fair引數設為true,就表示公平鎖
執行緒死鎖
兩個執行緒相互等待對方釋放鎖就造成了死鎖,比如某商城有A、B兩個試衣間,客戶甲進入了A試衣間,客戶乙進入了B試衣間,但是客戶甲覺得A試衣間不好用,想換到B試衣間,所以客戶甲在A試衣間中等客戶乙用完B試衣間出來,巧的是客戶已也覺得B試衣間不好用,也想換到A試衣間,所以客戶乙也再等客戶甲用完A試衣間出來,雙方都不知道對方都在等自己用完試衣間出來,而造成的一種相互等待的狀態,就叫做死鎖,
public static void main(String[] args){
Culprit c = new Culprit();
Police p = new Police();
new MyThread(c,p).start();
c.say(p);
//在這里主執行緒和MyThread執行緒同時執行,主執行緒拿到了罪犯類里的同步方法鎖,只有主執行緒能呼叫罪犯類里的同步方法,而MyThread執行緒拿到了警察類里的同步方法的鎖,只有MyThread執行緒能呼叫警察類里的同步方法,這樣,主執行緒就需要等待MyThread執行緒釋放所占用的鎖,而MyThread執行緒也同樣需要等待主執行緒釋放所占用的鎖,這樣就造成了兩執行緒相互等待的狀態,從而形成了死鎖,
//如果想要兩個執行緒不造成死鎖,那么就需要在其中一個執行緒還沒有搶占到鎖時,程式就執行完畢,這樣才不會造成死鎖,
//避免死鎖的方法就是,你已經呼叫了一個同步方法,就不要再繼續呼叫其他同步方法了,避免造成死鎖,
}
static class MyThread extends Thread{
private Culprit c;
private Police p;
public MyThread(Culprit c,Police p){
this.c = c;
this.p = p;
}
@Override
public void run(){
p.say(c);
}
}
//罪犯
static class Culprit{
public synchronized void say(Police p){//說
System.out.println("罪犯:你放了我,我放了人質");
p.response();
}
public synchronized void response(){//回應
System.out.println("罪犯被放了,罪犯也放了人質");
}
}
//警察
static class Police{
public synchronized void say(Culprit c){
System.out.println("警察:你放了人質,我放了你");//警察說了話,需要罪犯的回應
c.response();
}
public synchronized void response(){
System.out.println("警察救到了人質,但是罪犯跑了")
}
}
出現死鎖的情況:

沒有出現死鎖的情況:

多執行緒通信問題
Object類里的notify()、notifyAll()、wait()方法,
生產者與消費者
就像廚師和服務員,廚師在做飯的時候,服務員是在休息的,等廚師做好了,服務員被喚醒,而廚師進入休息狀態,
確保生產者在生產時消費者沒有進行消費,
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);
}
}

造成上述錯亂的原因是廚師做食物Food的時候剛給食物賦名字name,就被服務員端走了,而味道taste有可能是上一個物件遺留的,這就是兩個執行緒合作時出現的不協調現象,
而如果把食物的兩個方法定義成同步方法,會造成更加錯亂的現象,有可能,廚師做完了第一頓飯,有可能服務員還沒來得及端,廚師就做完了第二頓飯,那么第一頓飯就沒了,即廚師做飯做快了,或者服務員端菜端快了,
解決方法:廚師干活的時候,服務員歇著;廚師歇著的時候,服務員干活
static class Food{
private String name;
private String taste;
private boolean flag = true;//飯菜是否做好
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();//喚醒在當前this下睡著的所有執行緒
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();
}
}
}
}

執行緒的六種狀態
執行緒狀態,執行緒可以處于以下狀態之一:
NEW
尚未啟動的執行緒處于此狀態,RUNNABL
在Java虛擬機中執行的執行緒處于此狀態,BLOCKED
被阻塞等待監視器鎖定的執行緒處于此狀態,WAITING
無限期等待另一個執行緒執行特定操作的執行緒處于此狀態,TIMED_WAITING
正在等待另一個執行緒執行最多指定等待時間的操作的執行緒處于此狀態,TERMINATED
已退出的執行緒處于此狀態,
執行緒在給定時間點只能處于一種狀態, 這些狀態是虛擬機狀態,不反映任何作業系統執行緒狀態,

特殊的創建方法
Interface Callable
有兩種用法:1、和主執行緒一起運行,和執行緒一樣;2、主執行緒等待其完成
Callable和Runnable的區別
介面定義:
public interface Callable<V> {
V call() throws Exception;
}
public interface Runnable {
public abstract void run();
}
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();
Runnable與Callable的相同點
- 都是介面
- 都可以撰寫多執行緒程式
- 都采用Thread.start()啟動執行緒
Runnable與Callable的不同點
- Runnable沒有回傳值;Callable可以回傳執行結果
- Callable介面的call()允許拋出例外;Runnable的run()不能拋出
public static void main(String[] args){
Callable<Integer> c = new MyCallable();
FutureTask<Integer> task = new FutureTask<>(c);
new Thread(task).start();
Integer j = task.get();//呼叫get方法會導致主執行緒停在這里,等待MyCallable執行緒執行完畢
//task.cancel(true);//取消執行緒
System.out.println("回傳值:"+j);
for(int i=0;i<10;i++){
try{
Thread.sleep(100);
} catch(InterruptedException e) {
e.printStackTrace();
}
System.out.println(i);
}
}
static class MyCallable implements Callable<Integer> {
@Override
public Integer call() throws Exception {
//Thread.sleep(3000);
for(int i=0;i<10;i++){
try{
Thread.sleep(100);
} catch(InterruptedException e) {
e.printStackTrace();
}
System.out.println(i);
}
return 100;
}
}
執行緒池概述
執行緒操作流程:創建執行緒 -> 創建任務 -> 執行任務 -> 關閉執行緒
其中最耗費時間的是創建執行緒和關閉執行緒,
執行緒池Executors
如果并發的執行緒數量很多,并且每個執行緒都是執行一個時間很短的任務就結束了,這樣頻繁創建執行緒就會大大降低系統的效率,因為頻繁創建執行緒和銷毀執行緒需要時間,執行緒池就是一個容納多個執行緒的容器,池中的執行緒可以反復使用,省去了頻繁創建執行緒物件的操作,節省了大量的時間和資源,
執行緒池的好處
- 降低資源的消耗;
- 提高回應速度;
- 提高執行緒的可管理性;
執行緒池原理圖

Java中的四種執行緒池 .ExecutorService
1、快取執行緒池
特點:長度無限制,
執行流程:
- 判斷執行緒池是否存在空閑執行緒
- 存在則使用
- 不存在,則創建執行緒并放入執行緒池,然后使用
public static void main(String[] args){
ExecutorService service = Executor.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、定長執行緒池
特點:長度是指定的數值
執行流程:
- 判斷執行緒池是否存在空閑執行緒
- 存在則使用
- 不存在空閑執行緒,且執行緒未滿的情況下,則創建執行緒,并放入執行緒池,然后使用
- 不存在空閑執行緒,且執行緒已滿的情況下,則等待執行緒池存在空閑執行緒
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());
}
});
service.execute(new Runnable() {
@Override
public void run() {
System.out.println("執行緒的名稱:"+Thread.currentThread().getName());
}
});
}
3、單執行緒執行緒池
特點:效果與定長執行緒池創建時傳入數值1效果一樣,
執行流程:
- 判斷執行緒池的那個執行緒是否空閑
- 空閑則使用
- 不空閑,則等待池中的單個執行緒空閑后使用
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());
}
});
}
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,1,TimeUnit.SECONDS);
Lambda運算式
函式式編程思想
面向物件:創建物件呼叫方法解決問題,
Lambda運算式關注的是物件的方法,
//冗余的Runnable代碼
public static void main(String[] args) {
MyRunnable r = new MyRunnable();
Thread t = new Thread(r);
t.start();
}
public static class MyRunnable implements Runnable {
@Override
public void run() {
System.out.println("鋤禾日當午");
}
}
//改善之后的Runnable代碼
public static void main(String[] args) {
Thread t = new Thread(new Runnable() {
@Override
public void run() {
System.out.println("鋤禾日當午");
}
});
t.start();
}
//使用Lambda運算式改善Runnable代碼
public static void main(String[] args) {
Thread t = new Thread(() -> System.out.println("鋤禾日當午"));//效果和上述代碼一致
//Thread t = new Thread(() -> {System.out.println("鋤禾日當午");});
t.start();
}
//自定義介面測驗
public static void main(String[] args) {
/*print(new MyMath() {
@Override
public int sum(int x,int y) {
return x+y;
}
},100,200);*/
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);
}
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/261336.html
標籤:其他
上一篇:人工智能在網路安全中的攻擊和防御
