【java学习—十五】线程的同步与死锁(5)


1. 多线程产生的问题

问题:
同一个账户,支付宝转账,微信转账。两个手机,一个手机开支付宝,另一个手机开微信。假设账户上有3000元,支付宝和微信同时提款2000,这时你的账户余额会变成了-1000元吗?
答:
当然不会,出现就有问题了
    
多个线程执行的不确定性引起执行结果的不稳定
多个线程对账本的共享,会造成操作的不完整性,会破坏数据。

代码实现:

package day15;

public class Test2 {
	public static void main(String[] args) {
		
		//定义账户对象
		Account a = new Account();
		
		//多线程对象
		User u_weixin = new User(a, 2000);
		User u_zhifubao = new User(a, 2000);
		
		Thread weixin = new Thread(u_weixin, "微信");
		Thread zhifubao = new Thread(u_zhifubao, "支付宝");
		
		weixin.start();
		zhifubao.start();
		
	}

}

class Account{
	public static int money = 3000;//全局变量,所有操作共享这个变量
	
	/**
	 * 提款先判断账户钱够不够
	 * 多线程调用这个方法就有问题,线程共享资源时,一个线程在执行这个方法没有完毕时,另一个线程又开始执行这个方法
	 * 解决思路:先让一个线程整体执行完这个方法,另一个线程再执行。
	 * @param m
	 */
	public void drawing(int m) {
		String name = Thread.currentThread().getName();
		
		if(money < m) {
			System.out.println(name + "操作,账户金额不足:" + money);
		}else {
			System.out.println(name + "操作,账户原有金额:" + money);
			System.out.println(name + "操作,取款金额:" + m);
			
			money = money - m;
			
			System.out.println(name + "操作,取款后的余额:" + money);
		}
		
	}
}

class User implements Runnable{
	
	Account account;
	int money;
	
	public User(Account account, int money) {
		this.account = account;
		this.money = money;
	}
	
	@Override
	public void run() {
		account.drawing(money);
		
	}
}

运行结果:

在这里插入图片描述

发现金额变成 -1000 了

    

多线程出现了安全问题

问题的原因
    当多条语句在操作同一个线程共享数据时,一个线程对多条语句只执行了一部分,还没有执行完,另一个线程参与进来执行。导致共享数据的错误。

解决办法 :
    对多条操作共享数据的语句,只能让一个线程都执行完,在执行过程中,其他线程不可以参与执行。

2. Synchronized 的使用方法

Java 对于多线程的安全问题提供了专业的解决方式:

同步机制

1 、 synchronized 还可以放在方法声明中,表示整个方法为同步方法。
如果针对对象要加同步锁,那就加在方法上,例如:
    public synchronized void show (String name){
        ....
    }

2 、 锁 代码块

如果针对某一段代码需要加同步锁,那就直接在代码块上加同步锁,例如:
    synchronized ( 对象) {
        // 需要被同步的代码;
    }


解决方法:
直接在方法上加上synchronized关键字

package day15;

public class Test2 {
	public static void main(String[] args) {
		
		//定义账户对象
		Account a = new Account();
		
		//多线程对象
		User u_weixin = new User(a, 2000);
		User u_zhifubao = new User(a, 2000);
		
		Thread weixin = new Thread(u_weixin, "微信");
		Thread zhifubao = new Thread(u_zhifubao, "支付宝");
		
		weixin.start();
		zhifubao.start();
		
	}
}

class Account{
	public static int money = 3000;//全局变量,所有操作共享这个变量
	
