java實(shí)現(xiàn)/創(chuàng)建線程的幾種方式小結(jié)
進(jìn)程與線程
進(jìn)程可以簡(jiǎn)單理解成一個(gè)可執(zhí)行程序例如.exe,在Windows中的任務(wù)管理器中可以查看每一個(gè)進(jìn)程,進(jìn)程是一次程序的執(zhí)行,是程序在數(shù)據(jù)集合上運(yùn)行的過(guò)程,是系統(tǒng)資源調(diào)度的一個(gè)單位。進(jìn)程主要負(fù)責(zé)向操作系統(tǒng)申請(qǐng)資源。然而一個(gè)進(jìn)程中,多個(gè)線程可以共享進(jìn)程中相同的內(nèi)存或文件資源。線程就是一個(gè)進(jìn)程一個(gè)程序要完成所依賴的子任務(wù),這些子任務(wù)便可以看作是一個(gè)線程。
第一種方式繼承Thread類
從java源碼可以看出Thread類本質(zhì)上實(shí)現(xiàn)了Runnable接口的實(shí)例類,代表了線程的一個(gè)線程的實(shí)例,啟動(dòng)的線程唯一辦法就是通過(guò)Thread類調(diào)用start()方法,start()方法是需要本地操作系統(tǒng)的支持,它將啟動(dòng)一個(gè)新的線程,并且執(zhí)行run()方法。

繼承Thread類實(shí)現(xiàn)線程代碼如下
創(chuàng)建一個(gè)Thread類,對(duì)象直接調(diào)用run方法會(huì)出現(xiàn)什么問(wèn)題?
package cn.thread.線程;
public class MyThread extends Thread{
public MyThread(String name){
super(null,null,name);
}
int piao =10;
@Override
public void run() {
while(piao>0){
System.out.println(Thread.currentThread().getName()+"......"+piao--);
}
}
public static void main(String[] args) {
MyThread mt = new MyThread("x");
mt.run();
}
}
結(jié)果:
可以發(fā)現(xiàn)是主線程執(zhí)行了run方法,并不是用戶線程執(zhí)行的run方法,此時(shí)可以得出用戶線程并沒(méi)有啟動(dòng),所以并不會(huì)執(zhí)行run里面的方法,且執(zhí)行完run方法便結(jié)束線程。

第二種創(chuàng)建線程的方法,實(shí)現(xiàn)Runnable接口
相比繼承Thread類而言,實(shí)現(xiàn)接口的可擴(kuò)展性得到了提升,Runnable接口也必須要封裝到Thread類里面,才可以調(diào)用start方法,啟動(dòng)線程。
實(shí)現(xiàn)代碼
package cn.thread.線程;
public class MyRunnable implements Runnable{
int piao = 10;
@Override
public void run() {
while(piao>0){
System.out.println(Thread.currentThread().getName()+"-----"+piao--);
}
}
public static void main(String[] args) {
Runnable r =new MyRunnable();
Thread t =new Thread(r);
t.start();
}
}
結(jié)果

