【java】线程同步(synchronized,wait,notify,notifyAll)

synchronized:

包括synchronized方法和synchronized块。

synchronized方法使用this作为默认的“同步监视器”,而synchronized块则需自行指定。

wait、notify、notifyAll必须在同步方法或块中,否则会抛出异常

Object.wait:

要执行obj.wait,当前线程必须拥有obj的监视器(monitor)

执行后会导致当前线程进入等待,直到其它线程调用obj.notify或obj.notifyAll

执行后当前线程会放弃在obj上的所有同步请求(synchronization claims)

注意只是使当前线程处于等待,但对同步方法/块的占有却释放了,所以其它线程便可以随后访问这个同步方法/块了

Object.notify,Object.notifyAll:

要执行obj.notify、obj.notifyAll,当前线程必须拥有obj的监视器(monitor)

obj.notify会随机唤醒一个等待在obj上的线程,而obj.notifyAll却会唤醒所有等待在obj上的线程

被唤醒的线程不能马上执行,直到当前线程放弃obj的同步锁(The awakened threads will not be able to proceed until the current thread relinquishes the lock on this object. ),也就是当前线程离开了同步方法/块后


实战一下吧:创建三个线程(a线程打印字符a,b线程打印字符b,c线程打印字符c),使三个线程协作打印出abcabcabc。

class Printer extends Thread {
	private String chr;
	private Object pre, curr;
	
	public Printer(Object pre, Object curr, String chr) {
		super();
		this.pre = pre;
		this.curr = curr;
		this.chr = chr;
	}
	
	@Override
	public void run() {
		for(int i = 0; i < 3; i++) {
			synchronized(pre) {
				synchronized(curr) {
					System.out.print(chr);
					curr.notify();
				}
				
				try {
					pre.wait();
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			}
		}

		System.out.print("\n"+chr+"end");
	}
}

public class Test {
	
	public static void main(String[] args) throws InterruptedException {
		Object
			a = new Object(),
			b = new Object(),
			c = new Object();
		
		Printer
			printerA = new Printer(c, a, "a"),
			printerB = new Printer(a, b, "b"),
			printerC = new Printer(b, c, "c");
		
		printerA.start();
		Thread.sleep(2000);
		printerB.start();
		Thread.sleep(2000);
		printerC.start();
	}
}

这个例子你可能看到过,是的,我也是网上发现的。但是你发现没,最终结果是:

abcabc
aend

bend(未出现)

cend(未出现)

而且程序并没有退出!为什么?

为简化问题,我们不妨把for循环去掉,原理是一样的

	@Override
	public void run() {
		synchronized(pre) {
			synchronized(curr) {
				System.out.print(chr);
				curr.notify();
			}
			
			try {
				pre.wait();
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}

		System.out.print("\n"+chr+"end");
	}

a线程执行后打印“a”,唤醒等待在a上的线程(此时其实并没有其它线程在a上等待)并在pre.wait处等待

b线程执行后打印“b”,唤醒等待在b上的线程(此时其实并没有其它线程在b上等待)并在pre.wait处等待

c线程执行后打印“c”,唤醒等待在c上的线程并在pre.wait处等待。请注意,等待在c上的线程被唤醒了,因此a线程继续往下执行,打印出“aend”后a线程结束。但问题是b,c线程呢?他们并没有被唤醒,因此它们始终在pre.wait处等待着!


因此我们必须为b,c设置一个出口:当i=2(其实是循环次数-1即3-1=2)的时候不执行pre.wait

class Printer extends Thread {
	private String chr;
	private Object pre, curr;
	
	public Printer(Object pre, Object curr, String chr) {
		super();
		this.pre = pre;
		this.curr = curr;
		this.chr = chr;
	}
	
	@Override
	public void run() {
		for(int i = 0; i < 3; i++) {
			synchronized(pre) {
				synchronized(curr) {
					System.out.print(chr);
					curr.notify();
				}
				
				if(i < 2) {
					try {
						pre.wait();
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
				}
			}
		}

		System.out.print("\n"+chr+"end");
	}
}

public class Test {
	
	public static void main(String[] args) throws InterruptedException {
		Object
			a = new Object(),
			b = new Object(),
			c = new Object();
		
		Printer
			printerA = new Printer(c, a, "a"),
			printerB = new Printer(a, b, "b"),
			printerC = new Printer(b, c, "c");
		
		printerA.start();
		Thread.sleep(2000);
		printerB.start();
		Thread.sleep(2000);
		printerC.start();
	}
}

当i=2时跳过pre.wait,程序继续执行。之后i=3,由于此时i已经不满足for循环的要求,所以循环结束,直接执行System.out.print("\n"+chr+"end"),之后线程退出。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值