Java實(shí)現(xiàn)線程同步的四種方式總結(jié)
什么是線程同步
當(dāng)使用多個(gè)線程來(lái)訪問(wèn)同一個(gè)數(shù)據(jù)時(shí),將會(huì)導(dǎo)致數(shù)據(jù)不準(zhǔn)確,相互之間產(chǎn)生沖突,非常容易出現(xiàn)線程安全問(wèn)題,如下圖所示:

比如多個(gè)線程都在操作同一數(shù)據(jù),都打算修改商品庫(kù)存,這樣就會(huì)導(dǎo)致數(shù)據(jù)不一致的問(wèn)題。
線程同步的真實(shí)意思,其實(shí)是“排隊(duì)”:幾個(gè)線程之間要排隊(duì),一個(gè)一個(gè)對(duì)共享資源進(jìn)行操作,而不是同時(shí)進(jìn)行操作。
所以我們用同步機(jī)制來(lái)解決這些問(wèn)題,加入同步鎖以避免在該線程沒(méi)有完成操作之前,被其他線程的調(diào)用,從而保證了該變量的唯一性和準(zhǔn)確性。
線程同步的幾種方式

1.使用synchronized關(guān)鍵字
這種方式比較靈活,修飾一個(gè)代碼塊,被修飾的代碼塊稱為同步語(yǔ)句塊。
其作用的范圍是大括號(hào){}括起來(lái)的代碼,作用的對(duì)象是調(diào)用這個(gè)代碼塊的對(duì)象,如下格式:
synchronized(對(duì)象) { //得到對(duì)象的鎖,才能操作同步代碼
需要被同步代碼;
}通常沒(méi)有必要同步整個(gè)方法,使用synchronized代碼塊同步關(guān)鍵代碼即可。
具體的示例如下:
public class SynchronizedThread {
class Bank {
private int account = 200;
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 + "賬戶余額為:" + bank.getAccount());
}
}
}
/**
* 建立線程,調(diào)用內(nè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();
}
}如果你還想深入了解Synchronized的底層原理,可以看 Synchronized的實(shí)現(xiàn)原理詳解(看這篇就夠了)
2.使用ReentrantLock
ReentrantLock類是可重入、互斥、實(shí)現(xiàn)了Lock接口的鎖,它與使用synchronized方法具有相同的基本行為和語(yǔ)義,并且擴(kuò)展了其能力。
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();
}
}
}synchronized 與 Lock 的對(duì)比
ReentrantLock是顯示鎖,手動(dòng)開啟和關(guān)閉鎖,別忘記關(guān)閉鎖;
synchronized 是隱式鎖,出了作用域自動(dòng)釋放;
ReentrantLock只有代碼塊鎖,synchronized 有代碼塊鎖和方法鎖;
使用 ReentrantLock鎖,JVM 將花費(fèi)較少的時(shí)間來(lái)調(diào)度線程,線程更好,并且具有更好的擴(kuò)展性(提供更多的子類);
優(yōu)先使用順序:
ReentrantLock> synchronized 同步代碼塊> synchronized 同步方法
3.使用原子變量實(shí)現(xiàn)線程同步
為了完成線程同步,我們將使用原子變量(Atomic***開頭的)來(lái)實(shí)現(xiàn)。
比如典型代表:AtomicInteger類存在于java.util.concurrent.atomic中,該類表示支持原子操作的整數(shù),采用getAndIncrement方法以原子方法將當(dāng)前的值遞加。
具體示例如下:
private AtomicInteger account = new AtomicInteger(100);
public AtomicInteger getAccount() {
return account;
}
public void save(int money) {
account.addAndGet(money);
}4.ThreadLocal實(shí)現(xiàn)線程同步
如果使用ThreadLocal管理變量,則每一個(gè)使用該變量的線程都獲得該變量的副本,副本之間相互獨(dú)立,這樣每一個(gè)線程都可以隨意修改自己的變量副本,而不會(huì)對(duì)其他線程產(chǎn)生影響,從而實(shí)現(xiàn)線程同步。
具體代碼示例如下:
//只改Bank類,其余代碼與上同
public class Bank{
// 創(chuàng)建一個(gè)線程本地變量 ThreadLocal
private static ThreadLocal<Integer> account = new ThreadLocal<Integer>(){
@Override
//返回當(dāng)前線程的"初始值"
protected Integer initialValue(){
return 100;
}
};
public void save(int money){
//設(shè)置線程副本中的值
account.set(account.get()+money);
}
public int getAccount(){
//返回線程副本中的值
return account.get();
}
}到此這篇關(guān)于Java實(shí)現(xiàn)線程同步的四種方式總結(jié)的文章就介紹到這了,更多相關(guān)Java線程同步內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Java字符串技巧之刪除標(biāo)點(diǎn)或最后字符的方法
這篇文章主要介紹了Java字符串技巧之刪除標(biāo)點(diǎn)或最后字符的方法,是Java入門學(xué)習(xí)中的基礎(chǔ)知識(shí),需要的朋友可以參考下2015-11-11
Java圖形化編程之JFrame疫苗接種系統(tǒng)詳解
GUI圖形界面設(shè)計(jì)是用戶和程序交互的工具,用戶通過(guò)圖形界面控制程序事件的發(fā)生。首先介紹Swing的基本體系結(jié)構(gòu),這是底層2021-09-09
springboot整合kaptcha生成驗(yàn)證碼功能
這篇文章主要介紹了springboot整合kaptcha生成驗(yàn)證碼,本文通過(guò)實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2020-10-10
微信小程序+后端(java)實(shí)現(xiàn)開發(fā)
這篇文章主要介紹了微信小程序+后端(java)實(shí)現(xiàn)開發(fā),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2020-04-04
詳解Java程序并發(fā)的Wait-Notify機(jī)制
這篇文章主要介紹了詳解Java程序并發(fā)的Wait-Notify機(jī)制,多線程并發(fā)是Java編程中的重要部分,需要的朋友可以參考下2015-07-07
javaweb中Filter(過(guò)濾器)的常見應(yīng)用
這篇文章主要介紹了javaweb中Filter的常見應(yīng)用,過(guò)濾器的使用方法,感興趣的小伙伴們可以參考一下2015-12-12