第三種創(chuàng)建線程的方法實(shí)現(xiàn)Callable接口
Callable接口使用方法和Runnable接口的方法類似不同的一點(diǎn)是Callable接口具有返回值,返回結(jié)果并且可能拋出異常的任務(wù)。實(shí)現(xiàn)者定義了一個(gè)不帶任何參數(shù)的叫做 call 的方法。 Callable 接口類似于 Runnable,兩者都是為那些其實(shí)例可能被另一個(gè)線程執(zhí)行的類設(shè)計(jì)的。但是 Runnable 不會(huì)返回結(jié)果,并且無(wú)法拋出經(jīng)過(guò)檢查的異常。
實(shí)現(xiàn)代碼
對(duì)下列代碼進(jìn)行分析
首先callable是接口不能直接創(chuàng)建對(duì)象,也不能創(chuàng)建線程。并且要實(shí)現(xiàn)call方法類似run方法的功能,call方法有返回值,會(huì)計(jì)算結(jié)果,如果無(wú)法計(jì)算結(jié)果,則拋出一個(gè)異常。
執(zhí)行callable任務(wù)之后,可以獲得一個(gè)future的對(duì)象,future基本上是主線程可以跟蹤進(jìn)度以及獲取其他線程結(jié)果的一種方式。在這里Test1()方法主要利用線程池和future的方法,去啟動(dòng)實(shí)現(xiàn)Callable接口的線程,具有返回值。而Test2()主要是采用FutureTask類去實(shí)現(xiàn)創(chuàng)建一個(gè)實(shí)現(xiàn)callable接口的線程,futuretask實(shí)現(xiàn)了future接口。
package com.openlab.test;
import java.util.Random;
import java.util.concurrent.Callable;
public class CallableTest implements Callable{
@Override
public Object call() throws Exception {
Random generator = new Random();
Integer randomNumber = generator.nextInt(5);
Thread.sleep(randomNumber*1000);
return randomNumber;
}
}
綜合練習(xí)代碼:
package cn.thread.線程;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Random;
import java.util.concurrent.*;
public class CallableTest implements Callable<Object> {//不能直接創(chuàng)建線程
int taskNum;
public CallableTest(int taskNum){
this.taskNum = taskNum;
}
@Override
public Object call() throws Exception {
System.out.println(">>>"+taskNum+"任務(wù)啟動(dòng)");
Date dataTemp = new Date();
Thread.sleep(1000);
Date dataTemp2 = new Date();
long time = dataTemp2.getTime() - dataTemp.getTime();
System.out.println(">>>>"+taskNum+"任務(wù)終止");
return taskNum+"任務(wù)返回運(yùn)行結(jié)果"+time;
// Random generator = new Random();
// Integer randomNumber = generator.nextInt(5);
// Thread.sleep(randomNumber*1000);
// return randomNumber;
}
/*test1方法采用Executors的靜態(tài)方法newFixedThreadPool(taskSize) 創(chuàng)建一個(gè)可重用固定線程集合的
線程池,以共享的無(wú)界隊(duì)列方式來(lái)運(yùn)行這些線程,獲取線程池。ExecutorService的submit方法提交一個(gè)
callable實(shí)例,得到一個(gè)future對(duì)象,最終將future對(duì)象存儲(chǔ)在list數(shù)組中,加入線程池的過(guò)程中就代表
著線程已經(jīng)開(kāi)始執(zhí)行,相當(dāng)于一個(gè)線程池代理過(guò)程,就不需要采用start方法啟動(dòng)線程。最后對(duì)future進(jìn)行
打印輸出。切記一定要關(guān)閉線程池!*/
static void test1() throws ExecutionException, InterruptedException {
System.out.println("程序開(kāi)始");
Date data1 = new Date();
int taskSize = 5;
//構(gòu)建線程池對(duì)象
ExecutorService pool = Executors.newFixedThreadPool(taskSize);
List<Future> list =new ArrayList<Future>();
for(int i=0;i<taskSize;i++){
Callable c = new CallableTest(i);
Future f = pool.submit(c);
list.add(pool.submit(c));
}
//關(guān)閉線程池
pool.shutdown();
for(Future f:list){
System.out.println(">>>"+f.get().toString());
}
Date date2 = new Date();
System.out.println("程序運(yùn)行結(jié)束-----"+(date2.getTime()-data1.getTime())+"毫秒");
}
/*test2方法主要是采用futuretask類,可以直接把callable作為參數(shù)來(lái)申明futuretask對(duì)象,
這里相當(dāng)于把線程池?fù)Q成了futuretask數(shù)組,因?yàn)閠est1線程池可以對(duì)callable進(jìn)行封裝,
在這里可以直接采用futuretask就行封裝,在加上futuretask又實(shí)現(xiàn)了runnable接口,
所以可以直接創(chuàng)建線程采用start的方式進(jìn)行啟動(dòng)線程。*/
static void test2() throws ExecutionException, InterruptedException {
System.out.println("----程序開(kāi)始-----");
Date date1 =new Date();
int taskSize = 5;
FutureTask[] randNumber = new FutureTask[taskSize];
List<Future> list =new ArrayList<Future>();
for(int i=0;i<taskSize;i++){
Callable c = new CallableTest(i);
randNumber[i] = new FutureTask(c);
Thread t = new Thread(randNumber[i]);
t.start();
}
for(Future f:randNumber){
System.out.println(">>>"+f.get().toString());
}
Date date2 = new Date();
System.out.println("程序運(yùn)行結(jié)束-----"+(date2.getTime()-date1.getTime())+"毫秒");
}
public static void main(String[] args) throws Exception {
// CallableTest c = new CallableTest();
// Integer i = (Integer) c.call();
test1();
test2();
}
}
執(zhí)行結(jié)果

