建立Test類的對(duì)象實(shí)例如下:
Test test = new Test();
對(duì)于靜態(tài)方法來說,只要加上了synchronized關(guān)鍵字,這個(gè)方法就是同步的,無論是使用test.method(),還是使用Test.method()來調(diào)用method方法,method都是同步的,并不存在非靜態(tài)方法的多個(gè)實(shí)例的問題。
在23種設(shè)計(jì)模式中的單件(Singleton)模式如果按傳統(tǒng)的方法設(shè)計(jì),也是線程不安全的,下面的代碼是一個(gè)線程不安全的單件模式。
// 線程安全的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();
}
}
在使用synchronized關(guān)鍵字時(shí)有以下四點(diǎn)需要注意:
1. synchronized關(guān)鍵字不能繼承。
雖然可以使用synchronized來定義方法,但synchronized并不屬于方法定義的一部分,因此,synchronized關(guān)鍵字不能被繼承。如果在父類中的某個(gè)方法使用了synchronized關(guān)鍵字,而在子類中覆蓋了這個(gè)方法,在子類中的這個(gè)方法默認(rèn)情況下并不是同步的,而必須顯式地在子類的這個(gè)方法中加上synchronized關(guān)鍵字才可以。當(dāng)然,還可以在子類方法中調(diào)用父類中相應(yīng)的方法,這樣雖然子類中的方法不是同步的,但子類調(diào)用了父類的同步方法,因此,子類的方法也就相當(dāng)于同步了。這兩種方式的例子代碼如下:
在子類方法中加上synchronized關(guān)鍵字
在子類方法中調(diào)用父類的同步方法
2. 在定義接口方法時(shí)不能使用synchronized關(guān)鍵字。
3. 構(gòu)造方法不能使用synchronized關(guān)鍵字,但可以使用下節(jié)要討論的synchronized塊來進(jìn)行同步。
4. synchronized可以自由放置。
在前面的例子中使用都是將synchronized關(guān)鍵字放在方法的返回類型前面。但這并不是synchronized可放置唯一位置。在非靜態(tài)方法中,synchronized還可以放在方法定義的最前面,在靜態(tài)方法中,synchronized可以放在static的前面,代碼如下:
但要注意,synchronized不能放在方法返回類型的后面,如下面的代碼是錯(cuò)誤的:
synchronized關(guān)鍵字只能用來同步方法,不能用來同步類變量,如下面的代碼也是錯(cuò)誤的。
雖然使用synchronized關(guān)鍵字同步方法是最安全的同步方式,但大量使用synchronized關(guān)鍵字會(huì)造成不必要的資源消耗以及性能損失。雖然從表面上看synchronized鎖定的是一個(gè)方法,但實(shí)際上synchronized鎖定的是一個(gè)類。也就是說,如果在非靜態(tài)方法method1和method2定義時(shí)都使用了synchronized,在method1未執(zhí)行完之前,method2是不能執(zhí)行的。靜態(tài)方法和非靜態(tài)方法的情況類似。但靜態(tài)和非靜態(tài)方法不會(huì)互相影響。看看如下的代碼:
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é)果如下:
從上面的運(yùn)行結(jié)果可以看出,method2和method4在method1和method3未結(jié)束之前不能運(yùn)行。因此,我們可以得出一個(gè)結(jié)論,如果在類中使用synchronized關(guān)鍵字來定義非靜態(tài)方法,那將影響這個(gè)中的所有使用synchronized關(guān)鍵字定義的非靜態(tài)方法。如果定義的是靜態(tài)方法,那么將影響類中所有使用synchronized關(guān)鍵字定義的靜態(tài)方法。這有點(diǎn)象數(shù)據(jù)表中的表鎖,當(dāng)修改一條記錄時(shí),系統(tǒng)就將整個(gè)表都鎖住了,因此,大量使用這種同步方式會(huì)使程序的性能大幅度下降。
新聞熱點(diǎn)
疑難解答
圖片精選