日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程语言 > java >内容正文

java

java中存在对多个对象加锁的情况_Java对象锁和类锁全面解析(多线程synchronized关键字)...

發(fā)布時間:2024/7/5 java 25 豆豆
生活随笔 收集整理的這篇文章主要介紹了 java中存在对多个对象加锁的情况_Java对象锁和类锁全面解析(多线程synchronized关键字)... 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

最近工作有用到一些多線程的東西,之前吧,有用到synchronized同步塊,不過是別人怎么用就跟著用,并沒有搞清楚鎖的概念。最近也是遇到一些問題,不搞清楚鎖的概念,很容易碰壁,甚至有些時候自己連用沒用對都不知道。

今天把一些疑惑都解開了,寫篇文章分享給大家,文章還算比較全面。當(dāng)然可能有小寶鴿理解得不夠深入透徹的地方,如果說得不正確還望指出。

看之前有必要跟某些猿友說一下,如果看一遍沒有看明白呢,也沒關(guān)系,當(dāng)是了解一下,等真正使用到了,再回頭看。

本文主要是將synchronized關(guān)鍵字用法作為例子來去解釋Java中的對象鎖和類鎖。特別的是希望能幫大家理清一些概念。

一、synchronized關(guān)鍵字

synchronized關(guān)鍵字有如下兩種用法:

1、 在需要同步的方法的方法簽名中加入synchronized關(guān)鍵字。

synchronized public void getValue() {

System.out.println("getValue method thread name="

+ Thread.currentThread().getName() + " username=" + username

+ " password=" + password);

}

上面的代碼修飾的synchronized是非靜態(tài)方法,如果修飾的是靜態(tài)方法(static)含義是完全不一樣的。具體不一樣在哪里,后面會詳細(xì)說清楚。

synchronized static public void getValue() {

System.out.println("getValue method thread name="

+ Thread.currentThread().getName() + " username=" + username

+ " password=" + password);

}

2、使用synchronized塊對需要進行同步的代碼段進行同步。

public void serviceMethod() {

try {

synchronized (this) {

System.out.println("begin time=" + System.currentTimeMillis());

Thread.sleep(2000);

System.out.println("end end=" + System.currentTimeMillis());

}

} catch (InterruptedException e) {

e.printStackTrace();

}

}

上面的代碼塊是synchronized (this)用法,還有synchronized (非this對象)以及synchronized (類.class)這兩種用法,這些使用方式的含義也是有根本的區(qū)別的。我們先帶著這些問題繼續(xù)往下看。

二、Java中的對象鎖和類鎖

小寶鴿似乎并沒有辦法用清晰簡短的語言來描述對象鎖和類鎖的概念。即便能用簡單的語句概況,也會顯得抽象。猿友們耐心看完自然會明白。

之前網(wǎng)上有找一些相關(guān)資料,有篇博客是這樣描述的(看的是轉(zhuǎn)載的,原創(chuàng)連接我也不知道):

一段synchronized的代碼被一個線程執(zhí)行之前,他要先拿到執(zhí)行這段代碼的權(quán)限,

在Java里邊就是拿到某個同步對象的鎖(一個對象只有一把鎖);

如果這個時候同步對象的鎖被其他線程拿走了,他(這個線程)就只能等了(線程阻塞在鎖池等待隊列中)。

取到鎖后,他就開始執(zhí)行同步代碼(被synchronized修飾的代碼);

線程執(zhí)行完同步代碼后馬上就把鎖還給同步對象,其他在鎖池中等待的某個線程就可以拿到鎖執(zhí)行同步代碼了。

這樣就保證了同步代碼在統(tǒng)一時刻只有一個線程在執(zhí)行。

這段話,除了最后一句,講得都是挺合理的。”這樣就保證了同步代碼在統(tǒng)一時刻只有一個線程在執(zhí)行。”這句話顯然不對,synchronized并非保證同步代碼同一時刻只有一個線程執(zhí)行,同步代碼同一時刻應(yīng)該可以有多個線程執(zhí)行。

上面提到鎖,這里先引出鎖的概念。先來看看下面這些啰嗦而必不可少的文字。

多線程的線程同步機制實際上是靠鎖的概念來控制的。

在Java程序運行時環(huán)境中,JVM需要對兩類線程共享的數(shù)據(jù)進行協(xié)調(diào):

