[TOC]
# 簡介
線程:線程是進程中的一個執行單元,負責當前進程中程序的執行,一個進程中至少有一個線程。一個進程中是可以有多個線程的,這個應用程序也可以稱之為多線程程序。
簡而言之:一個程序運行后至少有一個進程,一個進程中可以包含多個線程
* 程序運行原理
- 分時調度
所有線程輪流使用 CPU 的使用權,平均分配每個線程占用 CPU 的時間。
- 搶占式調度
優先讓優先級高的線程使用 CPU,如果線程的優先級相同,那么會隨機選擇一個(線程隨機性),Java使用的為搶占式調度。
其實,多線程程序并不能提高程序的運行速度,但能夠提高程序運行效率,讓CPU的使用率更高。
# Thread類
Thread是程序中的執行線程。Java 虛擬機允許應用程序并發地運行多個執行線程。

* 構造方法

* 常用方法

創建新執行線程有兩種方法。
* 一種方法是將類聲明為 Thread 的子類。該子類應重寫 Thread 類的 run 方法。創建對象,開啟線程。run方法相當于其他線程的main方法。
* 另一種方法是聲明一個實現 Runnable 接口的類。該類然后實現 run 方法。然后創建Runnable的子類對象,傳入到某個線程的構造方法中,開啟線程。
# 創建線程一
## 繼承Thread類
創建線程的步驟:
1 定義一個類繼承Thread。
2 重寫run方法。
3 創建子類對象,就是創建線程對象。
4 調用start方法,開啟線程并讓線程執行,同時還會告訴jvm去調用run方法。
* 測試類
~~~
public class Demo01 {
public static void main(String[] args) {
//創建自定義線程對象
MyThread mt = new MyThread("新的線程!");
//開啟新線程
mt.start();
//在主方法中執行for循環
for (int i = 0; i < 10; i++) {
System.out.println("main線程!"+i);
}
}
}
~~~
* 自定義線程類
~~~
public class MyThread extends Thread {
//定義指定線程名稱的構造方法
public MyThread(String name) {
//調用父類的String參數的構造方法,指定線程的名稱
super(name);
}
/**
* 重寫run方法,完成該線程執行的邏輯
*/
@Override
public void run() {
for (int i = 0; i < 10; i++) {
System.out.println(getName()+":正在執行!"+i);
}
}
}
~~~
思考:線程對象調用 run方法和調用start方法區別?
線程對象調用run方法不開啟線程。僅是對象調用方法。線程對象調用start開啟線程,并讓jvm調用run方法在開啟的線程中執行。
### 原理
* 繼承Thread類原理
我們為什么要繼承Thread類,并調用其的start方法才能開啟線程呢?
繼承Thread類:因為Thread類用來描述線程,具備線程應該有功能。那為什么不直接創建Thread類的對象呢?如下代碼:
~~~
Thread t1 = new Thread();
~~~
t1.start();//這樣做沒有錯,但是該start調用的是Thread類中的run方法,而這個run方法沒有做什么事情,更重要的是這個run方法中并沒有定義我們需要讓線程執行的代碼。
創建線程的目的是什么?
是為了建立程序單獨的執行路徑,讓多部分代碼實現同時執行。也就是說線程創建并執行需要給定線程要執行的任務。
對于之前所講的主線程,它的任務定義在main函數中。自定義線程需要執行的任務都定義在run方法中。
Thread類run方法中的任務并不是我們所需要的,只有重寫這個run方法。既然Thread類已經定義了線程任務的編寫位置(run方法),那么只要在編寫位置(run方法)中定義任務代碼即可。所以進行了重寫run方法動作。
### 獲取線程名稱
開啟的線程都會有自己的獨立運行棧內存,那么這些運行的線程的名字是什么呢?該如何獲取呢?既然是線程的名字,按照面向對象的特點,是哪個對象的屬性和誰的功能,那么我們就去找那個對象就可以了。查閱Thread類的API文檔發現有個方法是獲取當前正在運行的線程對象。還有個方法是獲取當前線程對象的名稱。既然找到了,我們就可以試試。

* Thread.currentThread()獲取當前線程對象
* Thread.currentThread().getName();獲取當前線程對象的名稱
~~~
class MyThread extends Thread { //繼承Thread
MyThread(String name){
super(name);
}
//復寫其中的run方法
public void run(){
for (int i=1;i<=20 ;i++ ){
System.out.println(Thread.currentThread().getName()+",i="+i);
}
}
}
class ThreadDemo {
public static void main(String[] args) {
//創建兩個線程任務
MyThread d = new MyThread();
MyThread d2 = new MyThread();
d.run();//沒有開啟新線程, 在主線程調用run方法
d2.start();//開啟一個新線程,新線程調用run方法
}
}
~~~
通過結果觀察,原來主線程的名稱:main;自定義的線程:Thread-0,線程多個時,數字順延。如Thread-1......
進行多線程編程時,不要忘記了Java程序運行是從主線程開始,main方法就是主線程的線程執行內容。
# 創建線程二
## 實現Runnable接口
創建線程的另一種方法是聲明實現 Runnable 接口的類。該類然后實現 run 方法。然后創建Runnable的子類對象,傳入到某個線程的構造方法中,開啟線程。

