在Java中一共有四種方法支持同步,其中前三個(gè)是同步方法,一個(gè)是管道方法。管道方法不建議使用。
在漢川等地區(qū),都構(gòu)建了全面的區(qū)域性戰(zhàn)略布局,加強(qiáng)發(fā)展的系統(tǒng)性、市場(chǎng)前瞻性、產(chǎn)品創(chuàng)新能力,以專(zhuān)注、極致的服務(wù)理念,為客戶(hù)提供成都網(wǎng)站建設(shè)、成都網(wǎng)站設(shè)計(jì) 網(wǎng)站設(shè)計(jì)制作按需設(shè)計(jì)網(wǎng)站,公司網(wǎng)站建設(shè),企業(yè)網(wǎng)站建設(shè),成都品牌網(wǎng)站建設(shè),營(yíng)銷(xiāo)型網(wǎng)站,成都外貿(mào)網(wǎng)站建設(shè),漢川網(wǎng)站建設(shè)費(fèi)用合理。
wait()/notify()方法
await()/signal()方法
BlockingQueue阻塞隊(duì)列方法
PipedInputStream/PipedOutputStream
阻塞隊(duì)列的一個(gè)簡(jiǎn)單實(shí)現(xiàn):
public class BlockingQueue {
private List queue = new LinkedList();
private int ?limit = 10;
public BlockingQueue(int limit){
this.limit = limit;
}
public synchronized void enqueue(Object item)throws InterruptedException ?{
while(this.queue.size() == this.limit) {
wait();
}
if(this.queue.size() == 0) {
notifyAll();
}
this.queue.add(item);
}
public synchronized Object dequeue() ?throws InterruptedException{
while(this.queue.size() == 0){
wait();
}
if(this.queue.size() == this.limit){
notifyAll();
}
return this.queue.remove(0);
}}
在enqueue和dequeue方法內(nèi)部,只有隊(duì)列的大小等于上限(limit)或者下限(0)時(shí),才調(diào)用notifyAll方法。如果隊(duì)列的大小既不等于上限,也不等于下限,任何線程調(diào)用enqueue或者dequeue方法時(shí),都不會(huì)阻塞,都能夠正常的往隊(duì)列中添加或者移除元素。
wait()/notify()方法
生產(chǎn)者的主要作用是生成一定量的數(shù)據(jù)放到緩沖區(qū)中,然后重復(fù)此過(guò)程。與此同時(shí),消費(fèi)者也在緩沖區(qū)消耗這些數(shù)據(jù)。該問(wèn)題的關(guān)鍵就是要保證生產(chǎn)者不會(huì)在緩沖區(qū)滿(mǎn)時(shí)加入數(shù)據(jù),消費(fèi)者也不會(huì)在緩沖區(qū)中空時(shí)消耗數(shù)據(jù)。
要解決該問(wèn)題,就必須讓生產(chǎn)者在緩沖區(qū)滿(mǎn)時(shí)休眠(要么干脆就放棄數(shù)據(jù)),等到下次消費(fèi)者消耗緩沖區(qū)中的數(shù)據(jù)的時(shí)候,生產(chǎn)者才能被喚醒,開(kāi)始往緩沖區(qū)添加數(shù)據(jù)。同樣,也可以讓消費(fèi)者在緩沖區(qū)空時(shí)進(jìn)入休眠,等到生產(chǎn)者往緩沖區(qū)添加數(shù)據(jù)之后,再喚醒消費(fèi)者。
1.同步方法 即有synchronized關(guān)鍵字修飾的方法。 由于java的每個(gè)對(duì)象都有一個(gè)內(nèi)置鎖,當(dāng)用此關(guān)鍵字修飾方法時(shí), 內(nèi)置鎖會(huì)保護(hù)整個(gè)方法。在調(diào)用該方法前,需要獲得內(nèi)置鎖,否則就處于阻塞狀態(tài)。 代碼如: public synchronized void save(){} 注: synchronized關(guān)鍵字也可以修飾靜態(tài)方法,此時(shí)如果調(diào)用該靜態(tài)方法,將會(huì)鎖住整個(gè)類(lèi) 2.同步代碼塊 即有synchronized關(guān)鍵字修飾的語(yǔ)句塊。 被該關(guān)鍵字修飾的語(yǔ)句塊會(huì)自動(dòng)被加上內(nèi)置鎖,從而實(shí)現(xiàn)同步 代碼如: synchronized(object){ } 注:同步是一種高開(kāi)銷(xiāo)的操作,因此應(yīng)該盡量減少同步的內(nèi)容。 通常沒(méi)有必要同步整個(gè)方法,使用synchronized代碼塊同步關(guān)鍵代碼即可。 代碼實(shí)例: 復(fù)制代碼package com.xhj.thread; /** * 線程同步的運(yùn)用 * * @author XIEHEJUN * */ public class SynchronizedThread { class Bank { private int account = 100; public int getAccount() { return account; } /** * 用同步方法實(shí)現(xiàn) * * @param money */ public synchronized void save(int money) { account += money; } /** * 用同步代碼塊實(shí)現(xiàn) * * @param money */ public void save1(int money) { synchronized (this) { account += money; } } } class NewThread implements Runnable { private Bank bank; public NewThread(Bank bank) { this.bank = bank; } @Override public void run() { for (int i = 0; i 10; i++) { // bank.save1(10); bank.save(10); System.out.println(i + "賬戶(hù)余額為:" + bank.getAccount()); } } } /** * 建立線程,調(diào)用內(nèi)部類(lèi) */ public void useThread() { Bank bank = new Bank(); NewThread new_thread = new NewThread(bank); System.out.println("線程1"); Thread thread1 = new Thread(new_thread); thread1.start(); System.out.println("線程2"); Thread thread2 = new Thread(new_thread); thread2.start(); } public static void main(String[] args) { SynchronizedThread st = new SynchronizedThread(); st.useThread(); } }復(fù)制代碼 3.使用特殊域變量(volatile)實(shí)現(xiàn)線程同步 a.volatile關(guān)鍵字為域變量的訪問(wèn)提供了一種免鎖機(jī)制, b.使用volatile修飾域相當(dāng)于告訴虛擬機(jī)該域可能會(huì)被其他線程更新, c.因此每次使用該域就要重新計(jì)算,而不是使用寄存器中的值 d.volatile不會(huì)提供任何原子操作,它也不能用來(lái)修飾final類(lèi)型的變量 例如: 在上面的例子當(dāng)中,只需在account前面加上volatile修飾,即可實(shí)現(xiàn)線程同步。 代碼實(shí)例: 復(fù)制代碼 //只給出要修改的代碼,其余代碼與上同 class Bank { //需要同步的變量加上volatile private volatile int account = 100; public int getAccount() { return account; } //這里不再需要synchronized public void save(int money) { account += money; } }復(fù)制代碼 注:多線程中的非同步問(wèn)題主要出現(xiàn)在對(duì)域的讀寫(xiě)上,如果讓域自身避免這個(gè)問(wèn)題,則就不需要修改操作該域的方法。 用final域,有鎖保護(hù)的域和volatile域可以避免非同步的問(wèn)題。 4.使用重入鎖實(shí)現(xiàn)線程同步 在JavaSE5.0中新增了一個(gè)java.util.concurrent包來(lái)支持同步。 ReentrantLock類(lèi)是可重入、互斥、實(shí)現(xiàn)了Lock接口的鎖, 它與使用synchronized方法和快具有相同的基本行為和語(yǔ)義,并且擴(kuò)展了其能力 ReenreantLock類(lèi)的常用方法有: ReentrantLock() : 創(chuàng)建一個(gè)ReentrantLock實(shí)例 lock() : 獲得鎖 unlock() : 釋放鎖 注:ReentrantLock()還有一個(gè)可以創(chuàng)建公平鎖的構(gòu)造方法,但由于能大幅度降低程序運(yùn)行效率,不推薦使用 例如: 在上面例子的基礎(chǔ)上,改寫(xiě)后的代碼為: 代碼實(shí)例: 復(fù)制代碼//只給出要修改的代碼,其余代碼與上同 class Bank { private int account = 100; //需要聲明這個(gè)鎖 private Lock lock = new ReentrantLock(); public int getAccount() { return account; } //這里不再需要synchronized public void save(int money) { lock.lock(); try{ account += money; }finally{ lock.unlock(); } } }復(fù)制代碼 注:關(guān)于Lock對(duì)象和synchronized關(guān)鍵字的選擇: a.最好兩個(gè)都不用,使用一種java.util.concurrent包提供的機(jī)制, 能夠幫助用戶(hù)處理所有與鎖相關(guān)的代碼。 b.如果synchronized關(guān)鍵字能滿(mǎn)足用戶(hù)的需求,就用synchronized,因?yàn)樗芎?jiǎn)化代碼 c.如果需要更高級(jí)的功能,就用ReentrantLock類(lèi),此時(shí)要注意及時(shí)釋放鎖,否則會(huì)出現(xiàn)死鎖,通常在finally代碼釋放鎖 5.使用局部變量實(shí)現(xiàn)線程同步 如果使用ThreadLocal管理變量,則每一個(gè)使用該變量的線程都獲得該變量的副本, 副本之間相互獨(dú)立,這樣每一個(gè)線程都可以隨意修改自己的變量副本,而不會(huì)對(duì)其他線程產(chǎn)生影響。 ThreadLocal 類(lèi)的常用方法 ThreadLocal() : 創(chuàng)建一個(gè)線程本地變量 get() : 返回此線程局部變量的當(dāng)前線程副本中的值 initialValue() : 返回此線程局部變量的當(dāng)前線程的"初始值" set(T value) : 將此線程局部變量的當(dāng)前線程副本中的值設(shè)置為value 例如: 在上面例子基礎(chǔ)上,修改后的代碼為: 代碼實(shí)例: 復(fù)制代碼//只改Bank類(lèi),其余代碼與上同 public class Bank{ //使用ThreadLocal類(lèi)管理共享變量account private static ThreadLocalInteger account = new ThreadLocalInteger(){ @Override protected Integer initialValue(){ return 100; } }; public void save(int money){ account.set(account.get()+money); } public int getAccount(){ return account.get(); } }復(fù)制代碼 注:ThreadLocal與同步機(jī)制 a.ThreadLocal與同步機(jī)制都是為了解決多線程中相同變量的訪問(wèn)沖突問(wèn)題。 b.前者采用以"空間換時(shí)間"的方法,后者采用以"時(shí)間換空間"的方式。
同步代碼塊是并發(fā)的時(shí)候鎖定一個(gè)代碼塊只能一個(gè)線程占用,同步方法是對(duì)方法的鎖定,如果能同步代碼塊盡量不要同步方法,否則影響效率
Java的同步可以用synchronized關(guān)鍵字來(lái)實(shí)現(xiàn)。
sychronized可以同步代碼,需要綁定一個(gè)對(duì)象,如synchronized(obj){}
也可以同步一個(gè)方法,是對(duì)方法進(jìn)行線程同步。如public void synchronized methodA(){}