1)保存在堆中的實例變量

2)保存在方法區(qū)中的類變量

這兩類數(shù)據(jù)是被所有線程共享的。

(程序不需要協(xié)調(diào)保存在Java 棧當(dāng)中的數(shù)據(jù)。因為這些數(shù)據(jù)是屬于擁有該棧的線程所私有的。)

這里插播一下廣告:關(guān)于JVM內(nèi)存,如果想了解可以看看博主的另外一篇文章:

方法區(qū)(Method Area)與Java堆一樣,是各個線程共享的內(nèi)存區(qū)域,它用于存儲已被虛擬機加載的類信息、常量、靜態(tài)變量、即時編譯器編譯后的代碼等數(shù)據(jù)。雖然Java虛擬機規(guī)范把方法區(qū)描述為堆的一個邏輯部分,但是它卻有一個別名叫做Non-Heap(非堆),目的應(yīng)該是與Java堆區(qū)分開來。

棧:在Java中,JVM中的棧記錄了線程的方法調(diào)用。每個線程擁有一個棧。在某個線程的運行過程中,如果有新的方法調(diào)用,那么該線程對應(yīng)的棧就會增加一個存儲單元,即幀(frame)。在frame中,保存有該方法調(diào)用的參數(shù)、局部變量和返回地址。

堆是JVM中一塊可自由分配給對象的區(qū)域。當(dāng)我們談?wù)摾厥?garbage collection)時,我們主要回收堆(heap)的空間。

Java的普通對象存活在堆中。與棧不同,堆的空間不會隨著方法調(diào)用結(jié)束而清空。因此,在某個方法中創(chuàng)建的對象,可以在方法調(diào)用結(jié)束之后,繼續(xù)存在于堆中。這帶來的一個問題是,如果我們不斷的創(chuàng)建新的對象,內(nèi)存空間將最終消耗殆盡。

在java虛擬機中,每個對象和類在邏輯上都是和一個監(jiān)視器相關(guān)聯(lián)的。

對于對象來說,相關(guān)聯(lián)的監(jiān)視器保護對象的實例變量。

對于類來說,監(jiān)視器保護類的類變量。

(如果一個對象沒有實例變量,或者一個類沒有變量,相關(guān)聯(lián)的監(jiān)視器就什么也不監(jiān)視。)

為了實現(xiàn)監(jiān)視器的排他性監(jiān)視能力,java虛擬機為每一個對象和類都關(guān)聯(lián)一個鎖。代表任何時候只允許一個線程擁有的特權(quán)。線程訪問實例變量或者類變量不需鎖。

但是如果線程獲取了鎖,那么在它釋放這個鎖之前,就沒有其他線程可以獲取同樣數(shù)據(jù)的鎖了。(鎖住一個對象就是獲取對象相關(guān)聯(lián)的監(jiān)視器)

類鎖實際上用對象鎖來實現(xiàn)。當(dāng)虛擬機裝載一個class文件的時候,它就會創(chuàng)建一個java.lang.Class類的實例。當(dāng)鎖住一個對象的時候,實際上鎖住的是那個類的Class對象。

一個線程可以多次對同一個對象上鎖。對于每一個對象,java虛擬機維護一個加鎖計數(shù)器,線程每獲得一次該對象,計數(shù)器就加1,每釋放一次,計數(shù)器就減 1,當(dāng)計數(shù)器值為0時,鎖就被完全釋放了。

java編程人員不需要自己動手加鎖,對象鎖是java虛擬機內(nèi)部使用的。

在java程序中,只需要使用synchronized塊或者synchronized方法就可以標(biāo)志一個監(jiān)視區(qū)域。當(dāng)每次進入一個監(jiān)視區(qū)域時,java 虛擬機都會自動鎖上對象或者類。

三、synchronized關(guān)鍵字各種用法與實例

看完了”二、Java中的對象鎖和類鎖”,我們再來結(jié)合”一、synchronized關(guān)鍵字”里面提到的synchronized用法。

事實上,synchronized修飾非靜態(tài)方法、同步代碼塊的synchronized (this)用法和synchronized (非this對象)的用法鎖的是對象,線程想要執(zhí)行對應(yīng)同步代碼,需要獲得對象鎖。

