java 多线程的同步控制

线程安全

  1. 互斥:许多线程在同一个共享数据上操作而互不干扰,这就要求同一时刻只能有一个线程访问该共享数据。
  2. 监视区:同一时刻只能被同一个线程执行的程序段。
  3. 关键字:synchronized:用于指定监视去
    语法规定
    在这里插入图片描述
    实现原理:首先判断对象的锁是否存在。
    如果在,就获得锁,然后开始执行紧随其后的代码段。
    如果对象的锁不存在(可能被其他线程拿走了),就开始等待,直到获得锁。
    当被synchronized限定的 代码段执行完毕,就会释放锁。

用多线程实现简单的存票与售票

//主方法
public class ProducerAndConsumer {
	public static void main(String[] args) {
		System.out.println("主线程开始执行");
		Ticket ticket = new Ticket(10);
		new Consumer(ticket).start();
		new Producer(ticket).start();
		System.out.println("主线程执行完毕");
	}

}

//票类
public class Ticket {
	//票号
	private int _number = 0;
	//总票数
	private int _size;
	//当前是否有票可卖
	boolean isAvailable = false;
	
	//构造方法
	public Ticket(int size) {
		// TODO Auto-generated constructor stub
		_size = size;
	}
	
	//获取票号
	public int getNumber()
	{
		return _number;
	}
	
	//获取票的总数
	public int getSize()
	{
		return _size;
	}
	
	//生产票
	void produceTicket()
	{
		_number++;
	}
}


//存票线程
public class Producer extends Thread {
	//票
	private Ticket _t = null;
	
	//构造方法
	public Producer(Ticket t) {
		// TODO Auto-generated constructor stub
		_t = t;
	}
	
	//线程体
	@Override
	public void run() {
		// TODO Auto-generated method stub
		while(_t.getNumber() < _t.getSize())
		{
			_t.produceTicket();
			System.out.println("Producer puts tickets " + _t.getNumber());
			_t.isAvailable = true;
		}
	}
}

//售票线程

public class Consumer extends Thread {
	Ticket _t = null;
	int i = 0;
	public Consumer(Ticket t) {
		// TODO Auto-generated constructor stub
		_t = t;
	}
	
	@Override
	public void run() {
		// TODO Auto-generated method stub
		while(_t.getSize() > i)
		{
			if(_t.isAvailable && i < _t.getNumber())
			{
				i++;
				System.out.println("Consumer buys ticket " + i);
				if(i == _t.getNumber())
				{
					try
					{
						Thread.sleep(1);
					}
					catch(Exception e)
					{
						System.out.println(e.getClass().getName());
					}
					_t.isAvailable = false;
				}
			}
		}
	}
}

上述程序会出现错误:这是因为当售票线程的已经售出的票等于当前最大票号的时候,售票线程就是休眠1ms。此时java的线程调度器开始执行存票线程,并一次性生产完了10张票,且将**_t.isAvailable设置为true。1ms以后,售票线程休眠完毕,将_t.isAvailable设置为false。这就让售票线程陷入了死循环,因为存票线程已经执行完毕,_t.isAvailable** 的状态只能永远是false。下面用关键字synchronized简单的改造一下该程序。

//主方法
public class ProducerAndConsumer {
	public static void main(String[] args) {
		System.out.println("主线程开始执行");
		Ticket ticket = new Ticket(10);
		new Consumer(ticket).start();
		new Producer(ticket).start();
		System.out.println("主线程执行完毕");
	}
}

//售票线程

public class Producer extends Thread {
	//票
	private Ticket _t = null;
	
	//构造方法
	public Producer(Ticket t) {
		// TODO Auto-generated constructor stub
		_t = t;
	}
	
	//线程体
	@Override
	public void run() {
		// TODO Auto-generated method stub
		while(_t.getNumber() < _t.getSize())
		{
			synchronized (_t) {
				_t.produceTicket();
				System.out.println("Producer puts tickets " + _t.getNumber());
				_t.isAvailable = true;
			}
		}
	}
}

//存票线程
public class Producer extends Thread {
	//票
	private Ticket _t = null;
	
	//构造方法
	public Producer(Ticket t) {
		// TODO Auto-generated constructor stub
		_t = t;
	}
	