## 步驟
創建線程的步驟。
1、定義類實現Runnable接口。
2、覆蓋接口中的run方法。。
3、創建Thread類的對象
4、將Runnable接口的子類對象作為參數傳遞給Thread類的構造函數。
5、調用Thread類的start方法開啟線程
* 代碼演示:
~~~
public class Demo02 {
public static void main(String[] args) {
//創建線程執行目標類對象
Runnable runn = new MyRunnable();
//將Runnable接口的子類對象作為參數傳遞給Thread類的構造函數
Thread thread = new Thread(runn);
Thread thread2 = new Thread(runn);
//開啟線程
thread.start();
thread2.start();
for (int i = 0; i < 10; i++) {
System.out.println("main線程:正在執行!"+i);
}
}
}
~~~
* 自定義線程執行任務類
~~~
public class MyRunnable implements Runnable{
//定義線程要執行的run方法邏輯
@Override
public void run() {
for (int i = 0; i < 10; i++) {
System.out.println("我的線程:正在執行!"+i);
}
}
}
~~~
## 原理
實現Runnable接口,避免了繼承Thread類的單繼承局限性。覆蓋Runnable接口中的run方法,將線程任務代碼定義到run方法中。
創建Thread類的對象,只有創建Thread類的對象才可以創建線程。線程任務已被封裝到Runnable接口的run方法中,而這個run方法所屬于Runnable接口的子類對象,所以將這個子類對象作為參數傳遞給Thread的構造函數,這樣,線程對象創建時就可以明確要運行的線程的任務。
## 好處
實現Runnable接口避免了單繼承的局限性,所以較為常用。實現Runnable接口的方式,更加的符合面向對象,線程分為兩部分,一部分線程對象,一部分線程任務。繼承Thread類,線程對象和線程任務耦合在一起。一旦創建Thread類的子類對象,既是線程對象,有又有線程任務。實現runnable接口,將線程任務單獨分離出來封裝成對象,類型就是Runnable接口類型。Runnable接口對線程對象和線程任務進行解耦。
# 線程的匿名內部類使用
使用線程的內匿名內部類方式,可以方便的實現每個線程執行不同的線程任務操作。
* 方式1:創建線程對象時,直接重寫Thread類中的run方法
~~~
new Thread() {
public void run() {
for (int x = 0; x < 40; x++) {
System.out.println(Thread.currentThread().getName()
+ "...X...." + x);
}
}
}.start();
~~~
* 方式2:使用匿名內部類的方式實現Runnable接口,重新Runnable接口中的run方法
~~~
Runnable r = new Runnable() {
public void run() {
for (int x = 0; x < 40; x++) {
System.out.println(Thread.currentThread().getName()
+ "...Y...." + x);
}
}
};
new Thread(r).start();
~~~
- 基礎
- 編譯和安裝
- scanner類(鍵盤錄入)
- Random類(隨機數)
- 數組
- 方法
- 類
- ArrayList集合
- char與int
- eclipse
- IDEA
- 變量與常量
- 常用API
- String,StringBuffer,StringBuilder
- 正則,Date,DateFormat,Calendar
- 包裝類,System,Math,Arrays,BigInteger,BigDecimal
- 集合,迭代器,增強for,泛型
- List,set,判斷集合唯一
- map,Entry,HashMap,Collections
- 異常
- IO
- File
- 遞歸
- 字節流
- 字符流
- IO流分類
- 轉換流
- 緩沖流
- 流的操作規律
- properties
- 序列化流與反序列化流
- 打印流
- commons-IO
- IO流總結
- 多線程
- 線程池
- 線程安全
- 線程同步
- 死鎖
- lock接口
- ThreadLoad
- 等待喚醒機制
- 線程狀態
- jdbc
- DBUtils
- 連接池DBCP
- c3p0連接池
- 網絡編程
- 多線程socket上傳圖片
- 反射
- xml
- 設計模式
- 裝飾器模式
- web service
- tomcat
- Servlet
- response
- request
- session和cookie
- JSP
- EL
- JSTL
- 事務
- 監聽器Listener
- 過濾器Filter
- json
- linux安裝軟件
- 反射詳解
- 類加載器和注解
- 動態代理
- jedis
- Hibernate
- 簡介
- 創建映射文件
- Hibernate核心配置文件
- 事務和增刪改查
- HibernateUtils
- 持久化對象的三種狀態
- 檢索方式
- query
- Criteria
- SQLQuery
- 持久化類
- 主鍵生成策略
- 緩存
- 事務管理
- 關系映射
- 注解
- 優化
- struts2
- 搭建
- 配置詳解
- Action
- 結果跳轉方式
- 訪問ServletAPI方式
- 如何獲得參數
- OGNL表達式
- valueStack 值棧
- Interceptor攔截器
- spring
- 導包
- IOC和DI
- Bean獲取與實例化
- Bean屬性注入
- spring注解
- 注解分層
- junit整合
- aop
- 動態代理實現
- cglib代理實現
- aop名詞
- spring的aop
- aop-xml詳解
- aop-注解詳解
- 代理方式選擇
- jdbcTemplate
- spring事務管理
- 回滾注意
- 事務傳播屬性
- MyBatis
- MyBatis簡介
- 入門程序
- 與jdbc hibernate不同
- 原始Dao開發
- Mapper動態代理方式
- SqlMapConfig.xml配置文件
- 輸入參數pojo包裝類
- resultMap
- 動態sql
- 一對一關聯
- 一對多
- 整合spring
- 逆向工程
- maven
- maven簡介
- 倉庫
- maven目錄結構
- maven常用命令
- 生命周期
- eclipse中maven插件
- 入門程序
- 整合struct
- 依賴范圍
- 添加插件
- idea配置
- jar包沖突
- 分模塊開發
- 構建可執行的jar包(包含依賴jar包)
- springMVC
- 處理流程
- java面試
- java版本升級
- java1-8版本變更
- java9新特性
- 鎖
- java資料
- idea
- jdk版本切換
- log4j
- 入門實例
- 基本使用方法
- Web中使用Log4j
- spring中使用log4j
- java代碼優化