死鎖
是指兩個或兩個以上的進程在執(zhí)行過程中,由于競爭資源或者由于彼此通信而造成的一種阻塞的現(xiàn)象,若無外力作用,它們都將無法推進下去。此時稱系統(tǒng)處于死鎖狀態(tài)或系統(tǒng)產(chǎn)生了死鎖。
產(chǎn)生死鎖的條件:
1)互斥條件:指進程對所分配到的資源進行排它性使用,即在一段時間內(nèi)某資源只由一個進程占用。如果此時還有其它進程請求資源,則請求者只能等待,直至占有資源的進程用畢釋放。
2)請求和保持條件:指進程已經(jīng)保持至少一個資源,但又提出了新的資源請求,而該資源已被其它進程占有,此時請求進程阻塞,但又對自己已獲得的其它資源保持不放。
3)不剝奪條件:指進程已獲得的資源,在未使用完之前,不能被剝奪,只能在使用完時由自己釋放。
4)環(huán)路等待條件:指在發(fā)生死鎖時,必然存在一個進程——資源的環(huán)形鏈,即進程集合{P0,P1,P2,···,Pn}中的P0正在等待一個P1占用的資源;P1正在等待P2占用的資源,……,Pn正在等待已被P0占用的資源。
- 自己總結(jié)(人話):
- 爭奪者數(shù)目大于爭奪資源
- 爭奪資源順序不對
- 拿到資源不放手
- 有另外一個等待使用資源的線程
解決死鎖:
只要打破四個必要條件之一就能有效預(yù)防死鎖的發(fā)生。
打破互斥條件:改造獨占性資源為虛擬資源,大部分資源已無法改造。
打破不可搶占條件:當(dāng)一進程占有一獨占性資源后又申請一獨占性資源而無法滿足,則退出原占有的資源。
打破占有且申請條件:采用資源預(yù)先分配策略,即進程運行前申請全部資源,滿足則運行,不然就等待,這樣就不會占有且申請。
打破循環(huán)等待條件:實現(xiàn)資源有序分配策略,對所有設(shè)備實現(xiàn)分類編號,所有進程只能采用按序號遞增的形式申請資源。
- 自己總結(jié)(人話):
- 確定每個線程的拿鎖順序
- 采用嘗試拿鎖的方式
活鎖
頻繁申請鎖兩個線程在嘗試拿鎖的機制中,發(fā)生多個線程之間互相謙讓,不斷發(fā)生同一個線程總是拿到同一把鎖,在嘗試拿另一把鎖時因為拿不到,而將本來已經(jīng)持有的鎖釋放的過程。
解決辦法:每個線程休眠隨機數(shù),錯開拿鎖的時間。
線程饑餓
低優(yōu)先級的線程,總是拿不到執(zhí)行時間
Thread Local
Thread Local 線程本地變量 為每個線程提供一個變量副本。實現(xiàn)線程隔離
eg: 創(chuàng)建三個線程分別對變量count+線程id
-
未使用ThreadLocal:
public class NoThreadLocal {
static Integer count = new Integer(1);
/**
* 運行3個線程
*/
public void startTArray(){
Thread[] runs = new Thread[3];
for(int i=0;i<runs.length;i++){
runs[i]=new Thread(new TestTask(i));
}
for(int i=0;i<runs.length;i++){
runs[i].start();
}
}
/**
*類說明:
*/
public static class TestTask implements Runnable{
int id;
public TestTask(int id){
this.id = id;
}
public void run() {
System.out.println(Thread.currentThread().getName()+":start");
count = count+id;
System.out.println(Thread.currentThread().getName()+":"
+count);
}
}
public static void main(String[] args){
NoThreadLocal test = new NoThreadLocal();
test.startTArray();
}
}
打印結(jié)果:
Thread-1:start
Thread-1:2
Thread-0:start
Thread-0:2
Thread-2:start
Thread-2:4
并沒有每個線程按照 t0->0 、t1->1 、t2->2
-
使用ThreadLocal:
public class UseThreadLocal {
//TODO
private static ThreadLocal<Integer> intThreadLocal = new ThreadLocal<Integer>(){
int count = 0;
@Override
protected Integer initialValue() {
return count;
}
};
/**
* 運行3個線程
*/
public void StartThreadArray(){
Thread[] runs = new Thread[3];
for(int i=0;i<runs.length;i++){
runs[i]=new Thread(new TestThread(i));
}
for(int i=0;i<runs.length;i++){
runs[i].start();
}
}
/**
*類說明:測試線程,線程的工作是將ThreadLocal變量的值變化,并寫回,看看線程之間是否會互相影響
*/
public static class TestThread implements Runnable{
int id;
public TestThread(int id){
this.id = id;
}
public void run() {
System.out.println(Thread.currentThread().getName()+":start");
//TODO
int value = intThreadLocal.get()+id;
intThreadLocal.set(value);
System.out.println(Thread.currentThread().getName()+" : "+intThreadLocal.get());
}
}
public static void main(String[] args){
UseThreadLocal test = new UseThreadLocal();
test.StartThreadArray();
}
}
打印結(jié)果:
Thread-0:start
Thread-0 : 0
Thread-1:start
Thread-1 : 1
Thread-2:start
Thread-2 : 2
- ThreadLocal簡析:
前面說到ThreadLocal是本地變量副本。那么他是怎么實現(xiàn)的呢。由ThreadLocal的set()入手
ThreadLocal.java
public void set(T value) {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);//通過線程獲得ThreadLocalMap
if (map != null)
map.set(this, value);
else
createMap(t, value);
}
拿到 ThreadLocal.ThreadLocalMap threadLocals
可以看到ThreadLocal.ThreadLocalMap 里面有個Entity[]
Entity的具體模型:
static class Entry extends WeakReference<ThreadLocal<?>> {
/** The value associated with this ThreadLocal. */
Object value;
//以threadLocal為key value 為值存入
Entry(ThreadLocal<?> k, Object v) {
super(k);
value = v;
}
}
因為可能存在多個類型的threadlocal 所以需要使用數(shù)組


