進程:
是任務的執行過程,持有資源(共享內存和共享文件)和線程
資源:
是進程和線程的載體
線程:
是系統中最小的執行單元
同一進程擁有多個線程
多個線程共享進程的資源
此圖畫的關系有些問題,因為thread也實現了Runnable接口
共享變量:
注:必須將共享變量變量聲明為volatile
使用volatile變量降低了發生內存一致性錯誤的風險, 因為任何對volatile變量的寫操作都與對該變量的讀操作建立了happens-before關系。這種關系意味著對volatile變量值的改變對其他線程總是可見的。更近一步, 當一個線程讀取volatile變量的時候,該線程不但讀取了最近的變化,而且是導致該變化發生代碼的全部影響
如何正確停止線程?
@Java線程——如何正確停止線程
一、錯誤一:stop()方法
1、not stop:stop()方法會使線程戛然而止
2、使程序突然中止,無法完成完整的業務步驟,也無法進行清理工作
二、錯誤二:interrupt()方法
1、interrupt()方法只能設置interrupt標志位(且在線程阻塞情況下,標志位會被清除,更無法設置中斷標志位),無法停止線程
三、正確方法:設置退出標志
1、使用退出標志位來停止while循環
2、完成最后一次業務后跳出while循環后,之后進行一些清理
interrupt()方法不會中斷一個正在運行的線程。這一方法實際上完成的是,在線程受到阻塞時拋出一個中斷信號,這樣線程就得以退出阻塞的狀態。更確切的說,如果線程被Object.wait, Thread.join和Thread.sleep三種方法之一阻塞,那么,它將接收到一個中斷異常(InterruptedException),從而提早地終結被阻塞狀態,然后該線程還是繼續運行的
線程常用方法
1.獲取線程名稱:getName();
2.取得當前線程對象:currentThread();
3.判斷是否啟動:isAlive();
4.強行運行:join();
5.線程休眠:sleep();
6.線程禮讓:yield();
什么時候使用thread 什么時候使用runnable
其實兩種方式效果一樣,但推薦使用runnable
因為Java是單繼承,繼承了thread就不能繼承其他的類
而實現runnable接口的話,擴展性要好很多
@Java線程——線程交互——爭用條件
1、當多個線程同時共享訪問同一數據(內存區域)時,每個線程都嘗試操作該數據,從而導致數據被破壞(corrupted),這種現象稱為爭用條件
2、原因是,每個線程在操作數據時,會先將數據初值讀【取到自己獲得的內存中】,然后在內存中進行運算后,重新賦值到數據。
3、爭用條件:線程1在還【未重新將值賦回去時】,線程1阻塞,線程2開始訪問該數據,然后進行了修改,之后被阻塞的線程1再獲得資源,而將之前計算的值覆蓋掉線程2所修改的值,就出現了數據丟失情況
線程的交互:互斥與同步
互斥:在同一時間只能有一條線程對關鍵數據或臨界區進行操作
同步:線程之間的一種通信機制
一條線程做了一件事情,然后用某種方式去告訴其它線程:"我做完了"
synchronized關鍵字實現互斥行為,既可以出現在方法體之上也可以出現在方法體內,以一種塊的形式出現。
然后通過lockObject的wait方法(注意:wait的線程被存放在wait set 中)和notifyAll方法實現同步。
步驟:
1.互斥:同一時間,只能有一個線程訪問數據
2.同步:通信機制;一個線程完成,以某種方式通知其他線程
3.鎖的概念:private final Object lockObj = new Object();
4.互斥實現方式:synchronized關鍵字
synchronized(lockObj){---執行代碼----}加鎖操作
lockObj.wait();線程等待狀態,以避免線程持續申請鎖,不去競爭cpu資源
lockObj.notifyAll();喚醒所有lockObj對象上等待的線程
同步是兩個線程之間的一種交互的操作(一個線程發出消息另外一個線程響應)。
同步的實現:wait();notify();notifyAll();這三個方法都是屬于Java中的Object對象的成員函數。
調用wait();和notifyAll();方法使線程進入等待或者喚醒不是在同一個線程的同一次操作中執行的,當操作結束,喚醒了所有的等待線程之后,線程又將有著公平的機會競爭CPU資源。
注意:notify();方法喚醒wait set 中的一條線程使其具有競爭CPU的機會,具體喚醒那一條線程是隨機的由Java的底層算法決定,我們不能去控制。
通過synchronized關鍵字為臨界區(critical)加鎖,這樣在線程競爭資源時,當某一條線程獲得鎖進入臨界區后,其他線程將無法再次獲取鎖進入臨界區(critical),直到獲得鎖的線程退出臨界區(critical),釋放鎖資源。Java的語法保證了同一時間只能有一條線程可以獲得lockObject。
java
1.原子性:Java內存模型只保證了基本讀取和賦值是原子性操作,如果要實現更大范圍操作的原子性,可以通過synchronized和Lock來實現。由于synchronized和Lock能夠保證任一時刻只有一個線程執行該代碼塊,那么自然就不存在原子性問題了,從而保證了原子性。
2.可見性:對于可見性,Java提供了volatile關鍵字來保證可見性。當一個共享變量被volatile修飾時,它會保證修改的值會立即被更新到主存,當有其他線程需要讀取時,它會去內存中讀取新值。而普通的共享變量不能保證可見性,因為普通共享變量被修改之后,什么時候被寫入主存是不確定的,當其他線程去讀取時,此時內存中可能還是原來的舊值,因此無法保證可見性。
另外,通過synchronized和Lock也能夠保證可見性,synchronized和Lock能保證同一時刻只有一個線程獲取鎖然后執行同步代碼,并且在釋放鎖之前會將對變量的修改刷新到主存當中。因此可以保證可見性。
3.有序性
在Java內存模型中,允許編譯器和處理器對指令進行重排序,但是重排序過程不會影響到單線程程序的執行,卻會影響到多線程并發執行的正確性。
在Java里面,可以通過volatile關鍵字來保證一定的“有序性”(具體原理在下一節講述)。另外可以通過synchronized和Lock來保證有序性,很顯然,synchronized和Lock保證每個時刻是有一個線程執行同步代碼,相當于是讓線程順序執行同步代碼,自然就保證了有序性。
另外,Java內存模型具備一些先天的“有序性”,即不需要通過任何手段就能夠得到保證的有序性,這個通常也稱為 happens-before 原則。如果兩個操作的執行次序無法從happens-before原則推導出來,那么它們就不能保證它們的有序性,虛擬機可以隨意地對它們進行重排序。
http://www.cnblogs.com/dolphin0520/p/3920373.html
建議:
1、Java Memory Mode:JMM描述了java線程如何通過內存進行交互,了解happens-before,synchronized,voliatile & final
2、Locks % Condition:鎖機制和等待條件的高層實現 java.util,concurrent.locks
3、線程安全性:原子性與可見性,死鎖等
4、多線程常用的交互模型
· Producer-Consumer模型
· Read-Write Lock模型
· Future模型
· Worker Thread模型
5、Java5中并發編程工具:java.util.concurrent 線程池ExcutorService Callable&Future BlockingQueue
6、推薦書本:CoreJava & JavaConcurrency In Practice