您的位置:首页 > 编程语言 > Java开发

多线程系列二——java线程间的互斥与同步

2015-09-30 22:03 836 查看
       “线程互斥是指某一资源同时只允许一个访问者对其进行访问,具有唯一性和排它性。但互斥无法限制访问者对资源的访问顺序,即访问是无序的。”
 
我们可以用银行转账的例子加以说明:
1、甲乙两用户对银行同一账户进行操作,余额为1000元;
2、甲用户将银行余额1000元读取到本地,进行取款操作。在进行取款过程中,乙用户向银行账户进行存款300元操作并将账户余额更新,而此时甲用户进行取款是建立在原始1000元的基础上进行的。完成操作后将最终错误的800元余额更新至银行账户。。。
 
用程序模拟此示例:
public class TestTransferAccounts {

//存取银行账户余额
static int balance = 1000;

//main方法中模拟两个用户同时对该银行账户进行更新(其中一个进行取款操作,另一个对账户进行汇款操作)
public static void main(String[] args) {

new TestTransferAccounts().test();
}

public void test(){
BankOperator bankOperator= new BankOperator();

new Thread(
new Runnable() {

@Override
public void run() {
bankOperator.addAccounts();
}
}
).start();

new Thread(
new Runnable() {

@Override
public void run() {
bankOperator.reduceAccounts();
}
}
).start();

//主线程等到另外两个线程操作完成后,查询账户余额进行打印。
try {
Thread.sleep(1000);
System.out.println("查询账户余额:" + balance);
} catch (InterruptedException e) {
e.printStackTrace();
}
}

class BankOperator{
//汇款操作方法
public void addAccounts(){
//将余额读取到本地
int myBalance = balance;
//中间处理过程花费500ms。。。
try {
Thread.sleep(200);
myBalance += 300;
} catch (InterruptedException e) {
e.printStackTrace();
}
//将余额更新回账目
balance = myBalance;
System.out.println(Thread.currentThread().getName() + ":" + balance);
}

//取款操作方法
public void reduceAccounts(){
//将余额读取到本地
int myBalance = balance;
//中间处理过程花费200ms。。。
try {
Thread.sleep(500);
myBalance -= 200;
} catch (InterruptedException e) {
e.printStackTrace();
}
//将余额更新回账目
balance = myBalance;
System.out.println(Thread.currentThread().getName() + ":" + balance);
}
}
}

执行结果:

 


正如文章开头我们提到的线程互斥概念,我们需要对汇款、取款两个线程添加互斥机制,同时只能允许一个用户调用addAccounts或reduceAccounts方法,我们需要对BankOperator对象内的这两个方法添加同步的机制,防止银行将我们的账户余额搞乱。

public class TestTransferAccounts {

//存取银行账户余额
static int balance = 1000;

//main方法中模拟两个用户同时对该银行账户进行更新(其中一个进行取款操作,另一个对账户进行汇款操作)
public static void main(String[] args) {

new TestTransferAccounts().test();
}

public void test(){
BankOperator bankOperator= new BankOperator();

new Thread(
new Runnable() {

@Override
public void run() {
bankOperator.addAccounts();
}
}
).start();

new Thread(
new Runnable() {

@Override
public void run() {
bankOperator.reduceAccounts();
}
}
).start();

//主线程等到另外两个线程操作完成后,查询账户余额进行打印。
try {
Thread.sleep(1000);
System.out.println("查询账户余额:" + balance);
} catch (InterruptedException e) {
e.printStackTrace();
}
}

class BankOperator{
//亏款操作方法
public void addAccounts(){
synchronized (this) {
//将余额读取到本地
int myBalance = balance;
//中间处理过程花费500ms。。。
try {
Thread.sleep(500);
myBalance += 300;
} catch (InterruptedException e) {
e.printStackTrace();
}
//将余额更新回账目
balance = myBalance;
System.out.println(Thread.currentThread().getName() + ":" + balance);

}
}

//取款操作方法
public void reduceAccounts(){
synchronized (this) {
//将余额读取到本地
int myBalance = balance;
//中间处理过程花费200ms。。。
try {
Thread.sleep(500);
myBalance -= 200;
} catch (InterruptedException e) {
e.printStackTrace();
}
//将余额更新回账目
balance = myBalance;
System.out.println(Thread.currentThread().getName() + ":" + balance);
}
}
}
}

执行结果:



synchronized关键字已经帮我们实现了同步的效果,而jdk5.0为我们提供了Lock对象,来实现更为强大的同步效果。
 
有关synchronized与Lock的比较可以参看:

http://www.cnblogs.com/benshan/p/3551987.html#top
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: