基礎概念
CPU核心數和線程數的比例:1:1,超線程技術比例:1:2
CPU時間片輪轉機制(RR調度)辨嗽,涉及線程上下文切換
進程和線程
- 進程:程序運行資源分配的最小單位世落,一個進程內部有多個線程,共享這個進程的資源糟需,進程可獨立運行屉佳,進程之間相互獨立
- 線程:CPU調度的最小單位,線程本身不能獨立運行洲押,必須依附于進程武花,自己不能申請資源,只能共享使用進程的資源
并行和并發(fā)
- 并行:同一時刻可以處理事情的能力
- 并發(fā):與單位時間相關杈帐,一定單位時間內處理事情的能力
- 舉例1:食堂有8個窗口可同時打飯体箕,則食堂打飯的并行數為8,一分鐘內一個窗口可以為3個學生服務挑童,則一分鐘內食堂都并發(fā)數為 8 * 3 = 24
- 舉例2:CPU的核心數為8累铅,則CPU的并發(fā)數為8,CPU的時間片分隔執(zhí)行周期為100ms炮沐,則1秒內CPU并發(fā)數為 1000 / 100 * 8 = 80
高并發(fā)的意義
- 充分利用CPU資源
- 加快用戶響應時間
- 使代碼模塊化争群,異步化處理
高并發(fā)的注意事項
- 資源共享,會導致線程沖突大年,照成線程死鎖
- 創(chuàng)建多過的線程,會導致資源枯竭
Java線程
Java啟動線程的方式
- 繼承Thread類
- 實現(xiàn)Runnable接口玉雾,無返回值
- 實現(xiàn)Callable接口翔试,有返回值
線程結束
- 程序執(zhí)行完成,自然結束
- 拋出異常結束
- stop()复旬、resume()垦缅、pause(),暴力停止驹碍,線程資源無法釋放壁涎,不建議使用
- interrupt()凡恍、interrupted()、isInterrupted()三種java線程協(xié)作方式停止線程
- thread.interrupt():中斷當前thread線程怔球,并不是強制關閉線程嚼酝,而且將中斷標記位設置為true,線程在某一個時刻中斷竟坛,無法控制
- thread.isInterrupted():僅返回thread線程中斷狀態(tài)
- static interrupted():靜態(tài)方法闽巩,作用于當前所在線程,返回當前線程的中斷狀態(tài)担汤,并會將中斷標記位設置為false
- 線程拋出InterruptedException異常時涎跨,會重置中斷標記位為false,需再次調用interrupt()方法才可退出循環(huán)
參考:http://www.reibang.com/p/4da928eed5e8
線程的生命周期
未命名文件2.jpg
線程的優(yōu)先級:myThread.setPriority(6); // 1 - 10之間崭歧,不一定生效
守護線程:如果主線程結束隅很,則守護線程強制結束
myThread.setDaemon(true)
注:守護線程會導致線程內的finally()方法不一定會執(zhí)行,導致資源無法釋放
sleep,wait,yield,join的區(qū)別
- sleep():在指定時間內讓正在運行的線程暫停率碾,使線程進入暫停狀態(tài)叔营,sleep()并不會釋放鎖
- wait():在其他線程調用notify()或notifyAll()方法之前,線程阻塞播掷,wait()方法會釋放鎖审编,這樣別的線程有機會競爭到鎖,wait()歧匈、notify()垒酬、notifyAll()必須在synchronized方法塊或者修飾的方法上使用
- yield():暫停正在執(zhí)行的線程,yield()方法只是將線程的狀態(tài)由運行狀態(tài)變成就緒狀態(tài)件炉,可能會在一個執(zhí)行周期搶占到了資源勘究,由就緒狀態(tài)變成運行狀態(tài)重新執(zhí)行,所以yield()方法調用后線程只需要重新獲取CPU的機會斟冕,sleep()方法得等到固定時間結束后才會進入就緒狀態(tài)
- join():等待調用join()方法的實例所代表的線程執(zhí)行完成后口糕,當前線程才能繼續(xù)執(zhí)行,比如main線程中調用了t.join()磕蛇,則主線程需要等待t線程結束后才會繼續(xù)執(zhí)行
線程共享
synchronized:內置鎖
- 對象鎖:鎖的范圍是對象
- 類鎖:鎖的范圍是類對應的class對象
volatile:最輕量級的同步機制景描,強制線程從主內存獲取值,可以保障線程的可見性秀撇,但無法保障線程的原子性超棺,volatile是非線程安全的
ThreadLocal:空間換線程安全,每個線程保存了對象的副本呵燕,對象之間相互獨立
wait()棠绘、notify()、notifyAll()方法
wait():調用wait()時,當前線程進入阻塞狀態(tài)氧苍,并釋放鎖夜矗,等待別的線程調用notify()或notifyAll()方法喚醒
notify():調用notify()方法通知喚醒某一個wait()方法所阻塞等待的線程
notifyAll(): 通知喚醒所有因調wait()方法所阻塞等待的線程
等待通知機制
等待方:
1、獲取對象鎖
2让虐、循環(huán)判斷是否滿足處理的條件紊撕,不滿足調用wait()方法
3、滿足條件執(zhí)行業(yè)務邏輯
通知方:
1澄干、獲取對象鎖
2逛揩、改變條件
3、通知所有等待在鎖對象上的線程
實例1:wait()麸俘、notifyAll()實現(xiàn)一個快遞通知辩稽,當快遞的里程數>100公里或者城市發(fā)生變化時給用戶通知
/**
* 當快遞的公里數大于100公里或城市不在北京時,給客戶發(fā)出提醒消息
*/
public class Express {
public static final String CURR_CITY = "北京";
private Object object = new Object();
/**
* 快遞行走的公里數
*/
private int km;
/**
* 快遞當前位置
*/
private String site = "北京";
/**
* 修改快遞公里數
* @param km
*/
public void changeKm(int km) {
synchronized (object) {
System.out.println(Thread.currentThread().getName() + " km : " + km);
this.km = km;
object.notifyAll();
}
}
/**
* 修改快遞城市
* @param city
*/
public void changeSite(String city) {
synchronized (object) {
System.out.println(Thread.currentThread().getName() + " site : " + site);
this.site = city;
object.notifyAll();
}
}
/**
* 等待公里數變化
*/
public void waitKm() throws InterruptedException {
synchronized (object) {
while (this.km <= 100) {
object.wait();
System.out.println(Thread.currentThread().getName() + " check km...");
}
System.out.println(Thread.currentThread().getName() + " , send change, curr km : " + km);
}
}
/**
* 等待城市變化
*/
public void waitSite() throws InterruptedException {
synchronized (object) {
while (CURR_CITY.equals(site)) {
object.wait();
System.out.println(Thread.currentThread().getName() + " check site...");
}
System.out.println(Thread.currentThread().getName() + " send change, curr site : " + site);
}
}
}
實例2:實現(xiàn)一個自定義的數據庫連接池
/**
* 自定義實現(xiàn)線程池
*/
public class DBPool {
private LinkedList<MyConnection> DB_POOL = new LinkedList<>();
private static final int INITIALIZE_SIZE = 10;
private Object lock = new Object();
public DBPool(int initializeSize) {
int size = initializeSize;
if (initializeSize <= 0) {
size = INITIALIZE_SIZE;
}
for (int i = 0; i < size; ++i) {
MyConnection connection = new MyConnection();
DB_POOL.add(connection);
}
}
/**
* 獲取鏈接
*/
public MyConnection getConnection(long waitTime) throws InterruptedException {
synchronized (lock) {
if (waitTime <= 0) {
while (DB_POOL.isEmpty()) {
lock.wait();
}
return DB_POOL.removeFirst();
} else {
long endTimeStamp = System.currentTimeMillis() + waitTime;
long remainTime = waitTime;
while (DB_POOL.isEmpty() && remainTime > 0) {
lock.wait();
remainTime = endTimeStamp - System.currentTimeMillis();
}
if (DB_POOL.isEmpty()) {
return null;
}
return DB_POOL.removeFirst();
}
}
}
/**
* 釋放連接
*/
public void releaseConnection(MyConnection connection) {
if (connection == null) {
return;
}
synchronized (lock) {
DB_POOL.addLast(connection);
lock.notifyAll();
}
}
}
yield()从媚、wait()逞泄、notify()/notifyAll()、sleep()方法對鎖的影響
- yield():暫停正在執(zhí)行的線程拜效,線程由進行狀態(tài)轉換為就緒狀態(tài)喷众,但不會釋放鎖
- sleep():暫停正在執(zhí)行的線程,線程由進行狀態(tài)轉換為暫停狀態(tài)紧憾,也不會釋放鎖
- wait():阻塞當前線程到千,調用wait()方法之前,必須先獲取到鎖赴穗,調用wait()方法之后會釋放鎖憔四,當wait()方法返回時又會重新獲取鎖
- notify()、notifyAll():調用notify()方法之前必須持有鎖般眉,喚醒wait()方法阻塞的線程了赵,其本身方法并不會釋放鎖,而是要等待調用notify()的方法結束后所持有的鎖釋放甸赃,所以notify()一般都放在方法的最后調用
join()方法
線程A調用線程B的join()方法柿汛,則線程A要等到線程B執(zhí)行結束后,線程A才能繼續(xù)執(zhí)行
import com.shawntime.enjoy.architect.concurrency.SleepUtils;
/**
* join()方法測試
*/
public class JoinTest {
private static class MyThread extends Thread {
private Thread thread;
public MyThread(String name, Thread thread) {
super(name);
this.thread = thread;
}
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + " waiting for " + thread.getName());
try {
thread.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "執(zhí)行完成...");
}
}
public static void main(String[] args) {
Thread thread = Thread.currentThread();
for (int i = 0; i < 10; ++i) {
MyThread myThread = new MyThread("my-thread" + i, thread);
myThread.start();
thread = myThread;
}
for (int i = 0; i < 3; ++i) {
System.out.println("主線程睡眠" + (i + 1) + "秒");
SleepUtils.sleepBySeconds(1);
}
System.out.println("Main方法執(zhí)行完成...");
}
}
執(zhí)行結果:
Main方法執(zhí)行完成...
my-thread0執(zhí)行完成...
my-thread1執(zhí)行完成...
my-thread2執(zhí)行完成...
my-thread3執(zhí)行完成...
my-thread4執(zhí)行完成...
my-thread5執(zhí)行完成...
my-thread6執(zhí)行完成...
my-thread7執(zhí)行完成...
my-thread8執(zhí)行完成...
my-thread9執(zhí)行完成...