java线程一之创建线程、线程池以及多线程运行时间统计
線程和進(jìn)程的基本概念
進(jìn)程和線程是動(dòng)態(tài)的概念。
進(jìn)程是 “執(zhí)行中的程序“,是一個(gè)動(dòng)詞,而程序是一個(gè)名詞,進(jìn)程運(yùn)行中程序的”代碼“,而且還有自己的計(jì)數(shù)器,寄存器,它會(huì)向系統(tǒng)申請(qǐng)系統(tǒng)資源。
線程是進(jìn)程中的一個(gè)控制流。一個(gè)程序可能可能包含多個(gè)任務(wù)并發(fā)運(yùn)行,而線程就是指一個(gè)任務(wù)重頭到尾的執(zhí)行流。
說的在簡單點(diǎn),線程是執(zhí)行中的任務(wù),一個(gè)程序包含多個(gè)任務(wù)。
多線程
單處理器中,為提高處理器的使用率(最終目標(biāo)),使得程序在進(jìn)行IO輸入出等不需要處理器時(shí),也能夠讓處理器在運(yùn)轉(zhuǎn),引進(jìn)多線程處理機(jī)制。多線程可以使得程序運(yùn)行的更快,執(zhí)行效率更高,交互性更強(qiáng),這是不言而喻的!
創(chuàng)建任務(wù)和線程
一個(gè)任務(wù)是一個(gè)對(duì)象,所以為創(chuàng)建一個(gè)任務(wù),必須定義一個(gè)類,定義一個(gè)任務(wù)類,為了說明這是一個(gè)任務(wù)類,它需要實(shí)現(xiàn)Runnable接口,這個(gè)接口只包含一個(gè)run方法。
當(dāng)我們定義好任務(wù)類taskClass之后,就可以用它的構(gòu)造方法創(chuàng)建一個(gè)任務(wù)啦:TaskClass task = new TaskClass(.....);
我們創(chuàng)建的任務(wù)只能在線程中運(yùn)行,Thread類中包含了創(chuàng)建線程以及控制線程的眾多方法。使用下面的語句創(chuàng)建任務(wù)線程:Thread thread = new Thread(task);
然后調(diào)用start()方法告訴java虛擬機(jī)該線程準(zhǔn)備運(yùn)行:thread.start();之后java虛擬機(jī)通過調(diào)用任務(wù)的run()方法執(zhí)行任務(wù)。
事例代碼:
public class TaskThreadDemo{
public static void main(String[] args)
{
//創(chuàng)建任務(wù) 并且需要為任務(wù) 創(chuàng)建 任務(wù)類,使用該類的構(gòu)造方法創(chuàng)建任務(wù)
PrintChar printA = new PrintChar('a',100);
PrintChar printB = new PrintChar('b',100);
PrintNum print100 = new PrintNum(100);
//為任務(wù)創(chuàng)建線程
Thread thread1 = new Thread(printA);
Thread thread2 = new Thread(printB);
Thread thread3= new Thread(print100);
//告訴虛擬機(jī)器線程開始運(yùn)行
thread1.start();
thread2.start();
thread3.start();
}
}
class PrintChar implements Runnable
{
private char charToPrint;
private int items;
public PrintChar(char c,int t)
{
charToPrint = c;
items = t;
}
@Override
public void run() {
// TODO Auto-generated method stub
for(int i=0;i<items;i++)
{
System.out.print(charToPrint);
}
}
}
class PrintNum implements Runnable
{
private int lastNumb;
public PrintNum(int n)
{
lastNumb = n;
}
@Override
public void run() {
// TODO Auto-generated method stub
for(int i= 1;i<100;i++)
{
System.out.print(" "+lastNumb);
}
}
}
一個(gè)好玩的閃爍文本框,直接用線程,不用main方法
import javax.swing.JApplet;
import javax.swing.JLabel;
public class FlashingText extends JApplet implements Runnable {
private static final long serialVersionUID = 1L;
private JLabel jlbText = new JLabel("Welcome",JLabel.CENTER);
public FlashingText()
{
add(jlbText);
new Thread(this).start();
}
public void run()
{
try {
while(true)
{
if(jlbText.getText()==null)
jlbText.setText("Welcome");
else
jlbText.setText(null);
Thread.sleep(200);
}
} catch (Exception e) {
// TODO: handle exception
}
}
}
Thread類
Thread類實(shí)現(xiàn)了Runnable接口,它包含為任務(wù)而創(chuàng)建線程的構(gòu)造方法,以及控制方法,下圖就是Thread類常見的控制線程的方法:
因?yàn)門hread類實(shí)現(xiàn)了Runnable接口,所以可以定義一個(gè)Thread的擴(kuò)展類,里面實(shí)現(xiàn)run方法,這樣也可以創(chuàng)建一個(gè)線程類,但并不是很推薦這種方法,因?yàn)樗鼘?font color="#9b00d3">創(chuàng)建任務(wù)和運(yùn)行任務(wù)的機(jī)制混在了一起,將任務(wù)從線程中分離出來比較好,即盡量使用Runnable接口創(chuàng)建線程,這樣得到的線程更加靈活,避免了java單繼承帶來的局限性。
線程池
線程池是管理并發(fā)執(zhí)行任個(gè)數(shù)的理想方法,java提供Executor接口來執(zhí)行線程池中的任務(wù),提供ExecutorService接口來管理和控制任務(wù)。為了創(chuàng)建Executor接口實(shí)例,我們可以用Executors類,Executors類提供了創(chuàng)建Executor接口對(duì)象的靜態(tài)方法,下圖描述了上面的上面所說的關(guān)系。
線程池中的shutdown()方法一般都是放在main方法的后面部分,當(dāng)所以的線程都添加到線程池中,即便有線程沒有執(zhí)行完畢,也可能會(huì)關(guān)閉線程池,未執(zhí)行完的線程繼續(xù)執(zhí)行,所以main方法可能比子線程先結(jié)束。
利用isTerminated()進(jìn)行線程池中所有線程運(yùn)行時(shí)間的統(tǒng)計(jì)
倘若希望主線程在子線程全部做完之后在執(zhí)行,可以考慮讓所以的子線程用jion方法,但這可能導(dǎo)致所有子線程變成串行,不是很好的辦法,當(dāng)然我們也可以多線程的輔助類CountDownLatch,這是一個(gè)與計(jì)時(shí)器有點(diǎn)類似功能的類。這次在看書學(xué)習(xí)的時(shí)候,發(fā)現(xiàn)了一個(gè)更好的辦法,就是在shutdown()方法后加上一句while(!executor.isTerminated()){},這是一個(gè)不錯(cuò)的方法(不過我把shoudown()方法放在該while語句后面,就會(huì)陷入死循環(huán),應(yīng)該是要先關(guān)閉線程池,在判斷線程池中的線程是否全部終止,因?yàn)橐灿锌赡茉趙hile語句后面在添加新的子線程)。
isTerminated()方法:如果線程池中所以線程都已完成并終止,則返回true.
可以使用CountDownLatch 和上面的方法對(duì)程序運(yùn)行計(jì)時(shí)統(tǒng)計(jì),不過我記得CountDownLatch類是需要指定線程的個(gè)數(shù)。
事例代碼
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class ExecutorDemo {
public static void main(String[] args){
long start = System.currentTimeMillis();
System.out.println("當(dāng)前時(shí)間"+" "+start);
ExecutorService executor = Executors.newFixedThreadPool(4);
executor.execute(new PrintChar('a',100));
executor.execute(new PrintChar('b',100));
executor.execute(new PrintChar('c',100));
executor.execute(new PrintNum(100));
executor.shutdown();
while(!executor.isTerminated()){
//System.out.print("-");
};
long end = System.currentTimeMillis();
System.out.println("
當(dāng)前時(shí)間"+" "+end);
System.out.println("
用時(shí)"+" "+(end-start)+"毫秒");
}
}
運(yùn)行截圖
截圖1 截圖2
不過不知道運(yùn)行太快的原因,還是啥別的原因, 會(huì)出現(xiàn)截圖2的情況,網(wǎng)上很少關(guān)于isTerminated()方法的介紹,如有大神知道原因,還請(qǐng)告知小弟。
這篇博客是自己在學(xué)習(xí)《java語言程序設(shè)計(jì)進(jìn)階篇》時(shí)所在筆記,代碼出自書上,最后那個(gè)記時(shí)的加了計(jì)時(shí)部分。大三開學(xué)在即,希望現(xiàn)在來學(xué)java還來得及。
本文出自于博客園蘭幽,轉(zhuǎn)載請(qǐng)說明出處。
總結(jié)
以上是生活随笔為你收集整理的java线程一之创建线程、线程池以及多线程运行时间统计的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: camunda
- 下一篇: 一步步分析MIPS数据通路(单周期)