	//线程体
	@Override
	public void run() {
		// TODO Auto-generated method stub
		while(_t.getNumber() < _t.getSize())
		{
			synchronized (_t) {
				_t.produceTicket();
				System.out.println("Producer puts tickets " + _t.getNumber());
				_t.isAvailable = true;
			}
		}
	}
}

//票类
public class Ticket {
	//票号
	private int _number = 0;
	//总票数
	private int _size;
	//当前是否有票可卖
	boolean isAvailable = false;
	
	//构造方法
	public Ticket(int size) {
		// TODO Auto-generated constructor stub
		_size = size;
	}
	
	//获取票号
	public int getNumber()
	{
		return _number;
	}
	
	//获取票的总数
	public int getSize()
	{
		return _size;
	}
	
	//生产票
	void produceTicket()
	{
		_number++;
	}
}

其改动主要就是将对象t设置了一个锁,让存票线程和售票线程不能同时访问之。
synchronized的本质就是让被他修饰的代码段变成一个“原子语句”,也就是说这些代码要么不被执行,要么就会被执行完才会允许其他线程操作该对象。
synchronized可以定义某些方法在同步控制下执行,只要在方法前面加上synchronized即可。
采用同步方法的方式修改上述代码实现同样的功能。其改动主要就是将售票方法和买票方法的实现写在了Ticket类中,并将其设置为同步方法。其实这样实现是更加合理的方式。

//主方法
public class ProducerAndContums {
	public static void main(String[] args) {
		System.out.println("主方法开始执行");
		Ticket ticket = new Ticket(10);
		Producer producer = new Producer(ticket);
		Contums contums = new Contums(ticket);
		producer.start();
		contums.start();
		System.out.println("主方法执行完毕");
	}
}

//票类
public class Ticket {
	//存票序号
	private int _number = 0;
	//票总数
	private int _size;
	//当前是否有票可售
	private boolean _isAvailiable = false;
	//售票号
	int _i;
	
	//构造方法
	public Ticket(int size) {
		// TODO Auto-generated constructor stub
		_size = size;
	}
	
	//获取存票号
	public int getNumber()
	{
		return _number;
	}
	
	//获取售票号
	public int getI() {
		return _i;
	}
	
	//获取最多生产多少票
	public int getSize()
	{
		return _size;
	}
	
	//获取当前是否有票
	public boolean getAvailable()
	{
		return _isAvailiable;
	}
	
	//存票方法
	public synchronized void put() {
		if(_number < _size)
		{
			_number++;
			System.out.println("Producer puts ticket " + _number);
			_isAvailiable = true;
		}
	}
	
	//售票方法
	public synchronized void sell() {
		if(_isAvailiable && _i < _number)
		{
			_i++;
			System.out.println("Customer buys ticket " + _i);
//			System.out.println("number = " + _number);
		}
		if(_i == _number)
		{
			_isAvailiable = false;
		}
	}
}

//存票线程
public class Producer extends Thread {
	private Ticket _t;
	//构造方法
	public Producer(Ticket t) {
		// TODO Auto-generated constructor stub
		_t = t;
	}
	//线程体
	@Override
	public void run() {
		// TODO Auto-generated method stub
		while(_t.getNumber() < _t.getSize())
		{
			_t.put();
		}
	}
}

//售票线程
public class Contums extends Thread{
	private Ticket _t;
	public Contums(Ticket t) {
		// TODO Auto-generated constructor stub
		_t = t;
	}
	
	@Override
	public void run() {
		// TODO Auto-generated method stub
		while(_t.getI() < _t.getSize())
		{
			_t.sell();
		}
	}
}

synchronized用法总结

  1. 该关键字可以实现线程同步,可以同步代码段,也可以同步方法。
  2. 类可以拥有同步和非同步方法,非同步方法的调用不收限制。
  3. 如果两个线程使用相同的实例来调用同步方法,那么同一时刻只能有一个线程执行方法,另一个需要等待锁。
  4. 可以给对象上锁,但是每个对象只有一个锁。
  5. 线程休眠时,它所持有的锁不会释放。
  6. 线程可以获得多个锁:比如在一个对象的同步方法中调用另一个对象的同步方法。
  7. 同步的缺点:损害并发性,应该在保证线程安全的情况下,尽可能缩小同步方法的范围,进而提高并发性。
  8. 在使用同步代码块的时候,应该说明要获取哪个对象的锁。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值