	/**
	 * 提款先判断账户钱够不够
	 * 多线程调用这个方法就有问题,线程共享资源时,一个线程在执行这个方法没有完毕时,另一个线程又开始执行这个方法
	 * 解决思路:先让一个线程整体执行完这个方法,另一个线程再执行。
	 * 通过 synchronized同步锁 来完成
	 * 可以直接在方法上加上synchronized关键字
	 * 在普通方法上加同步锁synchronized,锁的是整个对象,不是某一个方法
	 * @param m
	 */
	public synchronized void drawing(int m) {
		String name = Thread.currentThread().getName();
		
		if(money < m) {
			System.out.println(name + "操作,账户金额不足:" + money);
		}else {
			System.out.println(name + "操作,账户原有金额:" + money);
			System.out.println(name + "操作,取款金额:" + m);
			
			money = money - m;
			
			System.out.println(name + "操作,取款后的余额:" + money);
		}
	}
		
}

class User implements Runnable{
	
	Account account;
	int money;
	
	public User(Account account, int money) {
		this.account = account;
		this.money = money;
	}
	
	@Override
	public void run() {
		account.drawing(money);
		
	}
}

运行结果:

在这里插入图片描述


在普通方法上加同步锁synchronized,锁的是整个对象,不是某一个方法

举例:

package day15;

public class Test2 {
	public static void main(String[] args) {
		
		//定义账户对象
		Account a = new Account();
		
		//多线程对象
		User u_weixin = new User(a, 2000);
		User u_zhifubao = new User(a, 2000);
		
		Thread weixin = new Thread(u_weixin, "微信");
		Thread zhifubao = new Thread(u_zhifubao, "支付宝");
		
		weixin.start();
		zhifubao.start();
		
		
	}

}

class Account{
	public static int money = 3000;//全局变量,所有操作共享这个变量
	
	/**
	 * 提款先判断账户钱够不够
	 * 多线程调用这个方法就有问题,线程共享资源时,一个线程在执行这个方法没有完毕时,另一个线程又开始执行这个方法
	 * 解决思路:先让一个线程整体执行完这个方法,另一个线程再执行。
	 * 通过 synchronized同步锁 来完成
	 * 可以直接在方法上加上synchronized关键字
	 * 在普通方法上加同步锁synchronized,锁的是整个对象,不是某一个方法
	 * 
	 * 普通方法加同步锁,锁的是当前方法对应的对象,当前的对象的所有加了同步锁的方法是共用一个同步锁
	 * @param m
	 */
	public synchronized void drawing(int m) {
		String name = Thread.currentThread().getName();
		
		if(money < m) {
			System.out.println(name + "操作,账户金额不足:" + money);
		}else {
			System.out.println(name + "操作,账户原有金额:" + money);
			System.out.println(name + "操作,取款金额:" + m);
			
			money = money - m;
			
			System.out.println(name + "操作,取款后的余额:" + money);
		}
		
	}
	
	public synchronized void drawing1(int m) {
		String name = Thread.currentThread().getName();
		
		if(money < m) {
			System.out.println(name + "操作,账户金额不足:" + money);
		}else {
			System.out.println(name + "操作,账户原有金额:" + money);
			System.out.println(name + "操作,取款金额:" + m);
			
			money = money - m;
			
			System.out.println(name + "操作,取款后的余额:" + money);
		}
		
	}
	
	
}

class User implements Runnable{
	
	Account account;
	int money;
	
	public User(Account account, int money) {
		this.account = account;
		this.money = money;
	}
	
	@Override
	public void run() {

		if(Thread.currentThread().getName().equals("微信")) {
			account.drawing(money);
		}else {
			account.drawing1(money);
		}
		
	}
}

运行结果:

在这里插入图片描述

运行结果和上一段相同
说明:锁死的是 Account a 这个对象

普通方法加同步锁,锁的是当前方法对应的对象,当前的对象的所有加了同步锁的方法是共用一个同步锁


不同的对象是不同的锁

举例:

package day15;

public class Test2 {
	public static void main(String[] args) {
		
		//定义账户对象
		Account a = new Account();
		Account a1 = new Account();
		
		//多线程对象
		User u_weixin = new User(a, 2000);
		User u_zhifubao = new User(a1, 2000);
		
		Thread weixin = new Thread(u_weixin, "微信");
		Thread zhifubao = new Thread(u_zhifubao, "支付宝");
		
		weixin.start();
		zhifubao.start();
		
		
	}

}

