java同步線程
『壹』 java中線程同步方法有哪些
JAVA中線程同步方法一般有以下三種:
1 wait方法:
該方法屬於Object的方法,wait方法的作用是使得當前調用wait方法所在部分(代碼塊)的線程停止執行,並釋放當前獲得的調用wait所在的代碼塊的鎖,並在其他線程調用notify或者notifyAll方法時恢復到競爭鎖狀態(一旦獲得鎖就恢復執行)。
調用wait方法需要注意幾點:
第一點:wait被調用的時候必須在擁有鎖(即synchronized修飾的)的代碼塊中。
第二點:恢復執行後,從wait的下一條語句開始執行,因而wait方法總是應當在while循環中調用,以免出現恢復執行後繼續執行的條件不滿足卻繼續執行的情況。
第三點:若wait方法參數中帶時間,則除了notify和notifyAll被調用能激活處於wait狀態(等待狀態)的線程進入鎖競爭外,在其他線程中interrupt它或者參數時間到了之後,該線程也將被激活到競爭狀態。
第四點:wait方法被調用的線程必須獲得之前執行到wait時釋放掉的鎖重新獲得才能夠恢復執行。
2 notify方法和notifyAll方法:
notify方法通知調用了wait方法,但是尚未激活的一個線程進入線程調度隊列(即進入鎖競爭),注意不是立即執行。並且具體是哪一個線程不能保證。另外一點就是被喚醒的這個線程一定是在等待wait所釋放的鎖。
notifyAll方法則喚醒所有調用了wait方法,尚未激活的進程進入競爭隊列。
3 synchronized關鍵字:
第一點:synchronized用來標識一個普通方法時,表示一個線程要執行該方法,必須取得該方法所在的對象的鎖。
第二點:synchronized用來標識一個靜態方法時,表示一個線程要執行該方法,必須獲得該方法所在的類的類鎖。
第三點:synchronized修飾一個代碼塊。類似這樣:synchronized(obj) { //code.... }。表示一個線程要執行該代碼塊,必須獲得obj的鎖。這樣做的目的是減小鎖的粒度,保證當不同塊所需的鎖不沖突時不用對整個對象加鎖。利用零長度的byte數組對象做obj非常經濟。
『貳』 Java線程同步,是什麼意思
一種是方法前加sychronized
public void sychronized start() {
System.out.println("start");
}
另一種是在代碼段之前加sychronized
(sychronized){
。。。。。
}
同步方法(synchronized關鍵字修飾的方法)可以較好地解決並發問題,在一定程度上可以避免出現資源搶占、競爭條件和死鎖的情況,但其副作用是同步鎖可導致線程阻塞。這要求同步方法的執行時間不能太長。
這就是所謂的鎖機制,你何以使用sychronized(Object obj)鎖住某個對象,等你使用完這個對象之後,再進行鎖的釋放,其他需要該對象的線程才可以執行。
『叄』 昆明Java培訓:java語言中線程同步的方法有哪些
答:wait():使一個線程處於等待狀態,並且釋放所持有的對象的lock。
sleep():使一個正在運行的線程處於睡眠狀態,是一個靜態方法,調用此方法要捕捉InterruptedException異常。
notify():喚醒一個處於等待狀態的線程,注意的是在調用此方法的時候,並不能確切的喚醒某一個等待狀態的線程,而是由JVM確定喚醒哪個線程,而且不是按優先順序。
Allnotity():喚醒所有處入等待狀態的線程,注意並不是給所有喚醒線程一個對象的鎖,而是讓它們競爭。
『肆』 java線程同步問題
首先,你如果要線程同步的話,那麼需要用關鍵字去定義。你如果不去定義讓線程同步的話,那麼線程永遠都是串列的,因為每個時間片段只會執行一部分
Java線程:線程的同步-同步方法
線程的同步是保證多線程安全訪問競爭資源的一種手段。
線程的同步是Java多線程編程的難點,往往開發者搞不清楚什麼是競爭資源、什麼時候需要考慮同步,怎麼同步等等問題,當然,這些問題沒有很明確的答案,但有些原則問題需要考慮,是否有競爭資源被同時改動的問題?
在本文之前,請參閱《Java線程:線程的同步與鎖》,本文是在此基礎上所寫的。
對於同步,在具體的Java代碼中需要完成一下兩個操作:
把競爭訪問的資源標識為private;
同步哪些修改變數的代碼,使用synchronized關鍵字同步方法或代碼。
當然這不是唯一控制並發安全的途徑。
synchronized關鍵字使用說明
synchronized只能標記非抽象的方法,不能標識成員變數。
為了演示同步方法的使用,構建了一個信用卡賬戶,起初信用額為100w,然後模擬透支、存款等多個操作。顯然銀行賬戶User對象是個競爭資源,而多個並發操作的是賬戶方法oper(int x),當然應該在此方法上加上同步,並將賬戶的余額設為私有變數,禁止直接訪問。
/**
* Java線程:線程的同步
*
* @author leimin 2009-11-4 11:23:32
*/
public class Test {
public static void main(String[] args) {
User u = new User("張三", 100);
MyThread t1 = new MyThread("線程A", u, 20);
MyThread t2 = new MyThread("線程B", u, -60);
MyThread t3 = new MyThread("線程C", u, -80);
MyThread t4 = new MyThread("線程D", u, -30);
MyThread t5 = new MyThread("線程E", u, 32);
MyThread t6 = new MyThread("線程F", u, 21);
t1.start();
t2.start();
t3.start();
t4.start();
t5.start();
t6.start();
}
}
class MyThread extends Thread {
private User u;
private int y = 0;
MyThread(String name, User u, int y) {
super(name);
this.u = u;
this.y = y;
}
public void run() {
u.oper(y);
}
}
class User {
private String code;
private int cash;
User(String code, int cash) {
this.code = code;
this.cash = cash;
}
public String getCode() {
return code;
}
public void setCode(String code) {
this.code = code;
}
/**
* 業務方法
* @param x 添加x萬元
*/
public synchronized void oper(int x) {
try {
Thread.sleep(10L);
this.cash += x;
System.out.println(Thread.currentThread().getName() + "運行結束,增加「" + x + "」,當前用戶賬戶余額為:" + cash);
Thread.sleep(10L);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
@Override
public String toString() {
return "User{" +
"code='" + code + '\'' +
", cash=" + cash +
'}';
}
}
輸出結果:
線程A運行結束,增加「20」,當前用戶賬戶余額為:120
線程F運行結束,增加「21」,當前用戶賬戶余額為:141
線程E運行結束,增加「32」,當前用戶賬戶余額為:173
線程C運行結束,增加「-80」,當前用戶賬戶余額為:93
線程B運行結束,增加「-60」,當前用戶賬戶余額為:33
線程D運行結束,增加「-30」,當前用戶賬戶余額為:3
Process finished with exit code 0
反面教材,不同步的情況,也就是去掉oper(int x)方法的synchronized修飾符,然後運行程序,結果如下:
線程A運行結束,增加「20」,當前用戶賬戶余額為:61
線程D運行結束,增加「-30」,當前用戶賬戶余額為:63
線程B運行結束,增加「-60」,當前用戶賬戶余額為:3
線程F運行結束,增加「21」,當前用戶賬戶余額為:61
線程E運行結束,增加「32」,當前用戶賬戶余額為:93
線程C運行結束,增加「-80」,當前用戶賬戶余額為:61
Process finished with exit code 0
很顯然,上面的結果是錯誤的,導致錯誤的原因是多個線程並發訪問了競爭資源u,並對u的屬性做了改動。
可見同步的重要性。
注意:
通過前文可知,線程退出同步方法時將釋放掉方法所屬對象的鎖,但還應該注意的是,同步方法中還可以使用特定的方法對線程進行調度。這些方法來自於java.lang.Object類。
void notify()
喚醒在此對象監視器上等待的單個線程。
void notifyAll()
喚醒在此對象監視器上等待的所有線程。
void wait()
導致當前的線程等待,直到其他線程調用此對象的 notify() 方法或 notifyAll() 方法。
void wait(long timeout)
導致當前的線程等待,直到其他線程調用此對象的 notify() 方法或 notifyAll() 方法,或者超過指定的時間量。
void wait(long timeout, int nanos)
導致當前的線程等待,直到其他線程調用此對象的 notify() 方法或 notifyAll() 方法,或者其他某個線程中斷當前線程,或者已超過某個實際時間量。
『伍』 Java類的實例化順序是什麼樣的Java線程同步的方式有哪些
引言:java是在1990年初 ,被詹姆斯•高斯林等人開發的一門面向對象的編程語言。起初,java被稱為0ak,來經過發展0ak改名為java,與1995年的五月份正式向大家發布。
一、java類的實例化順序
java的實例化順序在繼承沒有的情況
單獨一個類的場景下,初始化順序為依次為靜態數據,繼承的基類的構造函數,成員變數,被調用的構造函數。
其中靜態數據只會初始化一次。(靜態數據包括靜態代碼塊和靜態變數,每個類的靜態數據只會初始化一次)
在繼承的情況下
添加兩個基類,讓繼承父親,父親繼承祖父。
繼承的情況就比較復雜了。由繼承了基類,還將往上回溯,遞歸地調用基類的無參構造方法。
在我們的例子中,在初始化靜態數據後,會先往上追溯,調用父的默認構造方法,此時再往上追溯到爺爺的默認構造方法。
無論是java還是什麼別的東西他都體現了現代社會與信息技術的不斷發展,人們在進行進行技術開發時也有了越來越多的方法。程序類的工作也有了更為快捷的方法,這為信息技術的發展也提供了更好的發展方法
『陸』 java多線程有幾種實現方法,都是什麼同步有幾種實現方法,都是什麼
java中多線程的實現方法有兩種:1.直接繼承thread類;2.實現runnable介面;同步的實現方法有五種:1.同步方法;2.同步代碼塊;3.使用特殊域變數(volatile)實現線程同步;4.使用重入鎖實現線程同步;5.使用局部變數實現線程同步
。
其中多線程實現過程中需注意重寫或者覆蓋run()方法,而對於同步的實現方法中使用較常使用的是利用synchronized編寫同步方法和代碼塊。
『柒』 java多線程有幾種實現方法線程之間如何同步
一、為什麼要線程同步
因為當我們有多個線程要同時訪問一個變數或對象時,如果這些線程中既有讀又有寫操作時,就會導致變數值或對象的狀態出現混亂,從而導致程序異常。舉個例子,如果一個銀行賬戶同時被兩個線程操作,一個取100塊,一個存錢100塊。假設賬戶原本有0塊,如果取錢線程和存錢線程同時發生,會出現什麼結果呢?取錢不成功,賬戶余額是100.取錢成功了,賬戶余額是0.那到底是哪個呢?很難說清楚。因此多線程同步就是要解決這個問題。
二、不同步時的代碼
Bank.Java
packagethreadTest;
/**
*@authorww
*
*/
publicclassBank{
privateintcount=0;//賬戶余額
//存錢
publicvoidaddMoney(intmoney){
count+=money;
System.out.println(System.currentTimeMillis()+"存進:"+money);
}
//取錢
publicvoidsubMoney(intmoney){
if(count-money<0){
System.out.println("余額不足");
return;
}
count-=money;
System.out.println(+System.currentTimeMillis()+"取出:"+money);
}
//查詢
publicvoidlookMoney(){
System.out.println("賬戶余額:"+count);
}
}
SyncThreadTest.java
packagethreadTest;
publicclassSyncThreadTest{
publicstaticvoidmain(Stringargs[]){
finalBankbank=newBank();
Threadtadd=newThread(newRunnable(){
@Override
publicvoidrun(){
//TODOAuto-generatedmethodstub
while(true){
try{
Thread.sleep(1000);
}catch(InterruptedExceptione){
//TODOAuto-generatedcatchblock
e.printStackTrace();
}
bank.addMoney(100);
bank.lookMoney();
System.out.println(" ");
}
}
});
Threadtsub=newThread(newRunnable(){
@Override
publicvoidrun(){
//TODOAuto-generatedmethodstub
while(true){
bank.subMoney(100);
bank.lookMoney();
System.out.println(" ");
try{
Thread.sleep(1000);
}catch(InterruptedExceptione){
//TODOAuto-generatedcatchblock
e.printStackTrace();
}
}
}
});
tsub.start();
tadd.start();
}
}
余額不足
賬戶余額:0
余額不足
賬戶余額:100
1441790503354存進:100
賬戶余額:100
1441790504354存進:100
賬戶余額:100
1441790504354取出:100
賬戶余額:100
1441790505355存進:100
賬戶余額:100
1441790505355取出:100
賬戶余額:100
三、使用同步時的代碼
(1)同步方法:
即有synchronized關鍵字修飾的方法。由於java的每個對象都有一個內置鎖,當用此關鍵字修飾方法時,內置鎖會保護整個方法。在調用該方法前,需要獲得內置鎖,否則就處於阻塞狀態。
修改後的Bank.java
packagethreadTest;
/**
*@authorww
*
*/
publicclassBank{
privateintcount=0;//賬戶余額
//存錢
(intmoney){
count+=money;
System.out.println(System.currentTimeMillis()+"存進:"+money);
}
//取錢
(intmoney){
if(count-money<0){
System.out.println("余額不足");
return;
}
count-=money;
System.out.println(+System.currentTimeMillis()+"取出:"+money);
}
//查詢
publicvoidlookMoney(){
System.out.println("賬戶余額:"+count);
}
}
再看看運行結果:
余額不足
賬戶余額:0
余額不足
賬戶余額:0
1441790837380存進:100
賬戶余額:100
1441790838380取出:100
賬戶余額:0
1441790838380存進:100
賬戶余額:100
1441790839381取出:100
賬戶余額:0
瞬間感覺可以理解了吧。
註: synchronized關鍵字也可以修飾靜態方法,此時如果調用該靜態方法,將會鎖住整個類
(2)同步代碼塊
即有synchronized關鍵字修飾的語句塊。被該關鍵字修飾的語句塊會自動被加上內置鎖,從而實現同步
Bank.java代碼如下:
packagethreadTest;
/**
*@authorww
*
*/
publicclassBank{
privateintcount=0;//賬戶余額
//存錢
publicvoidaddMoney(intmoney){
synchronized(this){
count+=money;
}
System.out.println(System.currentTimeMillis()+"存進:"+money);
}
//取錢
publicvoidsubMoney(intmoney){
synchronized(this){
if(count-money<0){
System.out.println("余額不足");
return;
}
count-=money;
}
System.out.println(+System.currentTimeMillis()+"取出:"+money);
}
//查詢
publicvoidlookMoney(){
System.out.println("賬戶余額:"+count);
}
}
運行結果如下:
余額不足
賬戶余額:0
1441791806699存進:100
賬戶余額:100
1441791806700取出:100
賬戶余額:0
1441791807699存進:100
賬戶余額:100
效果和方法一差不多。
註:同步是一種高開銷的操作,因此應該盡量減少同步的內容。通常沒有必要同步整個方法,使用synchronized代碼塊同步關鍵代碼即可。
(3)使用特殊域變數(volatile)實現線程同步
a.volatile關鍵字為域變數的訪問提供了一種免鎖機制
b.使用volatile修飾域相當於告訴虛擬機該域可能會被其他線程更新
c.因此每次使用該域就要重新計算,而不是使用寄存器中的值
d.volatile不會提供任何原子操作,它也不能用來修飾final類型的變數
Bank.java代碼如下:
packagethreadTest;
/**
*@authorww
*
*/
publicclassBank{
privatevolatileintcount=0;//賬戶余額
//存錢
publicvoidaddMoney(intmoney){
count+=money;
System.out.println(System.currentTimeMillis()+"存進:"+money);
}
//取錢
publicvoidsubMoney(intmoney){
if(count-money<0){
System.out.println("余額不足");
return;
}
count-=money;
System.out.println(+System.currentTimeMillis()+"取出:"+money);
}
//查詢
publicvoidlookMoney(){
System.out.println("賬戶余額:"+count);
}
}
運行效果怎樣呢?
余額不足
賬戶余額:0
余額不足
賬戶余額:100
1441792010959存進:100
賬戶余額:100
1441792011960取出:100
賬戶余額:0
1441792011961存進:100
賬戶余額:100
是不是又看不懂了,又亂了。這是為什麼呢?就是因為volatile不能保證原子操作導致的,因此volatile不能代替synchronized。此外volatile會組織編譯器對代碼優化,因此能不使用它就不適用它吧。它的原理是每次要線程要訪問volatile修飾的變數時都是從內存中讀取,而不是存緩存當中讀取,因此每個線程訪問到的變數值都是一樣的。這樣就保證了同步。
(4)使用重入鎖實現線程同步
在JavaSE5.0中新增了一個java.util.concurrent包來支持同步。ReentrantLock類是可重入、互斥、實現了Lock介面的鎖,它與使用synchronized方法和快具有相同的基本行為和語義,並且擴展了其能力。
ReenreantLock類的常用方法有:
ReentrantLock() : 創建一個ReentrantLock實例
lock() : 獲得鎖
unlock() : 釋放鎖
註:ReentrantLock()還有一個可以創建公平鎖的構造方法,但由於能大幅度降低程序運行效率,不推薦使用
Bank.java代碼修改如下:
packagethreadTest;
importjava.util.concurrent.locks.Lock;
importjava.util.concurrent.locks.ReentrantLock;
/**
*@authorww
*
*/
publicclassBank{
privateintcount=0;//賬戶余額
//需要聲明這個鎖
privateLocklock=newReentrantLock();
//存錢
publicvoidaddMoney(intmoney){
lock.lock();//上鎖
try{
count+=money;
System.out.println(System.currentTimeMillis()+"存進:"+money);
}finally{
lock.unlock();//解鎖
}
}
//取錢
publicvoidsubMoney(intmoney){
lock.lock();
try{
if(count-money<0){
System.out.println("余額不足");
return;
}
count-=money;
System.out.println(+System.currentTimeMillis()+"取出:"+money);
}finally{
lock.unlock();
}
}
//查詢
publicvoidlookMoney(){
System.out.println("賬戶余額:"+count);
}
}
運行效果怎麼樣呢?
余額不足
賬戶余額:0
余額不足
賬戶余額:0
1441792891934存進:100
賬戶余額:100
1441792892935存進:100
賬戶余額:200
1441792892954取出:100
賬戶余額:100
效果和前兩種方法差不多。
如果synchronized關鍵字能滿足用戶的需求,就用synchronized,因為它能簡化代碼 。如果需要更高級的功能,就用ReentrantLock類,此時要注意及時釋放鎖,否則會出現死鎖,通常在finally代碼釋放鎖
(5)使用局部變數實現線程同步
Bank.java代碼如下:
packagethreadTest;
/**
*@authorww
*
*/
publicclassBank{
privatestaticThreadLocal<Integer>count=newThreadLocal<Integer>(){
@Override
protectedIntegerinitialValue(){
//TODOAuto-generatedmethodstub
return0;
}
};
//存錢
publicvoidaddMoney(intmoney){
count.set(count.get()+money);
System.out.println(System.currentTimeMillis()+"存進:"+money);
}
//取錢
publicvoidsubMoney(intmoney){
if(count.get()-money<0){
System.out.println("余額不足");
return;
}
count.set(count.get()-money);
System.out.println(+System.currentTimeMillis()+"取出:"+money);
}
//查詢
publicvoidlookMoney(){
System.out.println("賬戶余額:"+count.get());
}
}
運行效果:
余額不足
賬戶余額:0
余額不足
賬戶余額:0
1441794247939存進:100
賬戶余額:100
余額不足
1441794248940存進:100
賬戶余額:0
賬戶余額:200
余額不足
賬戶余額:0
1441794249941存進:100
賬戶余額:300
看了運行效果,一開始一頭霧水,怎麼只讓存,不讓取啊?看看ThreadLocal的原理:
如果使用ThreadLocal管理變數,則每一個使用該變數的線程都獲得該變數的副本,副本之間相互獨立,這樣每一個線程都可以隨意修改自己的變數副本,而不會對其他線程產生影響。現在明白了吧,原來每個線程運行的都是一個副本,也就是說存錢和取錢是兩個賬戶,知識名字相同而已。所以就會發生上面的效果。
ThreadLocal與同步機制
a.ThreadLocal與同步機制都是為了解決多線程中相同變數的訪問沖突問題
b.前者採用以"空間換時間"的方法,後者採用以"時間換空間"的方式
『捌』 Java 線程同步幾種方式
(1)同步方法:
即有synchronized關鍵字修飾的方法。 由於java的每個對象都有一個內置鎖,當用此關鍵字修飾方法時,內置鎖會保護整個方法。在調用該方法前,需要獲得內置鎖,否則就處於阻塞狀態。
(2)同步代碼塊
即有synchronized關鍵字修飾的語句塊。被該關鍵字修飾的語句塊會自動被加上內置鎖,從而實現同步
(3)使用特殊域變數(Volatile)實現線程同步
a.volatile關鍵字為域變數的訪問提供了一種免鎖機制
b.使用volatile修飾域相當於告訴虛擬機該域可能會被其他線程更新
c.因此每次使用該域就要重新計算,而不是使用寄存器中的值
d.volatile不會提供任何原子操作,它也不能用來修飾final類型的變數
(4)使用重入鎖實現線程同步
在JavaSE5.0中新增了一個java.util.concurrent包來支持同步。ReentrantLock類是可重入、互斥、實現了Lock介面的鎖, 它與使用synchronized方法和快具有相同的基本行為和語義,並且擴展了其能力。
(5)使用局部變數實現線程同步