本篇內(nèi)容主要講解“如何使用Synchronized關(guān)鍵字同步類(lèi)方法”,感興趣的朋友不妨來(lái)看看。本文介紹的方法操作簡(jiǎn)單快捷,實(shí)用性強(qiáng)。下面就讓小編來(lái)帶大家學(xué)習(xí)“如何使用Synchronized關(guān)鍵字同步類(lèi)方法”吧!
10年積累的網(wǎng)站設(shè)計(jì)、網(wǎng)站制作經(jīng)驗(yàn),可以快速應(yīng)對(duì)客戶對(duì)網(wǎng)站的新想法和需求。提供各種問(wèn)題對(duì)應(yīng)的解決方案。讓選擇我們的客戶得到更好、更有力的網(wǎng)絡(luò)服務(wù)。我雖然不認(rèn)識(shí)你,你也不認(rèn)識(shí)我。但先制作網(wǎng)站后付款的網(wǎng)站建設(shè)流程,更有左貢免費(fèi)網(wǎng)站建設(shè)讓你可以放心的選擇與我們合作。
要想解決“臟數(shù)據(jù)”的問(wèn)題,最簡(jiǎn)單的方法就是使用synchronized關(guān)鍵字來(lái)使run方法同步,代碼如下:
public synchronized void run() { }
從上面的代碼可以看出,只要在void和public之間加上synchronized關(guān)鍵字,就可以使run方法同步,也就是說(shuō),對(duì)于同一個(gè)Java類(lèi)的對(duì)象實(shí)例,run方法同時(shí)只能被一個(gè)線程調(diào)用,并當(dāng)前的run執(zhí)行完后,才能被其他的線程調(diào)用。即使當(dāng)前線程執(zhí)行到了run方法中的yield方法,也只是暫停了一下。由于其他線程無(wú)法執(zhí)行run方法,因此,最終還是會(huì)由當(dāng)前的線程來(lái)繼續(xù)執(zhí)行。先看看下面的代碼:
sychronized關(guān)鍵字只和一個(gè)對(duì)象實(shí)例綁定
class Test { public synchronized void method() { } } public class Sync implements Runnable { private Test test; public void run() { test.method(); } public Sync(Test test) { this.test = test; } public static void main(String[] args) throws Exception { Test test1 = new Test(); Test test2 = new Test(); Sync sync1 = new Sync(test1); Sync sync2 = new Sync(test2); new Thread(sync1).start(); new Thread(sync2).start(); } }
在Test類(lèi)中的method方法是同步的。但上面的代碼建立了兩個(gè)Test類(lèi)的實(shí)例,因此,test1和test2的method方法是分別執(zhí)行的。要想讓method同步,必須在建立Sync類(lèi)的實(shí)例時(shí)向它的構(gòu)造方法中傳入同一個(gè)Test類(lèi)的實(shí)例,如下面的代碼所示:
Sync sync1 = new Sync(test1); 不僅可以使用synchronized來(lái)同步非靜態(tài)方法,也可以使用synchronized來(lái)同步靜態(tài)方法。如可以按如下方式來(lái)定義method方法:
class Test { public static synchronized void method() { } }
建立Test類(lèi)的對(duì)象實(shí)例如下:
Test test = new Test();
對(duì)于靜態(tài)方法來(lái)說(shuō),只要加上了synchronized關(guān)鍵字,這個(gè)方法就是同步的,無(wú)論是使用test.method(),還是使用Test.method()來(lái)調(diào)用method方法,method都是同步的,并不存在非靜態(tài)方法的多個(gè)實(shí)例的問(wèn)題。
在23種設(shè)計(jì)模式中的單件(Singleton)模式如果按傳統(tǒng)的方法設(shè)計(jì),也是線程不安全的,下面的代碼是一個(gè)線程不安全的單件模式。
package test; // 線程安全的Singleton模式 class Singleton { private static Singleton sample; private Singleton() { } public static Singleton getInstance() { if (sample == null) { Thread.yield(); // 為了放大Singleton模式的線程不安全性 sample = new Singleton(); } return sample; } } public class MyThread extends Thread { public void run() { Singleton singleton = Singleton.getInstance(); System.out.println(singleton.hashCode()); } public static void main(String[] args) { Thread threads[] = new Thread[5]; for (int i = 0; i < threads.length; i++) threads[i] = new MyThread(); for (int i = 0; i < threads.length; i++) threads[i].start(); } }
在上面的代碼調(diào)用yield方法是為了使單件模式的線程不安全性表現(xiàn)出來(lái),如果將這行去掉,上面的實(shí)現(xiàn)仍然是線程不安全的,只是出現(xiàn)的可能性小得多。
程序的運(yùn)行結(jié)果如下:
25358555
26399554
7051261
29855319
5383406
上面的運(yùn)行結(jié)果可能在不同的運(yùn)行環(huán)境上有所有同,但一般這五行輸出不會(huì)完全相同。從這個(gè)輸出結(jié)果可以看出,通過(guò)getInstance方法得到的對(duì)象實(shí)例是五個(gè),而不是我們期望的一個(gè)。這是因?yàn)楫?dāng)一個(gè)線程執(zhí)行了Thread.yield()后,就將CPU資源交給了另外一個(gè)線程。由于在線程之間切換時(shí)并未執(zhí)行到創(chuàng)建Singleton對(duì)象實(shí)例的語(yǔ)句,因此,這幾個(gè)線程都通過(guò)了if判斷,所以,就會(huì)產(chǎn)生了建立五個(gè)對(duì)象實(shí)例的情況(可能創(chuàng)建的是四個(gè)或三個(gè)對(duì)象實(shí)例,這取決于有多少個(gè)線程在創(chuàng)建Singleton對(duì)象之前通過(guò)了if判斷,每次運(yùn)行時(shí)可能結(jié)果會(huì)不一樣)。
要想使上面的單件模式變成線程安全的,只要為getInstance加上synchronized關(guān)鍵字即可。代碼如下:
public static synchronized Singleton getInstance() { }
當(dāng)然,還有更簡(jiǎn)單的方法,就是在定義Singleton變量時(shí)就建立Singleton對(duì)象,代碼如下:
private static final Singleton sample = new Singleton();
然后在getInstance方法中直接將sample返回即可。這種方式雖然簡(jiǎn)單,但不知在getInstance方法中創(chuàng)建Singleton對(duì)象靈活。讀者可以根據(jù)具體的需求選擇使用不同的方法來(lái)實(shí)現(xiàn)單件模式。
在使用synchronized關(guān)鍵字時(shí)有以下四點(diǎn)需要注意:
1. synchronized關(guān)鍵字不能繼承。
雖然可以使用synchronized來(lái)定義方法,但synchronized并不屬于方法定義的一部分,因此,synchronized關(guān)鍵字不能被繼承。如果在父類(lèi)中的某個(gè)方法使用了synchronized關(guān)鍵字,而在子類(lèi)中覆蓋了這個(gè)方法,在子類(lèi)中的這個(gè)方法默認(rèn)情況下并不是同步的,而必須顯式地在子類(lèi)的這個(gè)方法中加上synchronized關(guān)鍵字才可以。當(dāng)然,還可以在子類(lèi)方法中調(diào)用父類(lèi)中相應(yīng)的方法,這樣雖然子類(lèi)中的方法不是同步的,但子類(lèi)調(diào)用了父類(lèi)的同步方法,因此,子類(lèi)的方法也就相當(dāng)于同步了。這兩種方式的例子代碼如下:
在子類(lèi)方法中加上synchronized關(guān)鍵字
class Parent { public synchronized void method() { } } class Child extends Parent { public synchronized void method() { } }
在子類(lèi)方法中調(diào)用父類(lèi)的同步方法
class Parent { public synchronized void method() { } } class Child extends Parent { public void method() { super.method(); } }
2. 在定義接口方法時(shí)不能使用synchronized關(guān)鍵字。
3. 構(gòu)造方法不能使用synchronized關(guān)鍵字,但可以使用下節(jié)要討論的synchronized塊來(lái)進(jìn)行同步。
4. synchronized可以自由放置。
在前面的例子中使用都是將synchronized關(guān)鍵字放在方法的返回類(lèi)型前面。但這并不是synchronized可放置***位置。在非靜態(tài)方法中,synchronized還可以放在方法定義的最前面,在靜態(tài)方法中,synchronized可以放在static的前面,代碼如下:
public synchronized void method(); synchronized public void method(); public static synchronized void method(); public synchronized static void method(); synchronized public static void method();
但要注意,synchronized不能放在方法返回類(lèi)型的后面,如下面的代碼是錯(cuò)誤的:
public void synchronized method(); public static void synchronized method();
synchronized關(guān)鍵字只能用來(lái)同步方法,不能用來(lái)同步類(lèi)變量,如下面的代碼也是錯(cuò)誤的。
public synchronized int n = 0; public static synchronized int n = 0;
雖然使用synchronized關(guān)鍵字同步方法是最安全的同步方式,但大量使用synchronized關(guān)鍵字會(huì)造成不必要的資源消耗以及性能損失。雖然從表面上看synchronized鎖定的是一個(gè)方法,但實(shí)際上synchronized鎖定的是一個(gè)類(lèi)。也就是說(shuō),如果在非靜態(tài)方法method1和method2定義時(shí)都使用了synchronized,在method1未執(zhí)行完之前,method2是不能執(zhí)行的。靜態(tài)方法和非靜態(tài)方法的情況類(lèi)似。但靜態(tài)和非靜態(tài)方法不會(huì)互相影響。看看如下的代碼:
package test; public class MyThread1 extends Thread { public String methodName; public static void method(String s) { System.out.println(s); while (true) ; } public synchronized void method1() { method("非靜態(tài)的method1方法"); } public synchronized void method2() { method("非靜態(tài)的method2方法"); } public static synchronized void method3() { method("靜態(tài)的method3方法"); } public static synchronized void method4() { method("靜態(tài)的method4方法"); } public void run() { try { getClass().getMethod(methodName).invoke(this); } catch (Exception e) { } } public static void main(String[] args) throws Exception { MyThread1 myThread1 = new MyThread1(); for (int i = 1; i <= 4; i++) { myThread1.methodName = "method" + String.valueOf(i); new Thread(myThread1).start(); sleep(100); } } }
運(yùn)行結(jié)果如下:
非靜態(tài)的method1方法
靜態(tài)的method3方法
從上面的運(yùn)行結(jié)果可以看出,method2和method4在method1和method3未結(jié)束之前不能運(yùn)行。因此,我們可以得出一個(gè)結(jié)論,如果在類(lèi)中使用synchronized關(guān)鍵字來(lái)定義非靜態(tài)方法,那將影響這個(gè)中的所有使用synchronized關(guān)鍵字定義的非靜態(tài)方法。如果定義的是靜態(tài)方法,那么將影響類(lèi)中所有使用synchronized關(guān)鍵字定義的靜態(tài)方法。這有點(diǎn)象數(shù)據(jù)表中的表鎖,當(dāng)修改一條記錄時(shí),系統(tǒng)就將整個(gè)表都鎖住了,因此,大量使用這種同步方式會(huì)使程序的性能大幅度下降。
到此,相信大家對(duì)“如何使用Synchronized關(guān)鍵字同步類(lèi)方法”有了更深的了解,不妨來(lái)實(shí)際操作一番吧!這里是創(chuàng)新互聯(lián)網(wǎng)站,更多相關(guān)內(nèi)容可以進(jìn)入相關(guān)頻道進(jìn)行查詢,關(guān)注我們,繼續(xù)學(xué)習(xí)!