首先說一下在開發(fā)中為什么會(huì)有線程和線程池。
日常開發(fā)中,如果一個(gè)操作需要好幾個(gè)步完成,其中有一兩個(gè)步非同步或事務(wù)操作,比如記錄日志,那么為了提高響應(yīng)時(shí)間,可以把這一兩步單獨(dú)開一個(gè)線程,由新開的線程來完成。
為什么會(huì)有線程池呢?如果當(dāng)前并發(fā)巨大,同一時(shí)刻會(huì)啟動(dòng)多個(gè)線程來完成,那么隨著線程的增多,服務(wù)器的內(nèi)存最終會(huì)耗盡,因此需要有線程池來管理這些線程,設(shè)置線程池的容量,新開了線程,都放到線程池等待,有空余資源就處理這些線程。這樣就避免新開無數(shù)線程導(dǎo)致服務(wù)器資源耗盡的情況了。
新建一個(gè)線程
新建一個(gè)類FirstThread,該類實(shí)現(xiàn)Runnable接口,并重寫Run方法,則該類就可以做為一個(gè)線程實(shí)現(xiàn)類來使用。
代碼如下:
public class FirstThread implements Runnable {
private String username;//傳參數(shù)
public FirstThread(String username) {
this.username = username;
}
@Override
public void run() {
for (int i = 0; i < 10; i++) {
System.out.println(username + i);
}
}
}
線程啟動(dòng)
首先new 當(dāng)前的FirstThread,再new 一個(gè)Thread,執(zhí)行start方法即可,比如:
public static void main(String[] args) {
FirstThread ft1 = new FirstThread("張三");
FirstThread ft2 = new FirstThread("李四");
Thread t1 = new Thread(ft1);
Thread t2 = new Thread(ft2);
t1.start();
t2.start();
}
線程池
線程池原理如下:
1、Executors.newFixedThreadPool(10)初始化一個(gè)包含10個(gè)線程的線程池executor;
2、通過executor.execute方法提交20個(gè)任務(wù),每個(gè)任務(wù)打印當(dāng)前的線程名;
3、負(fù)責(zé)執(zhí)行任務(wù)的線程的生命周期都由Executor框架進(jìn)行管理;
4、線程池里最大有10個(gè)線程,如果超過10個(gè),其余的等待,執(zhí)行完后再進(jìn)入線程池進(jìn)行任務(wù)執(zhí)行。如果小于10個(gè),則直接全部執(zhí)行。
代碼如下:
/**
* Created by 孔垂云 on 2017/4/21.
*/
public class ExecutorTest {
private static Executor executor = Executors.newFixedThreadPool(10);//新建一個(gè)10個(gè)線程的線程池
/**
* 內(nèi)部類實(shí)現(xiàn)一個(gè)線程
*/
static class Task implements Runnable {
@Override
public void run() {
System.out.println(Thread.currentThread().getName());//打印當(dāng)前線程的名稱
try {
TimeUnit.SECONDS.sleep(new Random().nextInt(10));//暫停幾秒鐘
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public static void main(String[] args) {
for (int i = 0; i < 20; i++) {
executor.execute(new Task());
}
}
}
Future和Callable實(shí)現(xiàn)
/**
* Created by 孔垂云 on 2017/4/21.
*/
public class FutureTest {
private static ExecutorService executor = Executors.newFixedThreadPool(10);//定義10個(gè)線程的線程池
static class Task implements Callable<String> {
@Override
public String call() throws Exception {
TimeUnit.SECONDS.sleep(1);
return "Hello Future";
}
}
public static void main(String[] args) throws Exception {
Future<String> future = executor.submit(new Task());
System.out.println("開始執(zhí)行線程:");
String ret = future.get();//獲取該線程的返回值
System.out.println("執(zhí)行結(jié)果:" + ret);
System.out.println("執(zhí)行完畢");
executor.shutdown();
}
}
在實(shí)際業(yè)務(wù)場(chǎng)景中,F(xiàn)uture和Callable基本是成對(duì)出現(xiàn)的,Callable負(fù)責(zé)產(chǎn)生結(jié)果,F(xiàn)uture負(fù)責(zé)獲取結(jié)果。
1、Callable接口類似于Runnable,只是Runnable沒有返回值。
2、Callable任務(wù)除了返回正常結(jié)果之外,如果發(fā)生異常,該異常也會(huì)被返回,即Future可以拿到異步執(zhí)行任務(wù)各種結(jié)果;
3、Future.get方法會(huì)導(dǎo)致主線程阻塞,直到Callable任務(wù)執(zhí)行完成;
4、線程都執(zhí)行完畢后,執(zhí)行executor.shutdown();,關(guān)閉主進(jìn)程。