|
|
|
用Java线程获取优异性能(五) |
|
|
作者:未知 来源:未知 加入时间:2005-10-26 人气:118 |
你能够使用synchronized声明去消除NeedForSynchronizationDemo的竞态条件。如何消除,请看练习列表2:
列表2.
SynchronizationDemo1.java
// SynchronizationDemo1.java
class SynchronizationDemo1
{
public static void main (String [] args)
{
FinTrans ft = new FinTrans ();
TransThread tt1 = new TransThread (ft, "Deposit Thread");
TransThread tt2 = new TransThread (ft, "Withdrawal Thread");
tt1.start ();
tt2.start ();
}
}
class FinTrans
{
public static String transName;
public static double amount;
}
class TransThread extends Thread
{
private FinTrans ft;
TransThread (FinTrans ft, String name)
{
super (name); //保存线程的名称 Save thread's name
this.ft = ft; //保存对金融事务对象的引用
}
public void run ()
{
for (int i = 0; i < 100; i++)
{
if (getName ().equals ("Deposit Thread"))
{
synchronized (ft)
{
ft.transName = "Deposit";
try
{
Thread.sleep ((int) (Math.random () * 1000));
}
catch (InterruptedException e)
{
}
ft.amount = 2000.0;
System.out.println (ft.transName + " " + ft.amount);
}
}
else
{
synchronized (ft)
{
ft.transName = "Withdrawal";
try
{
Thread.sleep ((int) (Math.random () * 1000));
}
catch (InterruptedException e)
{
}
ft.amount = 250.0;
System.out.println (ft.transName + " " + ft.amount);
}
}
}
}
}
仔细看看SynchronizationDemo1,run()方法包含两个夹在synchronized (ft) { and }间的关键代码部份。每个存款和取款线程必须在任一线程进入它的关键代码部份前获得与ft引用的FinTrans对象相关的锁。假如如果存款线程在它的关键代码部份且取款线程想进入它自己的关键代码部份,取款线程就应努力获得锁。因为当存款线程在它的关键代码部份执行时控制着锁, JVM 便强迫取款线程等待直到存款线程执行完关键代码部份并释放锁。(当执行离开关键代码部份时,锁自动释放)
技巧:当你需要决定是否一个线程控制与一个给定对象相关的锁时,调用Thread的静态布尔holdsLock(Object o)方法。如果线程调用控制着与对象相关的锁的方法,这个方法便返回一个布尔真值。否则,返回一个假值。例如,如果你打算将System.out.println (Thread.holdsLock (ft))放置在SynchronizationDemo1的main()方法末尾, holdsLock()将返回假值。返回 假值是因为执行main()方法的主线程没有使用同步机制获得任何锁。可是,如果你打算将System.out.println (Thread.holdsLock (ft))放在run()的synchronized (ft)声明中, holdsLock()将返回真值因为无论是存款线程或是取款线程都不得不在那些线程能够进入它的关键代码部份前获得与ft引用的FinTrans对象相关的锁。
Synchronized方法
你能够通过你的程序的源代码使用synchronized声明。然而,你也可能陷入过多使用这样的声明而导致代码效率低。例如,假设你的程序包含一个带两个连续synchronized声明的方法,每一个声明都企图获得同一公共对象的锁。因为获得和翻译对象的锁要消耗时间,重复调用(在一个循环中)那个方法会降低程序的性能。每次对那个方法的一个调用都必须获得和释放两个锁。程序花费大量的时间获得和释放锁。要消除这个问题,你应考虑使用同步方法。
一个同步方法不是一个实例就是一个其头包含synchronized关键字的类方法。例如: synchronized void print (String s)。当你同步一个完整实例方法时,一个线程必须获得与那个方法调用出现的对象相关的锁。例如,给一个ft.update("Deposit", 2000.0)实例方法调用,并且假定update()是同步的,一个方法必须获得与ft引用的对象相关的锁。要看一个SynchronizationDemo1版本的同步方法的源代码,请查看列表3:
列表3.
SynchronizationDemo2.java
// SynchronizationDemo2.java
class SynchronizationDemo2
{
public static void main (String [] args)
{
FinTrans ft = new FinTrans ();
TransThread tt1 = new TransThread (ft, "Deposit Thread");
TransThread tt2 = new TransThread (ft, "Withdrawal Thread");
tt1.start ();
tt2.start ();
}
}
class FinTrans
{
private String transName;
private double amount;
synchronized void update (String transName, double amount)
{
this.transName = transName;
this.amount = amount;
System.out.println (this.transName + " " + this.amount);
}
}
class TransThread extends Thread
{
private FinTrans ft;
TransThread (FinTrans ft, String name)
{
super (name); //保存线程名称
this.ft = ft; //保存对金融事务对象的引用
}
public void run ()
{
for (int i = 0; i < 100; i++)
if (getName ().equals ("Deposit Thread"))
ft.update ("Deposit", 2000.0);
else
ft.update ("Withdrawal", 250.0);
}
}
虽然比列表2稍微更简洁,表3达到的是同一目的。如果存款线程调用update()方法, JVM检查看是否取款线程已经获得与ft引用的对象相关的锁。如果是这样,存款线程就等待。否则,那个线程就进入关键代码部份。
SynchronizationDemo2示范了一个同步实例方法。然而,你也能够同步class 方法。例如, java.util.Calendar类声明了一个public static synchronized Locale [] getAvailableLocales() 方法。因为类方法没有一个this引用的概念,那么类方法从哪里获得它的锁呢?类方法从类对象获得它们的锁——每一个与Class对象相关的载入的类,从那些载入的类的类方法得到它们的锁。我称这样的锁为class locks。
|
|
相关文章:
相关软件:
|
|
|