java多线程之synchronized

首先来看下一个场景,某电影院某个时间4个窗口同时在卖票,本场电影总共票只有100张,卖完为止。看下实际代码。

package cn.com.thread;

public class TestThread {
	public static void main(String[] args) {
		SellTicketThread t=new SellTicketThread();
		new Thread(t,"窗口1").start();
		new Thread(t,"窗口2").start();
		new Thread(t,"窗口3").start();
		new Thread(t,"窗口4").start();
	}
}
package cn.com.thread;

public class SellTicketThread extends Thread {
	private int ticket = 100;

	@Override
	public void run() {
		while (true) {
			if(sellTicket()){
				break;
			}
		}
	}

	private boolean sellTicket() {
		try {
			if (ticket <= 0) {
				return true;
			}
			Thread.sleep(30);
		} catch (Exception e) {
		}
		System.out.println(Thread.currentThread().getName()+":"+(ticket--));
		return false;
	}
}

从运行结果来看,发现出现了负数。在实际业务中是不可以的。

从而我们得出一个结论:多个线程访问一个对象中的实例变量,可能会出现`非线程安全`。


synchronized方法


如果在方法上加关键字synchronized,那么就不会出现上面的那个问题。

package cn.com.thread;

public class SellTicketThread extends Thread {
	private int ticket = 100;

	@Override
	public void run() {
		while (true) {
			if(sellTicket()){
				break;
			}
		}
	}

	private synchronized boolean sellTicket() {
		try {
			if (ticket <= 0) {
				return true;
			}
			Thread.sleep(30);
		} catch (Exception e) {
		}
		System.out.println(Thread.currentThread().getName()+":"+(ticket--));
		return false;
	}
}

看到这里有些人就要问了,既然加了锁,我们的代码就只能被一个线程调用,这样岂不是降低了效率,在同步代码的部分并没有多线程并发的情况出现呀?如果你能想到这一点,就说明你对锁的机制了解的差不多了,的确,情况的确如此,因为我们要尽量的缩小同步锁的范围,有什么原则么?所以有时间同步方法并不适合。


synchronized代码块


假如在卖票之前,我们还要去做相关的事。比如我们在美团买了一张票,可能在电影院的系统中,需要去效验下,是不是你在美团买了票,在决定是否出票。这个过程相当耗时,而每个人都是单独的对象去访问,所以是线程安全,但是我们要是用synchronized方法,是不适合的。

package cn.com.thread;

public class SellTicketThread extends Thread {
	private int ticket = 100;
        private Object lock=new Object();
	@Override
	public void run() {
		while (true) {
			if (sellTicket()) {
				break;
			}
		}
	}

	private boolean sellTicket() {
		System.out.println("效验逻辑,耗时10秒");
		try {
			synchronized (lock) {
				if (ticket <= 0) {
					return true;
				}
				System.out.println(Thread.currentThread().getName() + ":"+ (ticket--));
			}
			Thread.sleep(30);
		} catch (Exception e) {
		}

		return false;
	}
}

从上面的例子我们可以看出,这样也是达到我们要的结果。那么我们不禁要问该如何定义一个锁?

  • 所谓加锁,就是为了防止多个线程同时操作一份数据,如果多个线程操作的数据都是各自的,那么就没有加锁的必要
  • 共享数据的锁对于访问他们的线程来说必须是同一份,否则锁只能私有的锁,各锁个的,起不到保护共享数据的目的,试想一下将 Object lock 的定义放到 run 方法里面,每次都会实例化一个 lock,每个线程获取的锁都是不一样的,也就没有争抢可言,说的在通俗一点甲楼有一个门上了锁,A 要进门,乙楼有一个门上了锁 B 要进门,A 和 B 抢的不是一个门,因此不存在数据保护或者共享;
  • 锁的定义可以是任意的一个对象,该对象可以不参与任何运算,只要保证在访问的多个线程看来他是唯一的即可;

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值