17、多線程 (Thread、線程創建、線程池)
阿新 • • 發佈:2019-02-05
選擇 時間 比較 開啟 用戶 沒有 代碼 棧內存 運行模式
進程概念
*A:進程概念
*a:進程:進程指正在運行的程序。確切的來說,當一個程序進入內存運行,
即變成一個進程,進程是處於運行過程中的程序,並且具有一定獨立功能。
線程的概念
*A:線程的概念
*a:線程:線程是進程中的一個執行單元(執行路徑),負責當前進程中程序的執行,
一個進程中至少有一個線程。一個進程中是可以有多個線程的,
這個應用程序也可以稱之為多線程程序。
簡而言之:一個程序運行後至少有一個進程,一個進程中可以包含多個線程
深入線程的概念
*A:深入線程的概念 什麽是多線程呢? 即就是一個程序中有多個線程在同時執行。 一個核心的CPU在多個線程之間進行著隨即切換動作,由於切換時間很短(毫秒甚至是納秒級別),導致我們感覺不出來 單線程程序: 即,若有多個任務只能依次執行。當上一個任務執行結束後,下一個任務開始執行。如去網吧上網,網吧只能讓一個人上網,當這個人下機後,下一個人才能上網。 多線程程序: 即,若有多個任務可以同時執行。如,去網吧上網,網吧能夠讓多個人同時上網。
線程的運行模式?
*A:線程的運行模式 a:分時調度 所有線程輪流使用 CPU 的使用權,平均分配每個線程占用 CPU 的時間。 b:搶占式調度 優先讓優先級高的線程使用 CPU,如果線程的優先級相同,那麽會隨機選擇一個(線程隨機性),Java使用的為搶占式調度。 大部分操作系統都支持多進程並發運行,現在的操作系統幾乎都支持同時運行多個程序。比如:現在我們上課一邊使用編輯器,一邊使用錄屏軟件,同時還開著畫圖板,dos窗口等軟件。此時,這些程序是在同時運行,”感覺這些軟件好像在同一時刻運行著“。 實際上,CPU(中央處理器)使用搶占式調度模式在多個線程間進行著高速的切換。對於CPU的一個核而言,某個時刻,只能執行一個線程,而 CPU的在多個線程間切換速度相對我們的感覺要快,看上去就是在同一時刻運行。 其實,多線程程序並不能提高程序的運行速度,但能夠提高程序運行效率,讓CPU的使用率更高。
main的主線程?
*A:main的主線程 /* * 程序中的主線程 */ public class Demo { public static void main(String[] args) { System.out.println(0/0); function(); System.out.println(Math.abs(-9)); } public static void function(){ for(int i = 0 ; i < 10000;i++){ System.out.println(i); } } }
Thread類介紹
*A:Thread類介紹:Thread是程序中的執行線程。Java 虛擬機允許應用程序並發地運行多個執行線程。
發現創建新執行線程有兩種方法。
a:一種方法是將類聲明為 Thread 的子類。該子類應重寫 Thread 類的 run 方法。創建對象,開啟線程。run方法相當於其他線程的main方法。
b:另一種方法是聲明一個實現 Runnable 接口的類。該類然後實現 run 方法。然後創建Runnable的子類對象,傳入到某個線程的構造方法中,開啟線程。
實現線程程序繼承Thread
*A:實現線程程序繼承Thread
/*
* 創建和啟動一個線程
* 創建Thread子類對象
* 子類對象調用方法start()
* 讓線程程序執行,JVM調用線程中的run
*/
public class ThreadDemo {
public static void main(String[] args) {
SubThread st = new SubThread();
SubThread st1 = new SubThread();
st.start();
st1.start();
for(int i = 0; i < 50;i++){
System.out.println("main..."+i);
}
}
}
/*
* 定義子類,繼承Thread
* 重寫方法run
*/
public class SubThread extends Thread{
public void run(){
for(int i = 0; i < 50;i++){
System.out.println("run..."+i);
}
}
}
線程執行的隨機性
*A:線程執行的隨機性
/*
代碼分析:
整個程序就只有三個線程,
一個是主線程
啟動另外兩個線程
st.start();
st1.start();
for(int i = 0; i < 50;i++){
System.out.println("main..."+i);
}
一個是st(Thread-0)線程
for(int i = 0; i < 50;i++){
System.out.println("run..."+i);
}
一個是st1(Thread-1)線程下
*/
public class ThreadDemo {
public static void main(String[] args) {
SubThread st = new SubThread();
SubThread st1 = new SubThread();
st.start();
st1.start();
for(int i = 0; i < 50;i++){
System.out.println("main..."+i);
}
}
}
/*
* 定義子類,繼承Thread
* 重寫方法run
*/
public class SubThread extends Thread{
public void run(){
for(int i = 0; i < 50;i++){
System.out.println("run..."+i);
}
}
}
為什麽要繼承Thread
*A:什麽要繼承Thread
a:我們為什麽要繼承Thread類,並調用其的start方法才能開啟線程呢?
繼承Thread類:因為Thread類用來描述線程,具備線程應該有功能。那為什麽不直接創建Thread類的對象呢?
如下代碼:
Thread t1 = new Thread();
//這樣做沒有錯,但是該start調用的是Thread類中的run方法
//而這個run方法沒有做什麽事情,更重要的是這個run方法中並沒有定義我們需要讓線程執行的代碼。
t1.start();
b:創建線程的目的是什麽?
是為了建立程序單獨的執行路徑,讓多部分代碼實現同時執行。也就是說線程創建並執行需要給定線程要執行的任務。
對於之前所講的主線程,它的任務定義在main函數中。自定義線程需要執行的任務都定義在run方法中。
多線程內存圖解
*A:多線程內存圖解
多線程執行時,到底在內存中是如何運行的呢?
多線程執行時,在棧內存中,其實每一個執行線程都有一片自己所屬的棧內存空間。進行方法的壓棧和彈棧。
當執行線程的任務結束了,線程自動在棧內存中釋放了。但是當所有的執行線程都結束了,那麽進程就結束了。
獲取線程名字Thread類方法getName?
*A:獲取線程名字Thread類方法getName
/*
* 獲取線程名字,父類Thread方法
* String getName()
*/
public class NameThread extends Thread{
public NameThread(){
super("小強");
}
public void run(){
System.out.println(getName());
}
}
/*
* 每個線程,都有自己的名字
* 運行方法main線程,名字就是"main"
* 其他新鍵的線程也有名字,默認 "Thread-0","Thread-1"
*
* JVM開啟主線程,運行方法main,主線程也是線程,是線程必然就是
* Thread類對象
*/
public class ThreadDemo {
public static void main(String[] args) {
NameThread nt = new NameThread();
nt.start();
}
}
?
獲取線程名字Thread類方法currentThread
*A:獲取線程名字Thread類方法currentThread
/*
* 獲取線程名字,父類Thread方法
* String getName()
*/
public class NameThread extends Thread{
public void run(){
System.out.println(getName());
}
}
/*
* 每個線程,都有自己的名字
* 運行方法main線程,名字就是"main"
* 其他新鍵的線程也有名字,默認 "Thread-0","Thread-1"
*
* JVM開啟主線程,運行方法main,主線程也是線程,是線程必然就是
* Thread類對象
* Thread類中,靜態方法
* static Thread currentThread()返回正在執行的線程對象
*/
public class ThreadDemo {
public static void main(String[] args) {
NameThread nt = new NameThread();
nt.start();
/*Thread t =Thread.currentThread();
System.out.println(t.getName());*/
System.out.println(Thread.currentThread().getName());
}
}
線程名字設置
*A:線程名字設置
/*
* 獲取線程名字,父類Thread方法
* String getName()
*/
public class NameThread extends Thread{
public NameThread(){
super("小強");
}
public void run(){
System.out.println(getName());
}
}
/*
* 每個線程,都有自己的名字
* 運行方法main線程,名字就是"main"
* 其他新鍵的線程也有名字,默認 "Thread-0","Thread-1"
*
* JVM開啟主線程,運行方法main,主線程也是線程,是線程必然就是
* Thread類對象
* Thread類中,靜態方法
* static Thread currentThread()返回正在執行的線程對象
*/
public class ThreadDemo {
public static void main(String[] args) {
NameThread nt = new NameThread();
nt.setName("旺財");
nt.start();
}
}
Thread類方法sleep
*A:Thread類方法sleep
public class ThreadDemo {
public static void main(String[] args) throws Exception{
/*for(int i = 0 ; i < 5 ;i++){
Thread.sleep(50);
System.out.println(i);
}*/
new SleepThread().start();
}
}
public class SleepThread extends Thread{
public void run(){
for(int i = 0 ; i < 5 ;i++){
try{
Thread.sleep(500);//睡眠500ms,500ms已到並且cpu切換到該線程繼續向下執行
}catch(Exception ex){
}
System.out.println(i);
}
}
}
實現線程的另一種方式實現Runnable接口
*A:實現線程的另一種方式實現Runnable接口
/*
* 實現接口方式的線程
* 創建Thread類對象,構造方法中,傳遞Runnable接口實現類
* 調用Thread類方法start()
*/
public class ThreadDemo {
public static void main(String[] args) {
SubRunnable sr = new SubRunnable();
Thread t = new Thread(sr);
t.start();
for(int i = 0 ; i < 50; i++){
System.out.println("main..."+i);
}
}
}
/*
* 實現線程成功的另一個方式,接口實現
* 實現接口Runnable,重寫run方法
*/
public class SubRunnable implements Runnable{
public void run(){
for(int i = 0 ; i < 50; i++){
System.out.println("run..."+i);
}
}
}
實現接口方式的好處
*A:實現接口方式的好處
第二種方式實現Runnable接口避免了單繼承的局限性,所以較為常用。
實現Runnable接口的方式,更加的符合面向對象,線程分為兩部分,一部分線程對象,一部分線程任務。
繼承Thread類,線程對象和線程任務耦合在一起。
一旦創建Thread類的子類對象,既是線程對象,有又有線程任務。
實現runnable接口,將線程任務單獨分離出來封裝成對象,類型就是Runnable接口類型。Runnable接口對線程對象和線程任務進行解耦。
(降低緊密性或者依賴性,創建線程和執行任務不綁定)
匿名內部類實現線程程序
*A:匿名內部類實現線程程序
/*
* 使用匿名內部類,實現多線程程序
* 前提: 繼承或者接口實現
* new 父類或者接口(){
* 重寫抽象方法
* }
*/
public class ThreadDemo {
public static void main(String[] args) {
//繼承方式 XXX extends Thread{ public void run(){}}
new Thread(){
public void run(){
System.out.println("!!!");
}
}.start();
//實現接口方式 XXX implements Runnable{ public void run(){}}
Runnable r = new Runnable(){
public void run(){
System.out.println("###");
}
};
new Thread(r).start();
new Thread(new Runnable(){
public void run(){
System.out.println("@@@");
}
}).start();
}
}
線程的狀態圖
A:線程的狀態圖
a:參見線程狀態圖.jpg
線程池的原理
*A:線程池的原理
1.在java中,如果每個請求到達就創建一個新線程,開銷是相當大的。
2.在實際使用中,創建和銷毀線程花費的時間和消耗的系統資源都相當大,甚至可能要比在處理實際的用戶請求的時間和資源要多的多。
3.除了創建和銷毀線程的開銷之外,活動的線程也需要消耗系統資源。
如果在一個jvm裏創建太多的線程,可能會使系統由於過度消耗內存或“切換過度”而導致系統資源不足。
為了防止資源不足,需要采取一些辦法來限制任何給定時刻處理的請求數目,盡可能減少創建和銷毀線程的次數,特別是一些資源耗費比較大的線程的創建和銷毀,盡量利用已有對象來進行服務。
線程池主要用來解決線程生命周期開銷問題和資源不足問題。通過對多個任務重復使用線程,線程創建的開銷就被分攤到了多個任務上了,而且由於在請求到達時線程已經存在,所以消除了線程創建所帶來的延遲。這樣,就可以立即為請求服務,使用應用程序響應更快。另外,通過適當的調整線程中的線程數目可以防止出現資源不足的情況。
JDK5實現線程池
*A:JDK5實現線程池
/*
* JDK1.5新特性,實現線程池程序
* 使用工廠類 Executors中的靜態方法創建線程對象,指定線程的個數
* static ExecutorService newFixedThreadPool(int 個數) 返回線程池對象
* 返回的是ExecutorService接口的實現類 (線程池對象)
*
* 接口實現類對象,調用方法submit (Ruunable r) 提交線程執行任務
*
*/
public class ThreadPoolDemo {
public static void main(String[] args) {
//調用工廠類的靜態方法,創建線程池對象
//返回線程池對象,是返回的接口
ExecutorService es = Executors.newFixedThreadPool(2);
//調用接口實現類對象es中的方法submit提交線程任務
//將Runnable接口實現類對象,傳遞
es.submit(new ThreadPoolRunnable());
es.submit(new ThreadPoolRunnable());
es.submit(new ThreadPoolRunnable());
}
}
public class ThreadPoolRunnable implements Runnable {
public void run(){
System.out.println(Thread.currentThread().getName()+" 線程提交任務");
}
}
實現線程的Callable接口方式
*A:實現線程的Callable接口方式
/*
* 實現線程程序的第三個方式,實現Callable接口方式
* 實現步驟
* 工廠類 Executors靜態方法newFixedThreadPool方法,創建線程池對象
* 線程池對象ExecutorService接口實現類,調用方法submit提交線程任務
* submit(Callable c)
*/
public class ThreadPoolDemo1 {
public static void main(String[] args)throws Exception {
ExecutorService es = Executors.newFixedThreadPool(2);
//提交線程任務的方法submit方法返回 Future接口的實現類
Future<String> f = es.submit(new ThreadPoolCallable());
String s = f.get();
System.out.println(s);
}
}
/*
* Callable 接口的實現類,作為線程提交任務出現
* 使用方法返回值
*/
public class ThreadPoolCallable implements Callable<String>{
public String call(){
return "abc";
}
}
線程實現異步計算
A:線程實現異步計算
/*
* 使用多線程技術,求和
* 兩個線程,1個線程計算1+100,另一個線程計算1+200的和
* 多線程的異步計算
*/
public class ThreadPoolDemo {
public static void main(String[] args)throws Exception {
ExecutorService es = Executors.newFixedThreadPool(2);
Future<Integer> f1 =es.submit(new GetSumCallable(100));
Future<Integer> f2 =es.submit(new GetSumCallable(200));
System.out.println(f1.get());
System.out.println(f2.get());
es.shutdown();
}
}
public class GetSumCallable implements Callable<Integer> {
private int a;
public GetSumCallable(int a){
this.a=a;
}
public Integer call(){
int sum = 0 ;
for(int i = 1 ; i <=a ; i++){
sum = sum + i ;
}
return sum;
}
}
17、多線程 (Thread、線程創建、線程池)