class Account{
	public static int money = 3000;//全局变量,所有操作共享这个变量
	
	/**
	 * 提款先判断账户钱够不够
	 * 多线程调用这个方法就有问题,线程共享资源时,一个线程在执行这个方法没有完毕时,另一个线程又开始执行这个方法
	 * 解决思路:先让一个线程整体执行完这个方法,另一个线程再执行。
	 * 通过 synchronized同步锁 来完成
	 * 可以直接在方法上加上synchronized关键字
	 * 在普通方法上加同步锁synchronized,锁的是整个对象,不是某一个方法,
	 * 不同的对象是不同的锁,普通方法中加synchronized,线程使用不同的此方法的对象,还是有资源共享的问题
	 * @param m
	 */
	public synchronized void drawing(int m) {
		String name = Thread.currentThread().getName();
		
		if(money < m) {
			System.out.println(name + "操作,账户金额不足:" + money);
		}else {
			System.out.println(name + "操作,账户原有金额:" + money);
			System.out.println(name + "操作,取款金额:" + m);
			
			money = money - m;
			
			System.out.println(name + "操作,取款后的余额:" + money);
		}
		
	}
	
	public synchronized void drawing1(int m) {
		String name = Thread.currentThread().getName();
		
		if(money < m) {
			System.out.println(name + "操作,账户金额不足:" + money);
		}else {
			System.out.println(name + "操作,账户原有金额:" + money);
			System.out.println(name + "操作,取款金额:" + m);
			
			money = money - m;
			
			System.out.println(name + "操作,取款后的余额:" + money);
		}
		
	}
	
	
}

class User implements Runnable{
	
	Account account;
	int money;
	
	public User(Account account, int money) {
		this.account = account;
		this.money = money;
	}
	
	@Override
	public void run() {
	
		if(Thread.currentThread().getName().equals("微信")) {
			account.drawing(money);
		}else {
			account.drawing1(money);
		}
		
	}
}

运行结果:

在这里插入图片描述


如何解决上述问题:

举例:

package day15;

public class Test2 {
	public static void main(String[] args) {
		
		//定义账户对象
		Account a = new Account();
		Account a1 = new Account();
		
		//多线程对象
		User u_weixin = new User(a, 2000);
		User u_zhifubao = new User(a1, 2000);
		
		Thread weixin = new Thread(u_weixin, "微信");
		Thread zhifubao = new Thread(u_zhifubao, "支付宝");
		
		weixin.start();
		zhifubao.start();
		
		
	}

}

class Account{
	public static int money = 3000;//全局变量,所有操作共享这个变量	
	
	/**
	 * 静态方法 + synchronized,对于所有的对象都是使用同一个锁
	 * @param m
	 */
	public static synchronized void drawing2(int m) {
		String name = Thread.currentThread().getName();
		
		if(money < m) {
			System.out.println(name + "操作,账户金额不足:" + money);
		}else {
			System.out.println(name + "操作,账户原有金额:" + money);
			System.out.println(name + "操作,取款金额:" + m);
			
			money = money - m;
			
			System.out.println(name + "操作,取款后的余额:" + money);
		}
		
	}
}

class User implements Runnable{
	
	Account account;
	int money;
	
	public User(Account account, int money) {
		this.account = account;
		this.money = money;
	}
	
	@Override
	public void run() {

		account.drawing2(money);//调用类的静态方法
	}
}

运行结果:

在这里插入图片描述


锁代码块

举例:

package day15;

