進程:其實就是.exe文件
線程:進程的一個執行單元
1.應用程序包含多個進程
2.進程可以包含多個線程
3.一個java程序其實就是一個進程,一個進程就相當於一個單核cup
並行:同時執行
併發:交替執行
多線程並行:多條線程同時執行
多線程併發:多條線程同時請求執行,但是一個cpu一次只能執行一條線程,所以讓其交替執行,但是由於切換速度比較快,所以你看起來像多線程並行
java中研究的就是多線程併發
1.java程序默認會有兩條線程,一條是main線程,一條是垃圾回收線程
2.線程的調度是搶佔式調度,也就是隨機調度
創建線程類
- java使用java.lang.Thread類代表線程,所有的線程對象都必須是Thread類或其子類的實例
- 一條線程執行一段任務(一段代碼)
- 創建線程並啓動線程的方式
創建線程並啓動線程方式一:通過繼承Thread類
- 創建一個子類繼承Thread類
- 重寫從Thread類繼承過來的run()方法,把子線程需要執行的代碼放入run()方法中
- 創建該子類對象
- 使用該子類對象調用start()方法啓動線程
public class MyThread extends Thread{
/**
* 利用繼承中的特點
* 將線程名稱傳遞 進行設置
* */
public MyThread(String name){
super(name);
}
/*
* 重寫run方法
* 定義線程要執行的代碼
* */
public void run(){
for (int i = 0; i < 20; i++) {
//getName()方法 來自父親
System.out.println(getName()+i);
}
}
}
測試類
public class Demo {
public static void main(String[] args) {
System.out.println("這裏是main線程");
MyThread mt = new MyThread("小強");
mt.start();//開啓了一個新的線程
for (int i = 0; i < 20; i++) {
System.out.println("旺財:"+i);
}
}
}
創建線程並啓動線程方式二:通過實現Runnable接口
- 創建一個實現類實現Runnable接口
- 在實現類中實現run()方法,把子線程需要執行的代碼放入run()方法中
- 創建一個Thread類的對象,把實現類的對象作爲參數傳入
- 調用start()方法啓動線程
public class MyRunnable implements Runnable{
@Override
public void run() {
for (int i = 0; i < 20; i++) {
System.out.println(Thread.currentThread().getName()+" "+i);
}
}
}
public class Demo {
public static void main(String[] args) {
//創建自定義類對象 線程任務對象
MyRunnable mr = new MyRunnable();
//創建線程對象
Thread t = new Thread(mr, "小強");
t.start();
for (int i = 0; i < 20; i++) {
System.out.println("旺財 " + i);
}
}
}
創建線程並啓動線程方式三:通過匿名內部類創建線程
1.創建一個Thread類的對象,把Runnable接口的匿名內部類以參數的形式傳入,在匿名內部類中指定任務
2.啓動線程
new Thread(new Runnable(){
public void run(){
//多線程要執行的代碼
}
}).start();
Threed類:
構造方法:
public Thread() :分配一個新的線程對象。
public Thread(String name) :分配一個指定名字的新的線程對象。
public Thread(Runnable target) :分配一個帶有指定目標新的線程對象。
public Thread(Runnable target,String name) :分配一個帶有指定目標新的線程對象並指定名字。
常用方法:
public String getName() :獲取當前線程名稱。
public void start() :導致此線程開始執行; Java虛擬機調用此線程的run方法。
public void run() :此線程要執行的任務在此處定義代碼。
public static void sleep(long millis) :使當前正在執行的線程以指定的毫秒數暫停(暫時停止執行)。
public static Thread currentThread() :返回對當前正在執行的線程對象的引用。
線程安全問題:使用同步機制來保證線程安全
同步代碼塊:其實就是對 一段代碼進行加鎖
概述:synchronized關鍵字可以用於方法中的某個區塊中,表示只對這個區塊的資源實行互斥訪問
格式:
synchronized(同步鎖){
需要加鎖的代碼
}
同步鎖:
1.可以寫任意對象
2.多個線程對象要使用同一把鎖
同步方法:其實就是對整個方法進行加鎖
概述:使用synchronized修飾的方法,就叫做同步方法,保證A線程執行該方法的時候,其他線程只能在方法外等着。
格式:
修飾符 synchronized 返回值類型 方法名(參數列表){
可以會產生線程安全問題的代碼
}
同步鎖是誰?
對於非靜態static方法,同步鎖就是this
對於靜態static方法,我們使用當前方法所在類的字節碼對象(類名.class)
A線程使用的同步代碼塊,B線程使用的同步方法,要想讓這2個線程同時鎖住,那麼A和B線程中的同步要一致
Lock鎖:ReentrantLock 實現類
Lock鎖也稱同步鎖,加鎖與釋放鎖方法化了,如下:
public void lock(): 加同步鎖
public void unlock(); 釋放同步鎖
condition:
await():等待
signal():喚醒
signalAll():喚醒所有
多條線程想要實現同步,那麼鎖必須一致。
線程的六種狀態:新建、可運行、鎖阻塞、無限等待、計時等待、死亡
實現無限等待:
1.使用鎖對象調用wait()方法使得線程進入無限等待
2.無限等待線程想要被喚醒,那麼必須是其他線程使用鎖對象調用notif’y()或者notifyall()方法
3.調用wait()方法和調用notify()或者notifyall()方法的鎖對象必須是一致
4.線程進入無限等待之後,不會搶佔cpu資源,也不會爭奪鎖對象
5.無限等待線程被喚醒了,並且獲取到了鎖對象,那麼線程就會繼續從進入無限等待的位置開始繼續往下執行
線程池:
概念:是一個可以放多個線程的容器,其中的線程可以反覆使用
使用線程池中線程對象的步驟:
- 創建線程池對象。
- 創建Runnable接口子類對象。(task)
- 提交Runnable接口子類對象。(take task)
- 關閉線程池(一般不做)。
使用:
方法:
public static ExecutorService newFixedThreadPool(int nThreads) :返回線程池對象。(創建的是有界線 程池,也就是池中的線程個數可以指定最大數量)
// 創建線程池對象
ExecutorService service = Executors.newFixedThreadPool(2);//包含2個線程對象
public Future<?> submit(Runnable task) :獲取線程池中的某一個線程對象,並執行
// 從線程池中獲取線程對象,然後調用MyRunnable中的run()
service.submit(r);