synchronized同步块实例
在java中,每个对象都包含了一把锁(也叫做“监视器”),它自动成为对象的一部分(不必为此写任何特殊的代码)。在给定时刻,只有一个线程可以拥有一个对象的监视器。
示例:线程1进入withdrawal方法时,获得监视器(加锁);当线程1的方法执行完毕返回时,释放监视器(开锁),线程2的withdrawal方能进入
a. 为了确保在任何时刻一个共享对象只被一个线程使用,必须使用“同步(synchronized)”
b. 有两种方式实现同步
a) 使用同步方法
synchronized void methodA() {}
b) 使用同步块
synchronized(obj) { //obj是被锁定的对象
//要同步的语句
}
c. 用synchronized来标识的块或方法即为监视器监视的部分。只有使用synchronized,才能利用对象的监视器功能
d. 调用任何synchronized方法时,对象就会被锁定,不可再调用那个对象的其他任何synchronized方法,除非第一个方法完成了自已的工作,并解除锁定。因此,一个特定对象的所有synchronized方法都共享着一把锁,而且这把锁能防止多个方法对通用内存同时进行写操作(比如同时有多个线程)【任何一个Object有且仅有一把锁,所以方法级的synchronized会对其它有synchronized的方法也起作用,即运行时,此对象最多只能有一个带有synchronized的方法运行】
e. 每个类也有自已的一把锁(作为类的Class对象的一部分),所以synchronized static方法可在一个类的范围内被相互间锁定起来,防止与static数据的接触
f. 一般情况下,只在方法的层次上使用同步
实例:
java">package com.bijian.thread;
/*
* 定义了操作类型和金额
*/
public class FinTrans {
public static String transName;
public static double amount;
}
java">package com.bijian.thread;
public class TransThread extends Thread {
private FinTrans ft;
TransThread(FinTrans ft, String name) {
super(name); // Save thread's name
this.ft = ft; // Save reference to financial transaction object
}
public void run() {
for (int i = 0; i < 100; i++) {
if (getName().equals("Deposit Thread")) {
// Start of deposit thread's critical code section
// 存款操作(每次存2000元)
ft.transName = "Deposit";
try {
Thread.sleep((int) (Math.random() * 1000));
} catch (InterruptedException e) {
}
ft.amount = 2000.0;
System.out.println(ft.transName + " " + ft.amount);
// End of deposit thread's critical code section
} else {
// Start of withdrawal thread's critical code section
// 取款操作(每次取250元)
ft.transName = "Withdrawal";
try {
Thread.sleep((int) (Math.random() * 1000));
} catch (InterruptedException e) {
}
ft.amount = 250.0;
System.out.println(ft.transName + " " + ft.amount);
// End of withdrawal thread's critical code section
}
}
}
}
java">package com.bijian.thread;
public class NeedForSynchronizationDemo {
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();
}
}
运行结果:
Withdrawal 250.0 Withdrawal 2000.0 Deposit 250.0 Withdrawal 2000.0 Deposit 250.0 Withdrawal 2000.0 Deposit 250.0 Withdrawal 2000.0 Deposit 250.0 Withdrawal 2000.0 Deposit 250.0 …
发现 Withdrawal 的操作金额有些变成了 2000.0 ,而 Deposit 的操作金额有些变成了 250.0 。
一.同步块解决
修改TransThread类,加上synchronized同步块,如下所示:
java">package com.bijian.thread;
public class TransThread extends Thread {
private FinTrans ft;
TransThread(FinTrans ft, String name) {
super(name); // Save thread's name
this.ft = ft; // Save reference to financial transaction object
}
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);
}
}
}
}
}
运行结果如下所示:
Deposit 2000.0 Deposit 2000.0 Withdrawal 250.0 Withdrawal 250.0 Deposit 2000.0 Deposit 2000.0 Deposit 2000.0 Withdrawal 250.0 Withdrawal 250.0 Deposit 2000.0
二.同步方法解决
按现实中的场景,同步的动作应该放在FinTrans类中,而不应由访问的第三方来控制,因此修改实例如下。
java">package com.bijian.thread;
public class FinTrans {
private String transName;
private double amount;
public synchronized void update(String transName, double amount) {
this.transName = transName;
this.amount = amount;
System.out.println(this.transName + " " + this.amount);
}
public synchronized void save(double amount) {
this.transName = "Withdrawal";
this.amount = amount;
System.out.println(this.transName + " " + this.amount);
try {
Thread.sleep((int) (Math.random() * 1000));
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
public synchronized void take(double amount) {
this.transName = "Deposit";
this.amount = amount;
System.out.println(this.transName + " " + this.amount);
try {
Thread.sleep((int) (Math.random() * 1000));
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
java">package com.bijian.thread;
public class TransThread extends Thread {
private FinTrans ft;
TransThread(FinTrans ft, String name) {
super(name); // Save thread's name
this.ft = ft; // Save reference to financial transaction object
}
public void run() {
for (int i = 0; i < 100; i++)
if (getName().equals("Deposit Thread"))
ft.take(2000.0);
else
ft.save(250.0);
}
}
运行 NeedForSynchronizationDemo 类的 main 方法,结果如下所示:
Deposit 2000.0 Withdrawal 250.0 Deposit 2000.0 Withdrawal 250.0 Deposit 2000.0 Withdrawal 250.0 Deposit 2000.0 Withdrawal 250.0 Deposit 2000.0 Withdrawal 250.0 Deposit 2000.0 Withdrawal 250.0 Deposit 2000.0 Deposit 2000.0 Withdrawal 250.0 Deposit 2000.0 …