synchronized、wait、notify、notifyAll

因为同一个进程的线程之间可以共享同一个进程空间,所以实现线程的数据共享是很容易的(当然在并发中实现数据结构的一致性则另当别论),也可以使用管道流来进行线程通信(没必要)。此处只说协调控制机制

synchronized、wait、notify、notifyAll

使用Java语言内置关键字synchronized来进行加锁,可以保证任务执行的可见性和原子性,从而实现同步执行。synchronized关键字使用的是对象锁,锁的实现依附于对象,避免了锁的创建,所以接下来就直接默认synchronized就是对对象加锁,便于理解。使用方式上,可以显式对对象加锁synchronized(obj){},也可以隐式对当前对象加锁,就是所谓的同步方法或者synchronized(this){}代码块。这里多说一句,因为synchronized是依附对象的加锁策略,所以在普通方法上加synchronized修饰,加锁范围是当前类的实例,对静态方法上加synchronized修饰,加锁范围为Class类实例,两者属于不同对象,所以不存在竞争问题。

<span style="font-family:FangSong_GB2312;"><span style="font-size:18px;">public class t{
	public static void main(String[] args){
		final test te=new test();
		new Thread(){
			public void run(){
				synchronized(test.class){
					System.out.println("subthread1:"+Thread.currentThread().getId()+"start");
					try{
						Thread.sleep(3000);
					}catch(Exception e){
						e.printStackTrace();
					}
					System.out.println("subthread1:"+Thread.currentThread().getId()+"end");
				}
			}
		}.start();
		
		try{
			Thread.sleep(100);
		}catch(Exception e){
			e.printStackTrace();
		}
		te.plain_meth();
		te.staic_meth();
	}
}
class test{
	public synchronized static void staic_meth(){
		System.out.println("main staic_meth:"+Thread.currentThread().getId());
	}
	public synchronized void plain_meth(){
		System.out.println("main plain_meth:"+Thread.currentThread().getId());
	}
}</span></span>
执行结果

<span style="font-family:FangSong_GB2312;"><span style="font-size:18px;">subthread1:8start
main plain_meth:1
subthread1:8end
main staic_meth:1</span></span>


调用对象的wait方法,使当前线程进入阻塞状态,直到被notify、notifyAll唤醒。notify、notifyAll方法属于大范围无差别唤醒策略,当前进程空间内的所有调用wait进入阻塞状态的线程都可能响应,没有针对性。

生产者消费者示例

<span style="font-family:FangSong_GB2312;"><span style="font-size:18px;">public class t{
	private static final int CAPACITY = 3;
	public static void main(String[] args){
		Buff buf=new Buff(CAPACITY);
		for(int i=0;i<10;i++){
			new Producer("Producer "+i,buf).start();
			new Consumer("Consumer "+i,buf).start();
		}
	}
}
class Buff{
	private final Object[] obj;
	private int index=0;
	public Buff(int CAPACITY){
		obj=new Object[CAPACITY];
	}
	public void put(Object o){
		obj[index++]=o;
	}
	public Object take(){
		return obj[--index];
	}
	public boolean isEmpty(){
		return index==0;
	}
	public boolean isFull(){
		return obj.length==index;
	}
	public int getContent(){
		return index;
	}
}
class Producer extends Thread{
	private Buff buf;
	public Producer(String name,Buff buf){
		super(name);
		this.buf=buf;
	}
	public void run(){
		produce();
	}
	private void produce(){
		synchronized(buf){
			while(buf.isFull()){
				try{
					buf.wait();
				}catch(InterruptedException e){
					e.printStackTrace();
				}
			}
			buf.put(new Object());
			System.out.println(Thread.currentThread().getName()+" put a product");
			System.out.println("concurrent buf content: "+buf.getContent());
			buf.notify();
		}
	}
}
class Consumer extends Thread{
	private Buff buf;
	public Consumer(String name,Buff buf){
		super(name);
		this.buf=buf;
	}
	public void run(){
		consume();
	}
	private void consume(){
		synchronized(buf){
			while(buf.isEmpty()){
				try{
					buf.wait();
				}catch(InterruptedException e){
					e.printStackTrace();
				}
			}
			buf.take();
			System.out.println(Thread.currentThread().getName()+" take a product");
			System.out.println("concurrent buf content: "+buf.getContent());
			buf.notify();
		}
	}
}
</span></span>


这里说一下,生产者线程判断buf不满使用的检查条件是while(buf.isFull()){},这里使用while循环而不是if判断。因为当某一个消费者线程con调用了notify后,会有某个因为wait而阻塞的生产者线程pro被唤醒,但是此时con仍然占据锁,需要等con代码块执行完毕,释放锁后,pro获得锁开始执行,如果con脑袋秀逗了,notify后,加了一句buf.put(new Object()),则pro在if语句的wait醒来后,直接向下执行,可能抛出越界异常。

总结

Synchronized、wait、notify、notifyAll可以实现一定程度的线程协调作用,但是在灵活性方面做的不好,引入Lock、Condition可以在一方面弥补这些缺陷,当然API层次的locks包也有不足。



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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值