synchronized修飾靜態(tài)方法以及同步代碼塊的synchronized (類.class)用法鎖的是類,線程想要執(zhí)行對應(yīng)同步代碼,需要獲得類鎖。

因此,事實上synchronized關(guān)鍵字可以細(xì)分為上面描述的五種用法。

本文的實例均來自于《Java多線程編程核心技術(shù)》這本書里面的例子。

1、我們先看看非線程安全實例(Run.java):

public class Run {

public static void main(String[] args) {

HasSelfPrivateNum numRef = new HasSelfPrivateNum();

ThreadA athread = new ThreadA(numRef);

athread.start();

ThreadB bthread = new ThreadB(numRef);

bthread.start();

}

}

class HasSelfPrivateNum {

private int num = 0;

public void addI(String username) {

try {

if (username.equals("a")) {

num = 100;

System.out.println("a set over!");

Thread.sleep(2000);

} else {

num = 200;

System.out.println("b set over!");

}

System.out.println(username + " num=" + num);

} catch (InterruptedException e) {

// TODO Auto-generated catch block

e.printStackTrace();

}

}

}

class ThreadA extends Thread {

private HasSelfPrivateNum numRef;

public ThreadA(HasSelfPrivateNum numRef) {

super();

this.numRef = numRef;

}

@Override

public void run() {

super.run();

numRef.addI("a");

}

}

class ThreadB extends Thread {

private HasSelfPrivateNum numRef;

public ThreadB(HasSelfPrivateNum numRef) {

super();

this.numRef = numRef;

}

@Override

public void run() {

super.run();

numRef.addI("b");

}

}

運行結(jié)果為:

a set over!

b set over!

b num=200

a num=200

修改HasSelfPrivateNum如下,方法用synchronized修飾如下:

class HasSelfPrivateNum {

private int num = 0;

synchronized public void addI(String username) {

try {

if (username.equals("a")) {

num = 100;

System.out.println("a set over!");

Thread.sleep(2000);

} else {

num = 200;

System.out.println("b set over!");

}

System.out.println(username + " num=" + num);

} catch (InterruptedException e) {

// TODO Auto-generated catch block

e.printStackTrace();

}

}

}

運行結(jié)果是線程安全的:

b set over!

b num=200

a set over!

a num=100

實驗結(jié)論:兩個線程訪問同一個對象中的同步方法是一定是線程安全的。本實現(xiàn)由于是同步訪問,所以先打印出a,然后打印出b

這里線程獲取的是HasSelfPrivateNum的對象實例的鎖——對象鎖。

2、多個對象多個鎖

就上面的實例,我們將Run改成如下:

public class Run {

public static void main(String[] args) {

HasSelfPrivateNum numRef1 = new HasSelfPrivateNum();

HasSelfPrivateNum numRef2 = new HasSelfPrivateNum();

ThreadA athread = new ThreadA(numRef1);

athread.start();

ThreadB bthread = new ThreadB(numRef2);

bthread.start();

}

}

運行結(jié)果為:

a set over!

b set over!

b num=200

a num=200

這里是非同步的,因為線程athread獲得是numRef1的對象鎖,而bthread線程獲取的是numRef2的對象鎖,他們并沒有在獲取鎖上有競爭關(guān)系,因此,出現(xiàn)非同步的結(jié)果

這里插播一下:同步不具有繼承性

3、同步塊synchronized (this)

我們先看看代碼實例(Run.java)

public class Run {

public static void main(String[] args) {

ObjectService service = new ObjectService();

ThreadA a = new ThreadA(service);

a.setName("a");

a.start();

ThreadB b = new ThreadB(service);

b.setName("b");

b.start();

}

}

class ObjectService {

public void serviceMethod() {

try {

synchronized (this) {

System.out.println("begin time=" + System.currentTimeMillis());

Thread.sleep(2000);

System.out.println("end end=" + System.currentTimeMillis());

}

} catch (InterruptedException e) {

e.printStackTrace();

}

}

}

class ThreadA extends Thread {

private ObjectService service;

public ThreadA(ObjectService service) {

super();

this.service = service;

}

@Override

public void run() {

super.run();

service.serviceMethod();

}

}

class ThreadB extends Thread {

private ObjectService service;

public ThreadB(ObjectService service) {

super();

this.service = service;

}

@Override

public void run() {

super.run();

service.serviceMethod();

}

}

