淺析Java 并發(fā)編程中的synchronized
synchronized關(guān)鍵字,我們一般稱之為“同步鎖”,用它來修飾需要同步的方法和需要同步代碼塊,默認(rèn)是當(dāng)前對象作為鎖的對象。在用synchronized修飾類時(或者修飾靜態(tài)方法),默認(rèn)是當(dāng)前類的Class對象作為鎖的對象,故存在著方法鎖、對象鎖、類鎖這樣的概念。
一、沒有設(shè)置線程同步的情況
先給出以下代碼感受下代碼執(zhí)行的時候為什么需要同步?代碼可能比較枯燥,配上業(yè)務(wù)理解起來就會舒服很多,學(xué)生軍訓(xùn),有三列,每列5人,需要報數(shù),每個線程負(fù)責(zé)每一列報數(shù)。
class SynchronizedExample {
protected static int num = 0;
protected void numberOff() {
for(int i=0; i<5; i++) {
num++;
System.out.println(Thread.currentThread().getName()+":"+SynchronizedExample.num);
}
}
}
public class SynchronizedTest {
public static void main(String[] args) throws InterruptedException {
SynchronizedExample se = new SynchronizedExample();
for(int i=1; i<=3; i++) {
new Thread( ()-> {se.numberOff();}, "線程"+i).start();
}
}
}
執(zhí)行結(jié)果如下:
線程1:1
線程2:2
線程1:3
線程3:4
.......
之所以出現(xiàn)這種情況,是因為三個線程是異步的,沒有同步。
對應(yīng)的業(yè)務(wù)場景就是,在第一列沒有完成報數(shù)的時候,其他隊列搶報了,這在現(xiàn)實中是不允許的,所以需要類似于synchronized等具有同步功能的關(guān)鍵字粉末登場。
二、方法同步鎖
當(dāng)報數(shù)方法加上synchronized關(guān)鍵字之后,就會一列一列的報數(shù)。
protected synchronized void numberOff() {
for(int i=0; i<5; i++) {
num++;
System.out.println(Thread.currentThread().getName()+":"+SynchronizedExample.num);
}
}
執(zhí)行結(jié)果如下:
線程1:1
線程1:2
線程1:3
線程1:4
......
寫到這里還是要從技術(shù)層面講下原理,當(dāng)一個線程執(zhí)行帶有synchronized關(guān)鍵字的方法時,該線程會在該方法處設(shè)置一個鎖(其他線程打不開這個鎖,只能在外邊等該線程釋放掉該鎖,一般都是執(zhí)行完所有代碼后主動釋放鎖),表示此方法是當(dāng)前線程獨占的,對應(yīng)到上述業(yè)務(wù)中就是一次只能有一個隊列報數(shù)。
三、對象鎖
改進(jìn)后的代碼用到了一個對象鎖,該對象鎖默認(rèn)是當(dāng)前對象,上述代碼等同于以下代碼:
protected void numberOff() {
synchronized (this) {
for (int i = 0; i < 5; i++) {
num++;
System.out.println(Thread.currentThread().getName() + ":" + SynchronizedExample.num);
}
}
}
當(dāng)多個線程用一個對象鎖,各個線程可以達(dá)到同步的作用,如果每個線程都用自己的對象鎖,那么synchronized就失去了同步的作用。如以下代碼:
class SynchronizedExample {
protected static int num = 0;
protected void numberOff() {
synchronized (this) {
for (int i = 0; i < 5; i++) {
num++;
System.out.println(Thread.currentThread().getName() + ":" + SynchronizedExample.num);
}
}
}
}
public class SynchronizedTest {
public static void main(String[] args) throws InterruptedException {
SynchronizedExample se = new SynchronizedExample();
for(int i=1; i<=3; i++) {
new Thread( ()-> {new SynchronizedExample().numberOff();}, "隊列"+i).start();
}
}
}
執(zhí)行結(jié)果如下:
線程1:1
線程2:2
線程1:3
線程3:4
.......
有讀者會說不同線程執(zhí)行的是不同對象中的方法,肯定達(dá)不到同步的效果,也對,也很有道理,接著看如下代碼:
class SynchronizedExample {
protected static int num = 0;
protected void numberOff(Object lock) {
synchronized (lock) {
for (int i = 0; i < 5; i++) {
num++;
System.out.println(Thread.currentThread().getName() + ":" + SynchronizedExample.num);
}
}
}
}
public class SynchronizedTest {
public static void main(String[] args) throws InterruptedException {
SynchronizedExample se = new SynchronizedExample();
for(int i=1; i<=3; i++) {
new Thread( ()-> {se.numberOff(new Object());}, "隊列"+i).start();
}
}
}
執(zhí)行結(jié)果如下:
線程1:1
線程2:2
線程1:3
線程3:4
.......
四、類鎖
對于上述問題,讀者應(yīng)該得出一個結(jié)論,要想達(dá)到同步的效果,必須用同一個鎖,此時類鎖可以粉末登場??慈缦麓a:
protected void numberOff(Object lock) {
synchronized (SynchronizedExample.class) {
for (int i = 0; i < 5; i++) {
num++;
System.out.println(Thread.currentThread().getName() + ":" + SynchronizedExample.num);
}
}
}
上述代碼可以達(dá)到同步的效果。
五、靜態(tài)鎖
靜態(tài)鎖是針對靜態(tài)方法而言,當(dāng)一個靜態(tài)方法中有synchronized關(guān)鍵字時,默認(rèn)的是使用當(dāng)前類字節(jié)碼對象作為鎖。代碼示例如下:
class SynchronizedExample {
protected static int num = 0;
protected synchronized static void numberOff() {
for (int i = 0; i < 5; i++) {
num++;
System.out.println(Thread.currentThread().getName() + ":" + SynchronizedExample.num);
}
}
}
public class SynchronizedTest {
public static void main(String[] args) throws InterruptedException {
for (int i = 1; i <= 3; i++) {
new Thread(() -> { new SynchronizedExample().numberOff(); }, "隊列" + i).start();
}
}
}
六、線程池實現(xiàn)
最后用線程池將上述代碼寫一下
package ioo;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
class SynchronizedExample {
protected static int num = 0;
protected synchronized static void numberOff() {
for (int i = 0; i < 5; i++) {
num++;
System.out.println(Thread.currentThread().getName() + ":" + SynchronizedExample.num);
}
}
}
public class SynchronizedTest {
public static void main(String[] args) throws InterruptedException {
ExecutorService executorService = Executors.newCachedThreadPool();
for(int i=1; i<=3; i++) {
executorService.execute(() -> new SynchronizedExample().numberOff());
}
}
}
以上就是淺析Java 并發(fā)編程中的synchronized的詳細(xì)內(nèi)容,更多關(guān)于Java 并發(fā)編程 synchronized的資料請關(guān)注腳本之家其它相關(guān)文章!
- Java并發(fā)編程深入理解之Synchronized的使用及底層原理詳解 下
- Java并發(fā)編程深入理解之Synchronized的使用及底層原理詳解 上
- Java并發(fā)之synchronized實現(xiàn)原理深入理解
- java并發(fā)編程之深入理解Synchronized的使用
- 詳解Java并發(fā)編程之內(nèi)置鎖(synchronized)
- 淺析java并發(fā)中的Synchronized關(guān)鍵詞
- Java并發(fā) synchronized鎖住的內(nèi)容解析
- 詳解java并發(fā)編程(2) --Synchronized與Volatile區(qū)別
- 詳解Java利用同步塊synchronized()保證并發(fā)安全
- java并發(fā)之synchronized
相關(guān)文章
解決Mybatis-Plus更新方法不更新NULL字段的問題
這篇文章主要介紹了解決Mybatis-Plus更新方法不更新NULL字段的問題,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教2023-12-12
java基礎(chǔ)之 “==”與“equals”區(qū)別詳解
這篇文章主要介紹了java基礎(chǔ)之 “==”與“equals”區(qū)別詳解,需要的朋友可以參考下2020-02-02
Java基礎(chǔ)之?dāng)?shù)組超詳細(xì)知識總結(jié)
這篇文章主要介紹了Java基礎(chǔ)之?dāng)?shù)組詳解,文中有非常詳細(xì)的代碼示例,對正在學(xué)習(xí)java基礎(chǔ)的小伙伴們有很好的幫助,需要的朋友可以參考下2021-05-05
SpringCloud Gateway 利用 Mysql 實現(xiàn)動態(tài)路由的方法
這篇文章主要介紹了SpringCloud Gateway 利用 Mysql 實現(xiàn)動態(tài)路由的方法,本文給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下2021-02-02
JAVA中StringBuffer與String的區(qū)別解析
這篇文章主要介紹了JAVA中StringBuffer與String的區(qū)別解析,需要的朋友可以參考下2014-02-02
springmvc和js前端的數(shù)據(jù)傳遞和接收方式(兩種)
本文介紹了springmvc和js前端的數(shù)據(jù)傳遞和接收方式(兩種),詳細(xì)的介紹了兩種方式,一種是json格式傳遞,另一種是Map傳遞,具有一定的參考價值,有興趣的可以了解一下2017-12-12
Java設(shè)計模式之觀察者模式(Observer模式)介紹
這篇文章主要介紹了Java設(shè)計模式之觀察者模式(Observer模式)介紹,Java深入到一定程度,就不可避免的碰到設(shè)計模式(design pattern)這一概念,了解設(shè)計模式,將使自己對java中的接口或抽象類應(yīng)用有更深的理解,需要的朋友可以參考下2015-03-03