volatile的使用 (最輕量的同步機制)
適合一寫多讀的場景
- 優(yōu)點:保證可見性與快速更新
- 缺點:無法保證線程安全與操作原子性
使用 : - 可見性例子:
public class VolatileCase {
private static boolean ready;
private static int number;
private static class PrintThread extends Thread{
@Override
public void run() {
System.out.println("PrintThread is running.......");
while(!ready);
System.out.println("number = "+number);
}
}
public static void main(String[] args) {
new PrintThread().start();
SleepTools.second(1);
number = 51;
ready = true;
SleepTools.second(5);
System.out.println("main is ended!");
}
}
打印結(jié)果:
PrintThread is running.......
main is ended!
PrintThread并不知曉ready已經(jīng)變化
加入 volatile關(guān)鍵字
private volatile static boolean ready;
打印結(jié)果:
PrintThread is running.......
number = 51
main is ended!
線程不安全例子:
public class NotSafe {
private volatile long count =0;
public long getCount() {
return count;
}
public void setCount(long count) {
this.count = count;
}
//count進行累加
public void incCount(){
count++;
}
//線程
private static class Count extends Thread{
private NotSafe simplOper;
public Count(NotSafe simplOper) {
this.simplOper = simplOper;
}
@Override
public void run() {
for(int i=0;i<10000;i++){
simplOper.incCount();
}
}
}
public static void main(String[] args) throws InterruptedException {
NotSafe simplOper = new NotSafe();
//啟動兩個線程
Count count1 = new Count(simplOper);
Count count2 = new Count(simplOper);
count1.start();
count2.start();
Thread.sleep(50);
System.out.println(simplOper.count);
}
}
理想結(jié)果:20000
打印結(jié)果:
20000
13671
13529
因為線程的執(zhí)行是需要有cpu執(zhí)行權(quán)的 所以導(dǎo)致了結(jié)果的不確定性
synchronized的使用
synchronized 一定是作用在某個對象上 當(dāng)所在static 的方法 或者靜態(tài)塊 時 鎖住的是 X.class的對象
注:鎖只有作用在同個對象上才會起作用
public class SynTest {
private long count =0;
public long getCount() {
return count;
}
public void setCount(long count) {
this.count = count;
}
/*用在同步塊上*/
public void incCount(){
count++;
}
//線程
private static class Count extends Thread{
private SynTest simplOper;
public Count(SynTest simplOper) {
this.simplOper = simplOper;
}
@Override
public void run() {
for(int i=0;i<10000;i++){
simplOper.incCount();
}
}
}
public static void main(String[] args) throws InterruptedException {
SynTest simplOper = new SynTest();
//啟動兩個線程
Count count1 = new Count(simplOper);
Count count2 = new Count(simplOper);
count1.start();
count2.start();
Thread.sleep(50);
System.out.println(simplOper.count);//20000
}
}
打印結(jié)果:
18748 20000 16735 18307
加鎖:
public void incCount(){
synchronized (obj){
count++;
}
}
打印結(jié)果:
20000 20000 20000 20000
鎖的作用對象:
public class SynTest {
private long count =0;
private Object obj = new Object();//作為一個鎖
public long getCount() {
return count;
}
public void setCount(long count) {
this.count = count;
}
/*鎖的是SyncTest.class的類對象*/
public void incCount(){
synchronized (SyncTest.class){
count++;
}
}
/*用在方法上 鎖的是obj對象*/
public void incCount(){
synchronized (obj){
count++;
}
}
/*用在方法上 鎖的也是SynTest.class對象*/
public static synchronized void incCount2(){
count++;
}
/*用在方法上 鎖的也是當(dāng)前對象實例*/
public synchronized void incCount2(){
count++;
}
/*用在同步塊上,但是鎖的是當(dāng)前類的對象實例*/
public void incCount3(){
synchronized (this){
count++;
}
}
}