Java 线程同步机制

线程分为同步编程模型和异步编程模型,他们有什么区别?

假设现在又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对象的归还.

发布了140 篇原创文章 · 获赞 35 · 访问量 11万+

猜你喜欢

转载自blog.csdn.net/qq_30631063/article/details/104443679