线程分为同步编程模型和异步编程模型,他们有什么区别?
假设现在又t1和t2两个线程
异步编程模型: t1线程执行t1的,t2线程执行t2的,两个线程之间谁也不等谁
同步编程模型:t1线程和t2线程执行,当t1线程必须等t2线程执行结束之后,t1线程才能执行
我们之前提到线程都是异步的,因为可以提高程序的执行速度,那么为什么要引入同步线程?
同步线程的优点:
为了数据的安全, 例如银行取钱,尽管应用程序的使用率降低但是为了保证数据是安全的,必须加入线程同步机制,线程同步机制使得程序变成了单线程
什么情况下使用线程同步?
- 必须是多线程环境
- 多线程环境共享同一个数据
- 共享的数据涉及到修改操作
下面通过银行取款的例子来认识 线程异步和线程同步的区别,同时说明在一定条件下我们需要使用线程同步机制。
使用异步线程情况:
当A和B同时操作一个账户W,A先对W进行取钱动作,但是这个时候B也对账户W进行取钱,就会出现错误
public class ThreadTest{
public static void main(String[] args){
//创建一个公共的账户
Account act=new Account("actno-001",5000.0) //账户和余额
//创建线程对同一个账户取款
Porcessor p=new Porcessor(act)
thread t1=new thread(p);
thread t2=new thread(p);
t1.start();
t2.start();
}
}
//账户
class Account{
private String acton;//账户
private double balance;//余额
public Account(){}
public Account(String actno,double balance){
this.actno=actno;
this.balance=balance;
}
//setter and getter
//设置账户
public void setActno(String actno){
this.actno=actno;
}
//设置余额方法
public void setBalance(double balance){
this.balance=balance;
}
public string getActno(){
return actno;
}
public double getBalance(){
return balance;
}
//对外提供一个取款的方法
public void widthdraw(double money){
//对当前账户进行取款操作
double after=balance-money;
//更新余额
this.setBalance(after);
}
//取款线程 目的是让两个线程共享同一个账户
class Porcessor implements Runnable{
//账户
Account act;
//构造函数
Porcessor(Account act){
this.act=act;
}
//取款方法
public void run(){
act.widthdraw(1000.0);
system.out.println("取款1000成功,余额"+act.getBalance())
}
}
结果显示:
第一次取钱的话 5000-1000=4000 但是当还没有执行更新的时候
t2又来取钱 t2来取的时候balance 还是 5000 说明这个时候已经错了,因为balance还没有更新,所以这个程序有可能执行成功 有可能执行失败
所以错误,因为t1取完钱还没有来得及更新账户,t2就对账户进行了操作,由于是异步线程,所以互补影响,这就是异步带来的危害。
下面我们用同步线程机制来避免这样的错误发生,提高了安全性:
public class ThreadTest{
public static void main(String[] args){
//创建一个公共的账户
Account act=new Account("actno-001",5000.0) //账户和余额
//创建线程对同一个账户取款
Porcessor p=new Porcessor(act)
thread t1=new thread(p);
thread t2=new thread(p);
t1.start();
t2.start();
}
}
//账户
class Account{
private String acton;//账户
private double balance;//余额
public Account(){}
public Account(String actno,double balance){
this.actno=actno;
this.balance=balance;
}
//setter and getter
//设置账户
public void setActno(String actno){
this.actno=actno;
}
//设置余额方法
public void setBalance(double balance){
this.balance=balance;
}
public string getActno(){
return actno;
}
public double getBalance(){
return balance;
}
//对外提供一个取款的方法
public void widthdraw(double money){
//对当前账户进行取款操作
synchronized(this){
double after=balance-money;
//更新余额
this.setBalance(after);
}
}
//取款线程 目的是让两个线程共享同一个账户
class Porcessor implements Runnable{
//账户
Account act;
//构造函数
Porcessor(Account act){
this.act=act;
}
//取款方法
public void run(){
act.widthdraw(1000.0);
system.out.println("取款1000成功,余额"+act.getBalance())
}
}
运行结果:
仔细对比上面的两个代码,你会发现除了多了 synchronized(this){}之外,没有其他的变化,所以这个关键字就是操作需要同步执行的代码,把需要同步的代码,放到同步语句块 synchronized(this){}中。
原理:
t1线程和t2线程,t1线程执行到这个地方,遇到了synchronized关键字,就会去找this的对象锁,如果找到this对象锁,则进入同步语句块中执行程序,当同步语句块中的代码执行结束后,t1线程归还this的对象锁,
在t1线程执行同步语句块的过程中,如果t2线程也过来执行以下代码,也遇到synchronized关键字,所以也去找this的对象锁,但是发现该对象锁被t1线程持有,只能等待this对象的归还.