第四種實(shí)現(xiàn)線程的方法,基于線程池
其實(shí)在第三種的方法中就提到了兩種實(shí)現(xiàn)方法,一種線程池+future,另一種futuretask的方法。線程和數(shù)據(jù)庫(kù)連接這些資源都是非常寶貴的資源。那么每次需要的時(shí)候創(chuàng)建,不需要的時(shí)候銷(xiāo)毀,是非常浪費(fèi)資源的。那么我們就可以使用緩存的策略,也就是使用線程池。
// 創(chuàng)建線程池
ExecutorService threadPool = Executors.newFixedThreadPool(10);
while(true) {
threadPool.execute(new Runnable() { // 提交多個(gè)線程任務(wù),并執(zhí)行
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + " is running ..");
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
} }
總結(jié)
理論上實(shí)現(xiàn)線程的方法還有一些,本文所提及到的,基本都是一些創(chuàng)建線程常用的方法。希望本文對(duì)大家在學(xué)習(xí)線程的過(guò)程中有所幫助。
- java線程池的四種創(chuàng)建方式詳細(xì)分析
- JavaEE的進(jìn)程,線程和創(chuàng)建線程的5種方式詳解
- Java的線程與進(jìn)程以及線程的四種創(chuàng)建方式
- 聊聊java多線程創(chuàng)建方式及線程安全問(wèn)題
- Java線程的三種創(chuàng)建方式
- Java線程創(chuàng)建的四種方式總結(jié)
- 如何在Java中創(chuàng)建線程通信的四種方式你知道嗎
- Java創(chuàng)建線程及配合使用Lambda方式
- 很多人竟然不知道Java線程池的創(chuàng)建方式有7種
- java 創(chuàng)建線程的四種方式
- Java創(chuàng)建線程的方式解析
相關(guān)文章
超個(gè)性修改SpringBoot項(xiàng)目的啟動(dòng)banner的方法
這篇文章主要介紹了超個(gè)性修改SpringBoot項(xiàng)目的啟動(dòng)banner的方法,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2020-03-03
初識(shí)Spring Boot框架之Spring Boot的自動(dòng)配置
本篇文章主要介紹了初識(shí)Spring Boot框架之Spring Boot的自動(dòng)配置,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下。2017-04-04
java讀取文件顯示進(jìn)度條的實(shí)現(xiàn)方法
當(dāng)讀取一個(gè)大文件時(shí),一時(shí)半會(huì)兒無(wú)法看到讀取結(jié)果,就需要顯示一個(gè)進(jìn)度條,是程序員明白已經(jīng)讀了多少文件,可以估算讀取還需要多少時(shí)間,下面的代碼可以實(shí)現(xiàn)這個(gè)功能2014-01-01
Java中synchronized關(guān)鍵字的使用和原理詳解
這篇文章主要介紹了Java中synchronized關(guān)鍵字的使用和原理詳解,JVM 是通過(guò)進(jìn)入、退出 對(duì)象監(jiān)視器(Monitor)來(lái)實(shí)現(xiàn)對(duì)方法、同步塊的同步的,而對(duì)象監(jiān)視器的本質(zhì)依賴于底層操作系統(tǒng)的互斥鎖實(shí)現(xiàn),需要的朋友可以參考下2023-09-09
JavaWeb Refresh響應(yīng)頭代碼實(shí)例詳解
這篇文章主要介紹了JavaWeb Refresh響應(yīng)頭代碼實(shí)例詳解,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-02-02