運行結(jié)果:

begin time=1466148260341

end end=1466148262342

begin time=1466148262342

end end=1466148264378

這樣也是同步的,線程獲取的是同步塊synchronized (this)括號()里面的對象實例的對象鎖,這里就是ObjectService實例對象的對象鎖了。

需要注意的是synchronized (){}的{}前后的代碼依舊是異步的

4、synchronized (非this對象)

我們先看看代碼實例(Run.java)

public class Run {

public static void main(String[] args) {

Service service = new Service("xiaobaoge");

ThreadA a = new ThreadA(service);

a.setName("A");

a.start();

ThreadB b = new ThreadB(service);

b.setName("B");

b.start();

}

}

class Service {

String anyString = new String();

public Service(String anyString){

this.anyString = anyString;

}

public void setUsernamePassword(String username, String password) {

try {

synchronized (anyString) {

System.out.println("線程名稱為:" + Thread.currentThread().getName()

+ "在" + System.currentTimeMillis() + "進入同步塊");

Thread.sleep(3000);

System.out.println("線程名稱為:" + Thread.currentThread().getName()

+ "在" + System.currentTimeMillis() + "離開同步塊");

}

} catch (InterruptedException e) {

// TODO Auto-generated catch block

e.printStackTrace();

}

}

}

class ThreadA extends Thread {

private Service service;

public ThreadA(Service service) {

super();

this.service = service;

}

@Override

public void run() {

service.setUsernamePassword("a", "aa");

}

}

class ThreadB extends Thread {

private Service service;

public ThreadB(Service service) {

super();

this.service = service;

}

@Override

public void run() {

service.setUsernamePassword("b", "bb");

}

}

不難看出,這里線程爭奪的是anyString的對象鎖,兩個線程有競爭同一對象鎖的關(guān)系,出現(xiàn)同步

現(xiàn)在有一個問題:一個類里面有兩個非靜態(tài)同步方法,會有影響么?

答案是:如果對象實例A,線程1獲得了對象A的對象鎖,那么其他線程就不能進入需要獲得對象實例A的對象鎖才能訪問的同步代碼(包括同步方法和同步塊)。不理解可以細(xì)細(xì)品味一下!

5、靜態(tài)synchronized同步方法

我們直接看代碼實例:

public class Run {

public static void main(String[] args) {

ThreadA a = new ThreadA();

a.setName("A");

a.start();

ThreadB b = new ThreadB();

b.setName("B");

b.start();

}

}

class Service {

synchronized public static void printA() {

try {

System.out.println("線程名稱為:" + Thread.currentThread().getName()

+ "在" + System.currentTimeMillis() + "進入printA");

Thread.sleep(3000);

System.out.println("線程名稱為:" + Thread.currentThread().getName()

+ "在" + System.currentTimeMillis() + "離開printA");

} catch (InterruptedException e) {

e.printStackTrace();

}

}

synchronized public static void printB() {

System.out.println("線程名稱為:" + Thread.currentThread().getName() + "在"

+ System.currentTimeMillis() + "進入printB");

System.out.println("線程名稱為:" + Thread.currentThread().getName() + "在"

+ System.currentTimeMillis() + "離開printB");

}

}

class ThreadA extends Thread {

@Override

public void run() {

Service.printA();

}

}

class ThreadB extends Thread {

@Override

public void run() {

Service.printB();

}

}

運行結(jié)果:

線程名稱為:A在1466149372909進入printA

線程名稱為:A在1466149375920離開printA

線程名稱為:B在1466149375920進入printB

線程名稱為:B在1466149375920離開printB

兩個線程在爭奪同一個類鎖,因此同步

6、synchronized (class)

對上面Service類代碼修改成如下:

class Service {

public static void printA() {

synchronized (Service.class) {

try {

System.out.println("線程名稱為:" + Thread.currentThread().getName()

+ "在" + System.currentTimeMillis() + "進入printA");

Thread.sleep(3000);

System.out.println("線程名稱為:" + Thread.currentThread().getName()

+ "在" + System.currentTimeMillis() + "離開printA");

} catch (InterruptedException e) {

e.printStackTrace();

}

}

}

public static void printB() {

synchronized (Service.class) {

System.out.println("線程名稱為:" + Thread.currentThread().getName()

+ "在" + System.currentTimeMillis() + "進入printB");

System.out.println("線程名稱為:" + Thread.currentThread().getName()

+ "在" + System.currentTimeMillis() + "離開printB");

}

}

}