public class Test2 {
	public static void main(String[] args) {
		
		//定义账户对象
		Account a = new Account();
		Account a1 = new Account();
		
		//多线程对象
		User u_weixin = new User(a, 2000);
		User u_zhifubao = new User(a, 2000);
		
		Thread weixin = new Thread(u_weixin, "微信");
		Thread zhifubao = new Thread(u_zhifubao, "支付宝");
		
		weixin.start();
		zhifubao.start();	
		
	}
}

class Account{
	public static int money = 3000;//全局变量,所有操作共享这个变量
				
	/**
	 * 对代码块加入同步锁
	 * 代码块synchronized(this),所有当前的对象的synchronized(this)同步的代码都是使用同一个锁
	 * @param m
	 */
	public void drawing3(int m) {
		
		synchronized(this) {//表示当前对象的代码块被加了synchronized同步锁
			//用this锁代码块是代表当前的对象,如果在其他的方法中也有synchronized(this)的代码块,使用的都是同一个同步锁
			String name = Thread.currentThread().getName();
			
			if(money < m) {
				System.out.println(name + "操作,账户金额不足:" + money);
			}else {
				System.out.println(name + "操作,账户原有金额:" + money);
				System.out.println(name + "操作,取款金额:" + m);
				
				money = money - m;
				
				System.out.println(name + "操作,取款后的余额:" + money);
			}
		}
	}
	
}

class User implements Runnable{
	
	Account account;
	int money;
	
	public User(Account account, int money) {
		this.account = account;
		this.money = money;
	}
	
	@Override
	public void run() {

		account.drawing3(money);
	}
}

运行结果:

在这里插入图片描述


如果在其他的方法中也有synchronized(this)的代码块,使用的都是同一个同步锁

举例:

package day15;

public class Test2 {
	public static void main(String[] args) {
		
		//定义账户对象
		Account a = new Account();
		
		//多线程对象
		User u_weixin = new User(a, 2000);
		User u_zhifubao = new User(a, 2000);
		
		Thread weixin = new Thread(u_weixin, "微信");
		Thread zhifubao = new Thread(u_zhifubao, "支付宝");
		
		weixin.start();
		zhifubao.start();
	}
}

class Account{
	public static int money = 3000;//全局变量,所有操作共享这个变量
	
	/**
	 * 对代码块加入同步锁
	 * @param m
	 */
	public void drawing3(int m) {
		
		synchronized(this) {//表示当前对象的代码块被加了synchronized同步锁
			//用this锁代码块是代表当前的对象,如果在其他的方法中也有synchronized(this)的代码块,使用的都是同一个同步锁
			String name = Thread.currentThread().getName();
			
			if(money < m) {
				System.out.println(name + "操作,账户金额不足:" + money);
			}else {
				System.out.println(name + "操作,账户原有金额:" + money);
				System.out.println(name + "操作,取款金额:" + m);
				
				money = money - m;
				
				System.out.println(name + "操作,取款后的余额:" + money);
			}
		}
	}
	
	
	public void drawing4(int m) {
		
		synchronized(this) {//表示当前对象的代码块被加了synchronized同步锁
			//用this锁代码块是代表当前的对象,如果在其他的方法中也有synchronized(this)的代码块,使用的都是同一个同步锁
			String name = Thread.currentThread().getName();
			
			if(money < m) {
				System.out.println(name + "操作,账户金额不足:" + money);
			}else {
				System.out.println(name + "操作,账户原有金额:" + money);
				System.out.println(name + "操作,取款金额:" + m);
				
				money = money - m;
				
				System.out.println(name + "操作,取款后的余额:" + money);
			}
		}
	}

}

class User implements Runnable{
	
	Account account;
	int money;
	
	public User(Account account, int money) {
		this.account = account;
		this.money = money;
	}
	
	@Override
	public void run() {

		if(Thread.currentThread().getName().equals("微信")) {
			account.drawing3(money);
		}else {
			account.drawing4(money);
		}
		
		
	}
}

运行结果:

在这里插入图片描述


怎么使用不同的同步锁

举例:
(1)锁同一个对象,设置a、a1两个账户

