Java實現(xiàn)線程同步方法及原理詳解
一、概述
無論是什么語言,在多線程編程中,常常會遇到多個線同時操作程某個變量(讀/寫),如果讀/寫不同步,則會造成不符合預(yù)期的結(jié)果。
例如:線程A和線程B并發(fā)運行,都操作變量X,若線程A對變量X進(jìn)行賦上一個新值,線程B仍然使用變量X之前的值,很明顯線程B使用的X不是我們想要的值了。
Java提供了三種機制,解決上述問題,實現(xiàn)線程同步:
同步代碼塊
synchronized(鎖對象){
// 這里添加受保護(hù)的數(shù)據(jù)操作
}
同步方法
靜態(tài)同步方法:synchronized修飾的靜態(tài)方法,它的同步鎖是當(dāng)前方法所在類的字節(jié)碼對象
public static synchronized void staticMethod(){
}
非靜態(tài)同步方法:synchronized修飾的非靜態(tài)方法,它的同步鎖即為this
public synchronize void method(){
}
鎖機制
// 以可重入鎖舉例
Lock lock = new ReentrantLock(/*fail*/);
// fail:
// true表示使用公平鎖,即線程等待拿到鎖的時間越久,越容易拿到鎖
// false表示使用非公平鎖,線程拿到鎖全靠運氣。。。cpu時間片輪到哪個線程,哪個線程就能獲取鎖
lock.lock();
// 這里添加受保護(hù)的數(shù)據(jù)操作
lock.unlock();
個人理解:其實無論哪種機制實現(xiàn)線程同步,本質(zhì)上都是加鎖->操作數(shù)據(jù)->解鎖的過程。同步代碼塊是針對{}中,同步方法是針對整個方法。其ReentrantLock類提供的lock和unlock和C++的std::mutex提供lock和unlock類似
二、測試用例

同步代碼塊測試類
package base.synchronize;
public class SynchronizeBlock implements Runnable {
private int num = 100;
@Override
public void run() {
while (num > 1) {
synchronized (this) {
// 同步代碼塊,只有拿到鎖,才有cpu執(zhí)行權(quán)
System.out.println("Thread ID:" + Thread.currentThread().getId() + "---num:" + num);
num--;
}
}
System.out.println("Thread ID:" + Thread.currentThread().getId() + " exit");
}
}
同步方法測試類
package base.synchronize;
public class SynchronizeMethod implements Runnable {
private int num = 100;
public static int staticNum = 100;
boolean useStaticMethod;
public SynchronizeMethod(boolean useStaticMethodToTest) {
this.useStaticMethod = useStaticMethodToTest;
}
// 對于非靜態(tài)方法,同步鎖對象即this
public synchronized void method() {
System.out.println("Thread ID:" + Thread.currentThread().getId() + "---num:" + num);
num--;
}
// 對于靜態(tài)方法,同步鎖對象是當(dāng)前方法所在類的字節(jié)碼對象
public synchronized static void staticMethod() {
System.out.println("Static Method Thread ID:" + Thread.currentThread().getId() + "---num:" + staticNum);
staticNum--;
}
@Override
public void run() {
if (useStaticMethod) { // 測試靜態(tài)同步方法
while (staticNum > 1) {
staticMethod();
}
}else{ // 測試非靜態(tài)同步方法
while (num > 1){
method();
}
}
System.out.println("Thread ID:" + Thread.currentThread().getId() + " exit");
}
}
ReentrantLock測試類
package base.synchronize;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class SynchronizeLock implements Runnable {
private Lock lock = null;
private int num = 100;
public SynchronizeLock(boolean fair){
lock = new ReentrantLock(fair); // 可重入鎖
}
@Override
public void run() {
while (num > 1) {
try {
lock.lock();
System.out.println("Thread ID:" + Thread.currentThread().getId() + "---num:" + num);
num--;
} catch (Exception e) {
e.printStackTrace();
}finally {
lock.unlock();
}
}
System.out.println("Thread ID:" + Thread.currentThread().getId() + " exit");
}
}
測試三種機制的Demo
package base.synchronize;
public class Demo {
public static void main(String[] args) {
synchronizeBlockTest(); // 同步代碼塊
synchronizeMethodTest(); // 同步非靜態(tài)方法
synchronizeStaticMethodTest(); // 同步靜態(tài)方法
synchronizeLockTest(); // 可重入鎖機制
}
public static void synchronizeBlockTest(){
Runnable run = new SynchronizeBlock();
for(int i = 0; i < 3; i++){
new Thread(run).start();
}
}
public static void synchronizeMethodTest(){
Runnable run = new SynchronizeMethod(false);
for(int i = 0; i < 3; i++){
new Thread(run).start();
}
}
public static void synchronizeStaticMethodTest() {
Runnable run = new SynchronizeMethod(true);
for(int i = 0; i < 3; i++){
new Thread(run).start();
}
}
public static void synchronizeLockTest(){
Runnable run = new SynchronizeLock(false); // true:使用公平鎖 false:使用非公平鎖
for(int i = 0; i < 3; i++){
new Thread(run).start();
}
}
}
無論哪種機制,都得到預(yù)期的效果,打印100-0
以上就是本文的全部內(nèi)容,希望對大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
- 詳解Java內(nèi)部類——匿名內(nèi)部類
- Java內(nèi)部類和匿名內(nèi)部類的用法說明
- JAVA匿名內(nèi)部類語法分析及實例詳解
- Java匿名類,匿名內(nèi)部類實例分析
- java的多線程高并發(fā)詳解
- Java多線程下載網(wǎng)圖的完整案例
- java多線程CountDownLatch與線程池ThreadPoolExecutor/ExecutorService案例
- java多線程CyclicBarrier的使用案例,讓線程起步走
- java 定時同步數(shù)據(jù)的任務(wù)優(yōu)化
- 簡述JAVA同步、異步、阻塞和非阻塞之間的區(qū)別
- Java原生序列化和反序列化代碼實例
- java對象序列化與反序列化原理解析
- JAVA序列化和反序列化的底層實現(xiàn)原理解析
- Java中難理解的四個概念
相關(guān)文章
Jenkins一鍵打包部署SpringBoot應(yīng)用
本文主要介紹了Jenkins一鍵打包部署SpringBoot應(yīng)用,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2023-01-01
Eclipse創(chuàng)建JavaWeb工程的完整步驟記錄
很多新手不知道Eclipse怎么創(chuàng)建Java Web項目,一起來看看吧,這篇文章主要給大家介紹了關(guān)于Eclipse創(chuàng)建JavaWeb工程的完整步驟,需要的朋友可以參考下2023-10-10
Java中的構(gòu)造方法(構(gòu)造函數(shù))與普通方法區(qū)別及說明
這篇文章主要介紹了Java中的構(gòu)造方法(構(gòu)造函數(shù))與普通方法區(qū)別及說明,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教2024-03-03

