线程同步 synchronized

我们为什么需要synchronized?

  先谈谈并发:当同一个对象,被多个线程,同时访问时就是并发。

  大家都是一起操作并修改数据,特别是在读和写之间存在延时,就会导致的数据的混乱不准确。

  做了两年的财务开发,这次就拿取钱做案例吧:

public class Test {
	
	public static void main(String[] args) {
		//同一个对象
		WithdrawMoney wm = new WithdrawMoney();
		new Thread(wm, "小明").start(); 
		new Thread(wm, "小红").start(); //多个线程
	}
}

class WithdrawMoney implements Runnable {
	
	private Integer money = 1000;
	
	@Override
	public void run() {
		if (money > 900) {//想要取900
			try {
				if (Thread.currentThread().getName().equals("小明")) {//模拟小明存在网络延时,1秒
					Thread.sleep(1000);
				}
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
			money = money - 900;//取900
			System.out.println(Thread.currentThread().getName()+":现在还剩" + money);
		}else {
			System.out.println(Thread.currentThread().getName()+":余额不足");
		}
	}
}
--------------------------------------
输出结果为:
小红:现在还剩100
小明:现在还剩-800

  明显的,虽然小明执行if (Test.money > 0)时通过了,但一秒后进行扣款时,被小红先扣走了,导致小明再进行扣款,总金额为负数了。

  为了避免这种情况,保证数据的准确性与同步,我们可以使用synchronized来进行控制。

同步方法

  用synchronized修饰的方法,称为同步方法。

	public synchronized void method(int args) {}

  当多个线程使用同一个同步方法时,会进行排队等待,一个一个线程执行。

  注意,一定要是同一个同步方法,多个实例化对象的相同名字的同步方法,并不是同一个。

  使用同步方法改写取钱案例:

public class Test {
	
	public static void main(String[] args) {
		//同一个对象
		WithdrawMoney wm = new WithdrawMoney();
		new Thread(wm, "小明").start(); 
		new Thread(wm, "小红").start(); //多个线程
	}
}

class WithdrawMoney implements Runnable {
	
	private Integer money = 1000;
	
	@Override
	public void run() {
		withdraw();
	}
	
	synchronized void withdraw() {
		if (money > 900) {//想要取900
			try {
				if (Thread.currentThread().getName().equals("小明")) {//模拟小明存在网络延时,1秒
					Thread.sleep(1000);
				}
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
			money = money - 900;//取900
			System.out.println(Thread.currentThread().getName()+":现在还剩" + money);
		}else {
			System.out.println(Thread.currentThread().getName()+":余额不足");
		}
	}
}
-------------------------------
小明:现在还剩100
小红:余额不足

  虽然小明存在延时,但小红得等小明全部执行完后,才能继续执行。

同步块

  用synchronized修饰的代码块,称为同步块。

	synchronized (obj){ } // obj称之为同步监视器

  同步块中的obj称为同步监视器,obj可以是任何对象,但是推荐使用共享资源作为同步监视

  使用同步块改写取钱案例:

public class Test {

	public static void main(String[] args) {
		// 同一个对象
		WithdrawMoney wm = new WithdrawMoney();
		new Thread(wm, "小明").start();
		new Thread(wm, "小红").start(); // 多个线程
	}
}

class WithdrawMoney implements Runnable {

	private Integer money = 1000;

	@Override
	public void run() {
		synchronized (this) {//this表示同一个的实例化对象wm,此处也可以改为锁money
			if (money > 900) {// 想要取900
				try {
					if (Thread.currentThread().getName().equals("小明")) {// 模拟小明存在网络延时,1秒
						Thread.sleep(1000);
					}
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
				money = money - 900;// 取900
				System.out.println(Thread.currentThread().getName() + ":现在还剩" + money);
			} else {
				System.out.println(Thread.currentThread().getName() + ":余额不足");
			}
		}
	}
}
-------------------------------
小明:现在还剩100
小红:余额不足

  在同步块中是锁对象还是锁成员变量,取决于具体的场景,但务必要保证可以锁住。在能锁住的情况下,锁的范围越小,自然是对其他线程影响越小,效率越高越好的。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值