java多線程編程之Synchronized塊同步方法
文章分享了4個(gè)例子對(duì)synchronized的詳細(xì)解釋
1、是否加synchronized關(guān)鍵字的不同
public class ThreadTest {
public static void main(String[] args) {
Example example = new Example();
Thread t1 = new Thread1(example);
Thread t2 = new Thread1(example);
t1.start();
t2.start();
}
}
class Example {
public synchronized void excute() {
for (int i = 0; i < 5; ++i) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("excute:" + i);
}
}
}
class Thread1 extends Thread {
private Example example;
public Thread1(Example example) {
this.example = example;
}
@Override
public void run() {
example.excute();
}
}
加了synchronized關(guān)鍵字的輸出結(jié)果如下
會(huì)先輸出一組0-4,接著再輸出下一組,兩個(gè)線程順序執(zhí)行
excute:0
excute:1
excute:2
excute:3
excute:4
excute:0
excute:1
excute:2
excute:3
excute:4
沒(méi)加synchronized關(guān)鍵字的輸出結(jié)果如下
兩個(gè)線程同時(shí)執(zhí)行excute方法,同時(shí)并發(fā)的
excute:0
excute:0
excute:1
excute:1
excute:2
excute:2
excute:3
excute:3
excute:4
excute:4
2、多個(gè)方法的多線程情況
public class ThreadTest {
public static void main(String[] args) {
Example example = new Example();
Thread t1 = new Thread1(example);
Thread t2 = new Thread2(example);
t1.start();
t2.start();
}
}
class Example {
public synchronized void excute() {
for (int i = 0; i < 5; ++i) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("excute:" + i);
}
}
public synchronized void excute1() {
for (int i = 0; i < 5; ++i) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("excute1:" + i);
}
}
}
class Thread1 extends Thread {
private Example example;
public Thread1(Example example) {
this.example = example;
}
@Override
public void run() {
example.excute();
}
}
class Thread2 extends Thread {
private Example example;
public Thread2(Example example) {
this.example = example;
}
@Override
public void run() {
example.excute1();
}
}
執(zhí)行結(jié)果如下
同樣是順序執(zhí)行,執(zhí)行完一個(gè)線程再執(zhí)行另一個(gè)線程
excute:0
excute:1
excute:2
excute:3
excute:4
excute1:0
excute1:1
excute1:2
excute1:3
excute1:4
如果去掉synchronized關(guān)鍵字,則兩個(gè)方法并發(fā)執(zhí)行,并沒(méi)有相互影響。
但是如例子程序中所寫(xiě),即便是兩個(gè)方法:
執(zhí)行結(jié)果永遠(yuǎn)是執(zhí)行完一個(gè)線程的輸出再執(zhí)行另一個(gè)線程的?! ?/p>
說(shuō)明:
如果一個(gè)對(duì)象有多個(gè)synchronized方法,某一時(shí)刻某個(gè)線程已經(jīng)進(jìn)入到了某個(gè)synchronized方法,那么在該方法沒(méi)有執(zhí)行完畢前,其他線程是無(wú)法訪問(wèn)該對(duì)象的任何synchronized方法的。
結(jié)論:
當(dāng)synchronized關(guān)鍵字修飾一個(gè)方法的時(shí)候,該方法叫做同步方法。
Java中的每個(gè)對(duì)象都有一個(gè)鎖(lock),或者叫做監(jiān)視器(monitor),當(dāng)一個(gè)線程訪問(wèn)某個(gè)對(duì)象的synchronized方法時(shí),將該對(duì)象上鎖,其他任何線程都無(wú)法再去訪問(wèn)該對(duì)象的synchronized方法了(這里是指所有的同步方法,而不僅僅是同一個(gè)方法),直到之前的那個(gè)線程執(zhí)行方法完畢后(或者是拋出了異常),才將該對(duì)象的鎖釋放掉,其他線程才有可能再去訪問(wèn)該對(duì)象的synchronized方法。
注意這時(shí)候是給對(duì)象上鎖,如果是不同的對(duì)象,則各個(gè)對(duì)象之間沒(méi)有限制關(guān)系。
嘗試在代碼中構(gòu)造第二個(gè)線程對(duì)象時(shí)傳入一個(gè)新的Example對(duì)象,則兩個(gè)線程的執(zhí)行之間沒(méi)有什么制約關(guān)系。
3、靜態(tài)的同步方法
當(dāng)一個(gè)synchronized關(guān)鍵字修飾的方法同時(shí)又被static修飾,之前說(shuō)過(guò),非靜態(tài)的同步方法會(huì)將對(duì)象上鎖,但是靜態(tài)方法不屬于對(duì)象,而是屬于類(lèi),它會(huì)將這個(gè)方法所在的類(lèi)的Class對(duì)象上鎖。
public class ThreadTest {
public static void main(String[] args) {
Example example = new Example();
Example example2 = new Example();
Thread t1 = new Thread1(example);
Thread t2 = new Thread2(example2);
t1.start();
t2.start();
}
}
class Example {
public synchronized static void excute() {
for (int i = 0; i < 5; ++i) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("excute:" + i);
}
}
public synchronized static void excute1() {
for (int i = 0; i < 5; ++i) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("excute1:" + i);
}
}
}
class Thread1 extends Thread {
private Example example;
public Thread1(Example example) {
this.example = example;
}
@Override
public void run() {
example.excute();
}
}
class Thread2 extends Thread {
private Example example;
public Thread2(Example example) {
this.example = example;
}
@Override
public void run() {
example.excute1();
}
}
執(zhí)行結(jié)果如下
excute:0
excute:1
excute:2
excute:3
excute:4
excute1:0
excute1:1
excute1:2
excute1:3
excute1:4
如果沒(méi)有static修飾符,兩個(gè)線程分別傳入不同的對(duì)象,則會(huì)同時(shí)并發(fā)執(zhí)行
所以如果是靜態(tài)方法的情況(execute()和execute2()都加上static關(guān)鍵字),即便是向兩個(gè)線程傳入不同的Example對(duì)象,這兩個(gè)線程仍然是互相制約的,必須先執(zhí)行完一個(gè),再執(zhí)行下一個(gè)。
結(jié)論:
如果某個(gè)synchronized方法是static的,那么當(dāng)線程訪問(wèn)該方法時(shí),它鎖的并不是synchronized方法所在的對(duì)象,而是synchronized方法所在的類(lèi)所對(duì)應(yīng)的Class對(duì)象。Java中,無(wú)論一個(gè)類(lèi)有多少個(gè)對(duì)象,這些對(duì)象會(huì)對(duì)應(yīng)唯一一個(gè)Class對(duì)象,因此當(dāng)線程分別訪問(wèn)同一個(gè)類(lèi)的兩個(gè)對(duì)象的兩個(gè)static,synchronized方法時(shí),它們的執(zhí)行順序也是順序的,也就是說(shuō)一個(gè)線程先去執(zhí)行方法,執(zhí)行完畢后另一個(gè)線程才開(kāi)始。
4.synchronized塊
synchronized(object)
{
}
表示線程在執(zhí)行的時(shí)候會(huì)將object對(duì)象上鎖。(注意這個(gè)對(duì)象可以是任意類(lèi)的對(duì)象,也可以使用this關(guān)鍵字)。
這樣就可以自行規(guī)定上鎖對(duì)象。
public class ThreadTest {
public static void main(String[] args) {
Example example = new Example();
Thread t1 = new Thread1(example);
Thread t2 = new Thread2(example);
t1.start();
t2.start();
}
}
class Example {
public void excute() {
synchronized (this) {
for (int i = 0; i < 5; ++i) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("excute:" + i);
}
}
}
public void excute1() {
synchronized (this) {
for (int i = 0; i < 5; ++i) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("excute1:" + i);
}
}
}
}
class Thread1 extends Thread {
private Example example;
public Thread1(Example example) {
this.example = example;
}
@Override
public void run() {
example.excute();
}
}
class Thread2 extends Thread {
private Example example;
public Thread2(Example example) {
this.example = example;
}
@Override
public void run() {
example.excute1();
}
}
執(zhí)行結(jié)果如下
excute:0
excute:1
excute:2
excute:3
excute:4
excute1:0
excute1:1
excute1:2
excute1:3
excute1:4
例子程序4所達(dá)到的效果和例子程序2的效果一樣,都是使得兩個(gè)線程的執(zhí)行順序進(jìn)行,而不是并發(fā)進(jìn)行,當(dāng)一個(gè)線程執(zhí)行時(shí),將object對(duì)象鎖住,另一個(gè)線程就不能執(zhí)行對(duì)應(yīng)的塊。
synchronized方法實(shí)際上等同于用一個(gè)synchronized塊包住方法中的所有語(yǔ)句,然后在synchronized塊的括號(hào)中傳入this關(guān)鍵字。當(dāng)然,如果是靜態(tài)方法,需要鎖定的則是class對(duì)象。
可能一個(gè)方法中只有幾行代碼會(huì)涉及到線程同步問(wèn)題,所以synchronized塊比synchronized方法更加細(xì)粒度地控制了多個(gè)線程的訪問(wèn),只有synchronized塊中的內(nèi)容不能同時(shí)被多個(gè)線程所訪問(wèn),方法中的其他語(yǔ)句仍然可以同時(shí)被多個(gè)線程所訪問(wèn)(包括synchronized塊之前的和之后的)。
結(jié)論:
synchronized方法是一種粗粒度的并發(fā)控制,某一時(shí)刻,只能有一個(gè)線程執(zhí)行該synchronized方法;
synchronized塊則是一種細(xì)粒度的并發(fā)控制,只會(huì)將塊中的代碼同步,位于方法內(nèi)、synchronized塊之外的其他代碼是可以被多個(gè)線程同時(shí)訪問(wèn)到的。
以上就是關(guān)于java多線程編程Synchronized塊同步方法,希望對(duì)大家的學(xué)習(xí)有所幫助。
相關(guān)文章
MybatisPlus實(shí)現(xiàn)對(duì)象嵌套關(guān)聯(lián)查詢(xún)一對(duì)多List集合查詢(xún)
這篇文章主要介紹了MybatisPlus實(shí)現(xiàn)對(duì)象嵌套關(guān)聯(lián)查詢(xún)一對(duì)多List集合查詢(xún),具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-05-05
Windows系統(tǒng)中Java調(diào)用cmd命令及執(zhí)行exe程序的方法
這篇文章主要介紹了Windows系統(tǒng)中Java調(diào)用cmd命令及執(zhí)行exe程序的方法,主要用到了IOException類(lèi),需要的朋友可以參考下2016-03-03
SpringBoot配置默認(rèn)HikariCP數(shù)據(jù)源
咱們開(kāi)發(fā)項(xiàng)目的過(guò)程中用到很多的開(kāi)源數(shù)據(jù)庫(kù)鏈接池,比如druid、c3p0、BoneCP等等,本文主要介紹了SpringBoot配置默認(rèn)HikariCP數(shù)據(jù)源,具有一定的參考價(jià)值,感興趣的可以了解一下2023-11-11
一文告訴你為什么要重寫(xiě)hashCode()方法和equals()方法
本篇文章帶大家了解一下為什么重寫(xiě)hashCode()方法和equals()方法,文中有非常詳細(xì)的說(shuō)明以及代碼示例,對(duì)正在學(xué)習(xí)java的小伙伴們很有幫助,需要的朋友可以參考下2021-05-05
簡(jiǎn)單了解SpringBoot HATEOAS使用方法
這篇文章主要介紹了簡(jiǎn)單了解SpringBoot HATEOAS使用方法,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2019-10-10