package day15;

public class Test2 {
	public static void main(String[] args) {
		
		//定义账户对象
		Account a = new Account();
		Account a1 = new Account();
		
		//多线程对象
		User u_weixin = new User(a, 2000);
		User u_zhifubao = new User(a1, 2000);
		
		Thread weixin = new Thread(u_weixin, "微信");
		Thread zhifubao = new Thread(u_zhifubao, "支付宝");
		
		weixin.start();
		zhifubao.start();
		
		
	}

}

class Account{
	public static int money = 3000;//全局变量,所有操作共享这个变量
	
	/**
	 * synchronized修饰代码块,想要根据不同的对象有不同的锁
	 * @param m
	 */
	public void drawing5(int m, Account a) {
		
		synchronized(a) {//表示通过方法的参数传递进来的对象的代码块被加了synchronized同步锁
			//不同的对象就有不同的同步锁
			String name = Thread.currentThread().getName();
			
			if(money < m) {
				System.out.println(name + "操作,账户金额不足:" + money);
			}else {
				System.out.println(name + "操作,账户原有金额:" + money);
				System.out.println(name + "操作,取款金额:" + m);
				
				money = money - m;
				
				System.out.println(name + "操作,取款后的余额:" + money);
			}
		}
	}
	
}

class User implements Runnable{
	
	Account account;
	int money;
	
	public User(Account account, int money) {
		this.account = account;
		this.money = money;
	}
	
	@Override
	public void run() {
		
		account.drawing5(money, account);	
	}
}

运行结果:

在这里插入图片描述


(2)锁同一个对象,只设置a一个账户

package day15;

public class Test2 {
	public static void main(String[] args) {
		
		//定义账户对象
		Account a = new Account();
		
		//多线程对象
		User u_weixin = new User(a, 2000);
		User u_zhifubao = new User(a, 2000);
		
		Thread weixin = new Thread(u_weixin, "微信");
		Thread zhifubao = new Thread(u_zhifubao, "支付宝");
		
		weixin.start();
		zhifubao.start();
		
	}
}

class Account{
	public static int money = 3000;//全局变量,所有操作共享这个变量

	/**
	 * synchronized修饰代码块,想要根据不同的对象有不同的锁
	 * synchronized(a)这个小括号中传入不同的对象就是不同的锁
	 * @param m
	 */
	public void drawing5(int m, Account a) {
		
		synchronized(a) {//表示通过方法的参数传递进来的对象的代码块被加了synchronized同步锁
			//不同的对象就有不同的同步锁
			String name = Thread.currentThread().getName();
			
			if(money < m) {
				System.out.println(name + "操作,账户金额不足:" + money);
			}else {
				System.out.println(name + "操作,账户原有金额:" + money);
				System.out.println(name + "操作,取款金额:" + m);
				
				money = money - m;
				
				System.out.println(name + "操作,取款后的余额:" + money);
			}
		}
	}
	
	
	
}

class User implements Runnable{
	
	Account account;
	int money;
	
	public User(Account account, int money) {
		this.account = account;
		this.money = money;
	}
	
	@Override
	public void run() {
		
		account.drawing5(money, account);
	}
}

运行结果:

在这里插入图片描述

3. 线程的死锁问题

问题情景:
    线程a0,需要执行方法f0
    线程a1,需要执行方法f1
    f0和f1都是有同步锁的方法
    a0调用f1方法并且一直没有执行完f1
    a1调用f0方法并且一直没有执行完f0
    
导致a0和a1线程都在等待对方释放方法,对方都不释放,这样就形成了线程的死锁

死锁
    不同的线程分别占用对方需要的同步资源不放弃,都在等待对方放弃自己需要的同步资源,就形成了线程的死锁

解决方法
    ①专门的算法、原则,比如加锁顺序一致
    ②尽量减少同步资源的定义,尽量避免锁未释放的场景

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Cpdr

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值