线程同步:
一、线程同步:
线程的同步,就是要保证线程在执行某个计算时,需要保证相关的数据在这个计算过程中只能被一个线程访问,即保证访问数据的原子性---就像一个坐位,只能同时被一个人(线程)所使用。
二、举例,以在银行取钱为例子:
1.创建账户:
package 线程同步;
/**
* 银行账户对象
* @author Administrator
*
*/
public class Account {
private int total=0;
//构造有指定金额的银行账户
public Account(int save){
this.total = save;
}
public String toString(){
return "账户余额:"+total;
}
//取钱的
private void disSave(int count){
try{
Thread.sleep(1000);
}catch(Exception ed){
}
total=total-count;
}
//从账户上提取现金的调用方法
public int getCash(int count){
//金额不足
if (total<count) {
return -1;
}
//从账户上减去提取金额
disSave(count);
return count;
}
}
2.取钱线程:
package 线程同步;
public class GetCashThread extends Thread {
private Account ac;//操作账户
private String flag;//取钱方式
private int disCount;//取现金额
@Override
public void run() {
// TODO Auto-generated method stub
//super.run();
//在这里取钱
//synchronized (ac) {
int cash=ac.getCash(disCount);
System.out.println(flag+"取了:"+cash);
//}
}
public GetCashThread(Account ac,String flag,int disCount){
this.ac = ac;
this.flag = flag;
this.disCount = disCount;
}
}
3.Main类:
package 线程同步;
public class DriverBank {
public static void main(String[] args) {
//创建一个账号:
Account ac = new Account(5000);
//从ATM取
GetCashThread atmGet = new GetCashThread(ac, "ATM取款", 2500);
//从柜台取
GetCashThread counterGet = new GetCashThread(ac, "从柜台取款", 2600);
//启动取款线程
atmGet.start();
counterGet.start();
//主线程暂停,等到前面两个取款线程完成,打印出账户余额
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println(ac);
}
}
三、运行结果:
账户共有5000,可是分别取到2500,2600,余额是-100,这就是线程不同步,所造成的结果.ATM的线程取钱时,柜台取钱进入取钱,此时的ATM的线程还未结算完,导致账户上还是5000,
四、处理
解决原理:避免多个线程同时操作某一个对象的数据,就要让多线程操作对象的数据时候有一个先后顺序,一个操作完成另一个线程在操作的。
解决1---使用synchronized关键字
这个关键字可以放在一个方法前,表示这个方法同时只能被一个线程访问,或通过锁定某个对象放在代码块前,表示其限定的代码块只能同时被一个线程防问。
代码改善:
synchronized (ac) {
int cash=ac.getCash(disCount);
System.out.println(flag+"取了:"+cash);
}
运行结果:
解决2--使用java.util.concurrent包
这个包中有许多对并发编程有良好支持的类,其中新增了一个java.util.concurrent.locks.Lock接口,这是一个“锁”对象的接口,它的多个实现的子类可用来支持线程同步时灵活的锁机制
代码改善:
package 线程同步;
import java.util.concurrent.locks.*;
import java.util.concurrent.locks.ReentrantLock;
public class GetCashThread extends Thread {
private Account ac;//操作账户
private String flag;//取钱方式
private int disCount;//取现金额
//定义一个同步锁
private static Lock myLock = new ReentrantLock();
@Override
public void run() {
// TODO Auto-generated method stub
//super.run();
//在这里取钱
try{
myLock.lock();// 仅有一个线程可同时执行此段代码
int cash=ac.getCash(disCount);
System.out.println(flag+"取了:"+cash);
}finally{
myLock.unlock();
}
}
public GetCashThread(Account ac,String flag,int disCount){
this.ac = ac;
this.flag = flag;
this.disCount = disCount;
}
}
运行结果:
五、总结.
避免多线程同时操作某一对象的数据,要给线程加上执行顺序.