7月20日知識點(diǎn)
今天的主要內(nèi)容——線程
-
線程
-
線程的基本概念
- 線程與進(jìn)程的區(qū)別
-
線程的兩種創(chuàng)建方式(掌握)
注意線程兩種創(chuàng)建方式的區(qū)別
內(nèi)部類實(shí)現(xiàn)線程創(chuàng)建
線程的第三種創(chuàng)建方式——實(shí)現(xiàn)Callable接口
-
Thread類中的方法使用
- getName()
- setName()
- currentthread()
- sleep
- join
- wait
- yield
- setDaemon
-
線程的同步(掌握)
同步代碼塊
同步方法
多線程(線程安全問題)(掌握)
線程的死鎖(了解)
多線程和隊(duì)列實(shí)現(xiàn)買賣票(掌握)
-
-
Lambda表達(dá)式
- lambda表達(dá)式的幾種方式
- lambda表達(dá)式用途
線程
1. 線程的基本概念
- image
-
1.什么是線程
- 線程是程序執(zhí)行的一條路徑, 一個(gè)進(jìn)程中可以包含多條線程
- 多線程并發(fā)執(zhí)行可以提高程序的效率, 可以同時(shí)完成多項(xiàng)工作
進(jìn)程:其實(shí)是一個(gè)靜態(tài)的概念
* 我們平時(shí)所說的進(jìn)程開始執(zhí)行,指的是:進(jìn)程中主線程開始執(zhí)行了,即main方法開始執(zhí)行了計(jì)算機(jī)中實(shí)際運(yùn)行的都是線程。
操作系統(tǒng)是支持多線程,多進(jìn)程的,系統(tǒng)是同時(shí)執(zhí)行多個(gè)線程的
CPU的執(zhí)行:交替執(zhí)行眾多線程,因?yàn)樗俣瓤欤砻嫔鲜峭瑫r(shí)執(zhí)行多個(gè)線程,其實(shí)不是的。
注:
① Java的線程是通過java.lang.Thread類來實(shí)現(xiàn)的
② VM啟動時(shí)會有一個(gè)主方法(main方法)所定義的線程
③ 可通過創(chuàng)建Thread的實(shí)例來創(chuàng)建新的線程
④ 每個(gè)線程都是通過某個(gè)特定的Thread對象所對應(yīng)的run方法來完成其操作的,方法run()稱為線程體
⑤ 通過調(diào)用Thread類中的start()方法來啟動一個(gè)線程。
2. 多線程(多線程并行和并發(fā)的區(qū)別)(了解)
- 并行就是兩個(gè)任務(wù)同時(shí)運(yùn)行,就是甲任務(wù)進(jìn)行的同時(shí),乙任務(wù)也在進(jìn)行。(需要多核CPU)
- 并發(fā)是指兩個(gè)任務(wù)都請求運(yùn)行,而處理器只能按受一個(gè)任務(wù),就把這兩個(gè)任務(wù)安排輪流進(jìn)行,由于時(shí)間間隔較短,使人感覺兩個(gè)任務(wù)都在運(yùn)行。
- 比如我跟兩個(gè)網(wǎng)友聊天,左手操作一個(gè)電腦跟甲聊,同時(shí)右手用另一臺電腦跟乙聊天,這就叫并行。
- 如果用一臺電腦我先給甲發(fā)個(gè)消息,然后立刻再給乙發(fā)消息,然后再跟甲聊,再跟乙聊。這就叫并發(fā)。
3. 多線程(Java程序運(yùn)行原理和JVM的啟動是多線程的嗎)(了解)
-
A:Java程序運(yùn)行原理
- Java命令會啟動java虛擬機(jī),啟動JVM,等于啟動了一個(gè)應(yīng)用程序,也就是啟動了一個(gè)進(jìn)程。該進(jìn)程會自動啟動一個(gè) “主線程” ,然后主線程去調(diào)用某個(gè)類的 main 方法。
-
B:JVM的啟動是多線程的嗎
JVM啟動至少啟動了垃圾回收線程和主線程,所以是多線程的。
-
運(yùn)行程序證明JVM的多線程
public class TestJVMMultiThread { public static void main(String[] args) { //創(chuàng)建垃圾對象觀察垃圾回收線程 for(int i = 0;i < 100;i++) { new Test(); } for(int i = 0;i < 100;i++) { System.out.println("主線程在執(zhí)行"); } } } class Test{ @Override public void finalize(){ System.out.println("垃圾回收線程在執(zhí)行"); } }
4. 線程的兩種創(chuàng)建方式




3. 線程兩種創(chuàng)建方式程序
-
創(chuàng)建線程程序1:
imageimage-
用lambda表達(dá)式簡寫線程啟動過程
public class TestLambda { public static void main(String[] args) { System.out.println("哈哈哈哈我在測試啊"); Runnable runner = ()->{ StringBuffer s = new StringBuffer(); for(int i= 0;i < 10;i++) System.out.println(s.append("haha")); }; new Thread(runner).start(); //注意:同一個(gè)線程不可以啟動多次 System.out.println("任務(wù)完成"); } } /* * 在JDK1.8中輸出結(jié)果為: * ------------------------------------------ * 哈哈哈哈我在測試啊 任務(wù)完成 haha hahahaha hahahahahaha hahahahahahahaha hahahahahahahahahaha hahahahahahahahahahahaha hahahahahahahahahahahahahaha hahahahahahahahahahahahahahahaha hahahahahahahahahahahahahahahahahaha hahahahahahahahahahahahahahahahahahahaha ------------------------------------------ * */
-
-
創(chuàng)建線程程序2:
image- 此時(shí)Runner線程和main線程交替運(yùn)行
* 能使用接口實(shí)現(xiàn)線程就不要用繼承,因?yàn)橛昧私涌谝院筮€可以繼承和實(shí)現(xiàn)其它接口,而如果只繼承會比較死板
4. 創(chuàng)建線程的兩種方式的區(qū)別(掌握)
- 查看源碼的區(qū)別:
- a.繼承Thread : 由于子類重寫了Thread類的run(), 當(dāng)調(diào)用start()時(shí), 直接找子類的run()方法
- b.實(shí)現(xiàn)Runnable : 構(gòu)造函數(shù)中傳入了Runnable的引用, 成員變量記住了它, start()調(diào)用run()方法時(shí)內(nèi)部判斷成員變量Runnable的引用是否為空, 不為空編譯時(shí)看的是Runnable的run(),運(yùn)行時(shí)執(zhí)行的是子類的run()方法
- Thread類中的run()源碼為:
public void run() {
if (target != null) {
target.run();
}
}
5. 匿名內(nèi)部類實(shí)現(xiàn)線程
-
繼承Thread類
new Thread() { //1.繼承Thread類 public void run(){ //2.重寫run方法 for(int i = 0;i < 1000;i++) { System.out.println("aaaaaa"); } } }.start(); //3.開啟線程 -
實(shí)現(xiàn)Runnable接口
new Thread(new Runnable() { //1.將Runnable子類對象傳遞給Thread構(gòu)造方法 public void run(){ //2.重寫run方法 for(int i = 0;i < 1000;i++) { System.out.println("bb"); } } }).start(); //3.開啟線程
6. 創(chuàng)建線程的兩種方式的區(qū)別(掌握)
- 繼承Thread
- 好處是:可以直接使用Thread類中的方法,代碼簡單
* 弊端是:如果已經(jīng)有了父類,就不能用這種方法
- 實(shí)現(xiàn)Runnable接口
- 好處是:即使自己定義的線程類有了父類也沒關(guān)系,因?yàn)橛辛烁割愐部梢詫?shí)現(xiàn)接口,而且接口是可以多實(shí)現(xiàn)的
* 弊端是:不能直接使用Thread中的方法需要先獲取到線程對象后,才能得到Thread的方法,代碼復(fù)雜
7. 線程的第三種創(chuàng)建方式
-
使用Callable接口_java.util.concurrent
image- Callable接口與Runnable接口的區(qū)別:
- Runnable沒有返回值一說,而且run()方法并不拋出異常
- Callable中的call方法具有返回值
- Callable之所以有返回值,也是因?yàn)閷?shí)現(xiàn)了泛型,而Runnable接口不存在泛型
- Callable接口與Runnable接口的區(qū)別:
7.1 FutureTask類的學(xué)習(xí)——java.util.concurrent.FutureTask<V>

- FutureTask的構(gòu)造器

此時(shí)可理解為:FutureTask的作用就是接收一個(gè)實(shí)現(xiàn)了Callable接口的實(shí)現(xiàn)類對象
-
此時(shí)注意到FutureTask實(shí)現(xiàn)了Runnable接口
- 也就是說可以把FutureTask的實(shí)現(xiàn)類對象傳入到Runnable類型
- 而Thread類中的構(gòu)造器的參數(shù)為Runnable類型
-
因此梳理后可知
①先實(shí)現(xiàn)Callable接口
class myThread_3 implements Callable<String>{ @Override public String call() throws Exception { return "線程創(chuàng)建成功"; } }②將Callable實(shí)現(xiàn)類對象傳給FutureTask構(gòu)造器
FutureTask<String> ft = new FutureTask<>(new myThread_3()); * 因?yàn)镕utureTask實(shí)現(xiàn)了Runnable接口,因此以下代碼也正確 Runnable runner = new FutureTask<String>(new myThread_3()); * 接口引用指向?qū)崿F(xiàn)類對象 * 但是此時(shí)runner只能訪問Runnable中方法,不可以訪問FutureTask中的方法③創(chuàng)建線程并啟動
new Thread(ft).start();④使用FutureTask類中方法
public V get() throws InterruptedException,ExecutionException 通過Thread啟動線程之后 可以通過FutureTask存在get方法可以取得call()方法中的返回值 /*用到Callable有什么用?只是為了返回一個(gè)值?執(zhí)行線程的代碼run用什么代替?*/ /*回答:此時(shí)線程啟動start()方法調(diào)用的是call()方法,將需要執(zhí)行的代碼放到call()方法中,同時(shí)call()方法的特點(diǎn)在于有返回值*/ -
程序
public class TestCallable{ public static void main(String[] args) { FutureTask<String> ft = new FutureTask<String>(new myThread_3()); new Thread(ft).start(); //此時(shí)的啟動需要調(diào)用的是call()方法 String result = null; try { result = ft.get(); } catch (InterruptedException | ExecutionException e) { e.printStackTrace(); } System.out.println(result); } } class myThread_3 implements Callable<String>{ @Override public String call() throws Exception { System.out.println("此時(shí)start()方法調(diào)用的是call()方法"); return "線程創(chuàng)建成功"; } } /* 在JDK1.8中輸出結(jié)果為: * ---------------------------- * 此時(shí)start()方法調(diào)用的是call()方法 線程創(chuàng)建成功 ---------------------------- * */
8. 多線程(獲取名字和設(shè)置名字)(掌握)
-
獲取名字
-
通過getName()方法獲取線程對象的名字
public final String getName();
-
-
設(shè)置名字
-
通過構(gòu)造函數(shù)可以傳入String類型的名字
public Thread(String name) public Thread(Runnable target,String name) -
通過setName(String)方法可以設(shè)置線程對象的名字
public final void setName(String name);
-
-
程序?qū)崿F(xiàn)
new Thread("線程一") { //設(shè)置線程名字 public void run() { for(int i = 0;i < 100;i++) { System.out.println(this.getName() + "....aaaa"); //獲取線程名字 } } }.start(); new Thread("線程二") { //設(shè)置線程名字 public void run() { for(int i = 0;i < 100;i++) { System.out.println(this.getName() + "....bb"); } } }.start(); new Thread() { public void run() { this.setName("線程3"); //設(shè)置線程名字 for(int i = 0;i < 100;i++) { System.out.println(this.getName() + "....哈哈"); } } }.start();
9. 多線程(獲取當(dāng)前線程的對象)(掌握)
public static Thread currentThread()
Thread.currentthread();——注意返回值是Thread
//匿名內(nèi)部類實(shí)現(xiàn)當(dāng)前線程對象的獲取
new Thread() {
public void run() {
this.setName("線程A");
for(int i = 0;i < 1000;i++) {
System.out.println( Thread.currentThread().getName() + "....aaaa");
}
}
}.start();
new Thread(new Runnable() {
public void run() {
for(int i = 0;i < 1000;i++) {
System.out.println( Thread.currentThread().getName() + "....bb");
}
}
},"線程B").start();
}
10. 多線程(休眠線程)(掌握)
-
public static void sleep(long millis) throws InterruptedException
- 拋出InterruptionException
public class TestSleepOfThread {
public static void main(String[] args) {
Runnable r = ()->{
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Thread A");
};
new Thread(r).start();
System.out.println("main Thread");
}
}
/*
* 在JDK1.8中輸出結(jié)果為:
* -------------
* main Thread
Thread A
-------------
* */
11. 多線程(守護(hù)線程)(掌握)
setDaemon(), 設(shè)置一個(gè)線程為守護(hù)線程, 該線程不會單獨(dú)執(zhí)行, 當(dāng)其他非守護(hù)線程都執(zhí)行結(jié)束后, 自動退出
-
public final void setDaemon(boolean on)
-
on - if true, marks this thread as a daemon thread
public class TestSetDaemon { public static void main(String[] args) { Thread t1 = new Thread("線程A") { public void run() { for(int i = 0;i < 2;i++) { System.out.println(Thread.currentThread().getName() + "....aaaa"); } } }; Thread t2 = new Thread(new Runnable() { public void run() { for(int i = 0;i < 50;i++) { System.out.println(Thread.currentThread().getName() + "....bb"); } } },"線程B"); t2.setDaemon(true); //將t2設(shè)置為守護(hù)線程 t1.start(); t2.start(); } } /* * 在JDK1.8中輸出結(jié)果為: * ------------------------ * 線程A....aaaa 線程B....bb 線程A....aaaa 線程B....bb //存在時(shí)間緩沖問題 線程B....bb 線程B....bb 線程B....bb 線程B....bb 線程B....bb 線程B....bb * */
-
12. 多線程(加入線程)(掌握)
join(), 當(dāng)前線程暫停, 等待指定的線程執(zhí)行結(jié)束后, 當(dāng)前線程再繼續(xù)
join(int), 可以等待指定的毫秒之后繼續(xù)
-
public final void join() throws InterruptedException
public class TestJoin { public static void main(String[] args) { Thread t1 = new Thread("線程A") { public void run() { for(int i = 0;i < 100;i++) { System.out.println(getName() + "....aaaaaaa"); } } }; Runnable r = ()->{ for(int i = 0;i < 50;i++) { if(i == 2) { try { t1.join(); //t1.join(1000); t1插隊(duì)執(zhí)行1秒后,t1和t2線程再搶占CPU交替執(zhí)行 } catch (InterruptedException e) { e.printStackTrace(); } } System.out.println(Thread.currentThread().getName() + "....bb"); } }; t1.start(); new Thread(r).start(); } }
13. 多線程(禮讓線程)(了解)
- yield讓出cpu
14. 多線程(設(shè)置線程的優(yōu)先級)(了解)
- setPriority()設(shè)置線程的優(yōu)先級
15. 多線程(同步代碼塊)(掌握)
- 1.什么情況下需要同步
- 當(dāng)多線程并發(fā), 有多段代碼同時(shí)執(zhí)行時(shí), 我們希望某一段代碼執(zhí)行的過程中CPU不要切換到其他線程工作. 這時(shí)就需要同步.
- 如果兩段代碼是同步的, 那么同一時(shí)間只能執(zhí)行一段, 在一段代碼沒執(zhí)行結(jié)束之前, 不會執(zhí)行另外一段代碼.
- 2.同步代碼塊
使用synchronized關(guān)鍵字加上一個(gè)鎖對象來定義一段代碼, 這就叫同步代碼塊
鎖對象不可以用匿名對象,因?yàn)槟涿麑ο蟛皇峭粋€(gè)對象
-
多個(gè)同步代碼塊如果使用相同的鎖對象, 那么他們就是同步的
public class TestTickets_2 { public static void main(String[] args) { MyThread_6 myt = new MyThread_6(); new Thread(myt,"線程A").start(); new Thread(myt,"線程B").start(); } } class MyThread_6 implements Runnable{ private int ticket = 5; @Override public void run() { while(true) { if(this.ticket > 0) { System.out.println(Thread.currentThread().getName() + "開始賣票 = " + ticket--); } else { System.out.println("票已經(jīng)賣完"); break; } } } }
16. 多線程(同步方法)(掌握)
public class TestSynchronized_2 {
public static void main(String[] args) {
Printer_2 p = new Printer_2();
Runnable r = ()->{
for(int i = 0;i < 100;i++) {
p.print1();
}
};
Thread t2 = new Thread() {
public void run() {
for(int i = 0;i < 100;i++) {
p.print2();
}
}
};
new Thread(r).start();
t2.start();
}
}
class Printer_2{
public synchronized void print1() {
System.out.print("早");
System.out.print("上");
System.out.print("好");
System.out.print("啊");
System.out.print("\r\n");
}
public void print2() {
synchronized(this) {
System.out.print("河");
System.out.print("正");
System.out.print("宇");
System.out.print("好");
System.out.print("帥");
System.out.print("\r\n");
}
}
}
17. 多線程(線程安全問題)(掌握)
多線程并發(fā)操作同一數(shù)據(jù)時(shí), 就有可能出現(xiàn)線程安全問題
-
使用同步技術(shù)可以解決這種問題, 把操作數(shù)據(jù)的代碼進(jìn)行同步, 不要多個(gè)線程一起操作
/* * 鐵路售票,一共100張,通過四個(gè)窗口賣完 * */ public class TestSynchronized_3 { public static void main(String[] args) { new Ticket().start(); new Ticket().start(); new Ticket().start(); } } class Ticket extends Thread{ private static int ticket = 100; public void run() { while(true) { //synchronized(this) synchronized(Ticket.class) { if(ticket == 0) { break; } System.out.println(getName() + "這是第" + ticket-- + "號票"); } } } }
18. 多線程(火車站賣票的例子用實(shí)現(xiàn)Runnable接口)(掌握)
public class TestSynchronized_4 {
public static void main(String[] args) {
Ticket_2 t = new Ticket_2();
new Thread(t,"線程A").start();
new Thread(t,"線程B").start();
new Thread(t,"線程C").start();
new Thread(t,"線程D").start();
}
}
class Ticket_2 implements Runnable{
private int ticket = 100; //此時(shí)ticket不需要設(shè)置為共享,因?yàn)榫鶠橥粚ο?
@Override
public void run() {
while(true) {
synchronized(this) {
if(ticket == 0) {
break;
}
System.out.println(Thread.currentThread().getName() + " 這是第" + ticket-- + "號票");
}
}
}
}
19. 多線程(死鎖)(了解)
public class TestDeadLock {
private static Object o1 = new Object();
private static Object o2 = new Object();
public static void main(String[] args) {
new Thread("線程A") {
public void run() {
while(true) {
synchronized(o1) {
System.out.println(getName() + "正在使用o1,等待o2");
synchronized(o2) {
System.out.println(getName() +"等到o2,執(zhí)行成功");
}
}
}
}
}.start();
new Thread("線程B") {
public void run() {
while(true) {
synchronized(o2) {
System.out.println(getName() + "正在使用o2,等待o1");
synchronized(o1) {
System.out.println(getName() + "等到o1,執(zhí)行成功");
}
}
}
}
}.start();
}
}
/*
* 在JDK1.8中輸出結(jié)果為:
* ----------------------
線程A正在使用o1,等待o2
線程A等到o2,執(zhí)行成功
線程A正在使用o1,等待o2
線程B正在使用o2,等待o1
-------------------------
* */
- 因此:synchronized不要嵌套使用,容易出錯
20. 多線程和隊(duì)列實(shí)現(xiàn)買賣票
Queue接口
public interface Queue {
public void append(Object obj)throws Exception;
public Object delete()throws Exception;
public Object getFront()throws Exception;
public boolean isEmpty();
}
Class MyQueue
public class MyQueue implements Queue{
//1 設(shè)置隊(duì)列的默認(rèn)長度
static final int DEFAULT_SIZE=10; //默認(rèn)長度為10
//2 設(shè)置隊(duì)頭
int front;
//3 設(shè)置隊(duì)尾
int rear;
//4 定義統(tǒng)計(jì)元素的變量
int count;
//5 隊(duì)的最大長度
int maxSize;
Object[] queue; //設(shè)置隊(duì)列
//空構(gòu)造
public MyQueue() {
this.init(DEFAULT_SIZE); //用戶給定長度 默認(rèn)長度為10
}
//有參數(shù)的構(gòu)造
public MyQueue(int size) {
this.init(size); //開辟用戶給定的長度
}
/**
* 初始化方法
* @param size
*/
public void init(int size) {
//初始化屬性
this.maxSize=size; //外部傳進(jìn)來的size
//空隊(duì)列
front=rear=0;
count=0;
queue=new Object[size];
}
@Override
public void append(Object obj) throws Exception {
// TODO Auto-generated method stub
//首先隊(duì)列是否已滿
if(count>0&&front==rear) { //判斷隊(duì)列是否已滿
throw new Exception("隊(duì)列已滿");
}
this.queue[rear]=obj;
rear=(rear+1)%maxSize;
count++;
}
@Override
public Object delete() throws Exception {
// TODO Auto-generated method stub
if(this.isEmpty()) {
throw new Exception("隊(duì)列為空隊(duì)");
}
Object obj=this.queue[front];
front=(front+1)%maxSize;
count--;
return obj;
}
@Override
public Object getFront() throws Exception {
// TODO Auto-generated method stub
if(!this.isEmpty()) {
return this.queue[front];
}
return null;
}
@Override
public boolean isEmpty() {
// TODO Auto-generated method stub
return this.count==0;
}
}
Class WindowQueue
public class WindowQueue { //賣票的窗口
//定義賣票隊(duì)列
int maxSize=10;
MyQueue queue=new MyQueue(maxSize);
int num=0; //最多賣100張票
boolean flag=true ; //判斷是否繼續(xù)賣票
//排隊(duì)買票
public synchronized void producer()throws Exception{
if(this.queue.count<maxSize) {
this.queue.append(num++); //等待買票的數(shù)量++
System.out.println("第"+num+"個(gè)客戶排隊(duì)買票");
this.notifyAll(); //喚醒等待的線程
}else {
System.out.println("隊(duì)列已滿 請等待");
this.wait();
}
}
//賣票
public synchronized void consumer()throws Exception{
if(this.queue.count>0) {
Object obj=this.queue.delete(); //出隊(duì)
int temp=Integer.parseInt(obj.toString());
System.out.println("第"+(temp+1)+"個(gè)客戶買到票離開隊(duì)列");
//如果當(dāng)前的隊(duì)列為空 并且賣出票數(shù)大于100
if(this.queue.isEmpty()&&this.num>=100) {
this.flag=false;
}
this.notifyAll(); //喚醒等待的線程
}else {
System.out.println("隊(duì)列已空 請等待");
this.wait();
}
}
}
Class Producer
public class Producer implements Runnable{
WindowQueue queue;
public Producer(WindowQueue queue) {
this.queue=queue;
}
@Override
public void run() {
// TODO Auto-generated method stub
while(queue.num<100) { //必須小于100張票 才可以買票
try {
Thread.sleep(1000);
queue.producer();
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
Class Consumer
public class Consumer implements Runnable{
WindowQueue queue;
public Consumer(WindowQueue queue) {
this.queue=queue;
}
@Override
public void run() {
// TODO Auto-generated method stub
while(queue.flag) { //如果隊(duì)列為空 并且票數(shù)大于100 就不會賣票了
try {
Thread.sleep(1000);
queue.consumer();
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
Class QueueTest
public class QueueTest {
public static void main(String[] args) throws Exception {
WindowQueue queue=new WindowQueue();
Producer p=new Producer(queue);
Consumer con=new Consumer(queue);
//以上的代碼一定要注意 傳入的是同一個(gè)對象
Thread t1=new Thread(p);
Thread t2=new Thread(con);
t1.start();
t2.start();
}
}
Lambda表達(dá)式
-
Lambda表達(dá)式的形式
參數(shù),箭頭(→)以及一個(gè)表示
Lambda適用于接口中一個(gè)抽象方法時(shí)候使用
-
舉例1——函數(shù)式接口使用
Runnable runner = ()->{ StringBuffer s = new StringBuffer(); for(int i= 0;i < 10;i++) System.out.println(s.append("haha")); }; -
舉例2——只有一個(gè)抽象方法的接口的實(shí)現(xiàn)類的使用
Thread t1 = new Thread(()->{ StringBuffer s = new StringBuffer(); for(int i= 0;i < 10;i++) System.out.println(s.append("haha")); }); -
區(qū)別于匿名內(nèi)部類——需要寫出方法聲明
new Thread(new Runnable() { public void run() { StringBuffer s = new StringBuffer(); for(int i= 0;i < 10;i++) System.out.println(s.append("haha")); } }).start(); Thread t1 = new Thread() { public void run() { StringBuffer s = new StringBuffer(); for(int i= 0;i < 10;i++) System.out.println(s.append("haha")); } };