運行結(jié)果:

線程名稱為:A在1466149372909進入printA

線程名稱為:A在1466149375920離開printA

線程名稱為:B在1466149375920進入printB

線程名稱為:B在1466149375920離開printB

兩個線程依舊在爭奪同一個類鎖,因此同步

需要特別說明:對于同一個類A,線程1爭奪A對象實例的對象鎖,線程2爭奪類A的類鎖,這兩者不存在競爭關(guān)系。也就說對象鎖和類鎖互補干預(yù)內(nèi)政

靜態(tài)方法則一定會同步,非靜態(tài)方法需在單例模式才生效,但是也不能都用靜態(tài)同步方法,總之用得不好可能會給性能帶來極大的影響。另外,有必要說一下的是Spring的bean默認(rèn)是單例的。

總結(jié)

以上是生活随笔為你收集整理的java中存在对多个对象加锁的情况_Java对象锁和类锁全面解析(多线程synchronized关键字)...的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。

主站蜘蛛池模板: 一级在线播放 | 老司机精品福利视频 | 久久久免费av | 亚洲成人精品在线播放 | 综合狠狠 | 北京富婆泄欲对白 | videos另类灌满极品另类 | 国产欧美日韩亚洲 | 欧美色第一页 | 国产成人精品一区二区三区无码熬 | 亚洲第一黄网 | 超碰97成人| 九九九热视频 | jizz成熟丰满老女人 | 国产精品成人一区二区 | 成人网站在线进入爽爽爽 | 天天看天天色 | 精品国产午夜福利在线观看 | 女女h百合无遮涩涩漫画软件 | 丝袜美女啪啪 | 九九热在线播放 | 国产熟女高潮一区二区三区 | 日韩久久精品一区二区 | 福利色播 | 成人午夜在线 | 91粉色视频 | 日韩有码专区 | 一区二区中文字幕在线观看 | 国产精品白嫩极品美女视频 | 欧美大色一区 | 中文字幕黄色片 | 黄色片视频免费 | 午夜看看 | 麻豆亚洲av成人无码久久精品 | 修仙淫交(高h)h文 | 日韩人妻一区二区三区蜜桃视频 | 九九综合| 黄色成年网站 | 免费黄色网址观看 | 日韩在线视屏 | 黄色av一区二区三区 | 亚洲第一黄色 | 黄色片在线 | 亚洲视频天堂 | 国产精品欧美精品 | 日韩一区二区三区免费 | 碰碰色| 欧美日韩电影一区二区 | 森泽佳奈av | 99国产在线 | 四虎永久网址 | 亚洲第一色网站 | www.日韩欧美 | 一级黄色免费网站 | a视频网站 | 国产精品一区在线观看 | 国产日韩欧美在线播放 | 亚洲免费高清视频 | 善良的公与媳hd中文字 | 国产精品传媒在线 | 亚洲aⅴ在线观看 | 中国黄色网址 | 嫩草嫩草嫩草嫩草嫩草嫩草 | 精品人妻天天爽夜夜爽视频 | 久草麻豆 | 国产精品操| www.国产一区二区三区 | 美女视频黄a视频全免费观看 | 国产91久久久 | 中文字幕久久久久 | 亚洲精品18p | 少妇精品视频 | 国产在线一区视频 | 久久精品小视频 | 一区二区在线免费观看视频 | 一级bbbbbbbbb毛片 | 免费啪啪网 | 久久国产成人精品 | 国产高清视频免费在线观看 | 99re6在线精品视频免费播放 | 国产又粗又猛又黄又爽视频 | 色综合久久中文字幕无码 | 国产综合欧美 | 精彩久久 | 天堂av免费看 | 久久久永久久久人妻精品麻豆 | 色偷偷免费费视频在线 | 久久久ww | 中国毛片在线观看 | 午夜av剧场 | 一本一道波多野结衣av黑人 | 91精品国产综合久久久蜜臀粉嫩 | 色校园| 国产免费视屏 | 久久这里只有精品久久 | 午夜三级网站 | 男人的天堂色偷偷 | 青青草原国产 | 国产一线二线在线观看 |