Java synchronized线程同步锁及线程间通过对象的wait,notify进行交互

1.synchronized关键字
    synchronized是用来控制线程的并发执行的,它只能作用于一个方法或者一个代码块上,通过它能保证一段代码或一个方法有多个线程调用时能顺序执行。
    工作机制:
    当程序运行到非静态的synchronized同步方法上时,自动获得与正在执行代码类的当前实例(this实例)有关的锁。获得一个对象的锁也称为获取锁、锁定对象、在对象上锁定或在对象上同步。
    当程序运行到synchronized同步方法或代码块时才该对象锁才起作用。一个对象只有一个锁。所以,如果一个线程获得该锁,就没有其他线程可以获得锁,直到第一个线程释放(或返回)锁。这也意味着任何其他线程都不能进入该对象上的synchronized方法或代码块,直到该锁被释放。释放锁是指持锁线程退出了synchronized同步方法或代码块。
示例代码:
package com.ajita;

public class TestSynchronized {

	class Account {
		private int total = 0;

		public synchronized void add(int n) {
			total = total + n;
		}

		public synchronized void minus(int n) {
			total = total - n;
		}

		public int getTotal() {
			return total;
		}
	}

	class AddThread extends Thread {
		private Account acct;
		private int addCount = 0;

		AddThread(String name, Account acct, int addNum) {
			super(name);
			this.acct = acct;
			this.addCount = addNum;
		}

		public void run() {
			for (int i = 0; i < addCount; i++) {
				acct.add(1);
				System.out.println(this.getName() + acct.getTotal());
				try {
					Thread.sleep(11);
				} catch (InterruptedException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}
		}
	}

	class MinusThread extends Thread {
		private Account acct;
		private int minusNum = 0;

		MinusThread(String name, Account acct, int minusNum) {
			super(name);
			this.acct = acct;
			this.minusNum = minusNum;
		}

		public void run() {
			for (int i = 0; i < minusNum; i++) {
				acct.minus(1);
				System.out.println(this.getName() + acct.getTotal());
				try {
					Thread.sleep(10);
				} catch (InterruptedException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}
		}
	}

	public void test() {
		Account a = new Account();
		System.out.println(a.getTotal());
		Thread addThread = new AddThread("add", a, 50);
		Thread minusThread = new MinusThread("minus", a, 50);
		addThread.start();
		minusThread.start();
		try {
			addThread.join();
			minusThread.join();
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		System.out.println(a.getTotal());
	}

	/**
	 * @param args
	 */
	public static void main(String[] args) {
		TestSynchronized test = new TestSynchronized();
		test.test();
	}
}


使用synchronized锁,可能造成死锁的问题。最典型的是线程1先锁A,再来锁B,线程2先锁B,再来锁A,如下代码:
package com.ajita;

public class TestDeadLock {
	public class DeadlockRisk {

		private Object resourceA = new Object();
		private Object resourceB = new Object();

		public void lockAFirst() {
			synchronized (resourceA) {
				try {
					Thread.sleep(1000);
				} catch (InterruptedException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
				synchronized (resourceB) {
					System.out.println("lockAFirst get Locked");
				}
			}
		}

		public void lockBFirst() {
			synchronized (resourceB) {
				try {
					Thread.sleep(1000);
				} catch (InterruptedException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
				synchronized (resourceA) {
					System.out.println("lockBFirst get Locked");
				}
			}
		}
	}

	class LockAFirstThread extends Thread {
		public DeadlockRisk deadLock;

		public void run() {
			deadLock.lockAFirst();
		}
	}

	class LockBFirstThread extends Thread {
		public DeadlockRisk deadLock;

		public void run() {
			deadLock.lockBFirst();
		}
	}

	public void test() {
		DeadlockRisk lock = new DeadlockRisk();
		LockAFirstThread a = new LockAFirstThread();
		a.deadLock = lock;
		LockBFirstThread b = new LockBFirstThread();
		b.deadLock = lock;
		a.start();
		b.start();
	}

	/**
	 * @param args
	 */
	public static void main(String[] args) {
		TestDeadLock test = new TestDeadLock();
		test.test();
	}
}


2.wait,notify,notifyAll
    任意Java对象都有如下3个方法,不同线程之间可以利用同一个对象的这三个方法进行通信。
    关于等待/通知,要记住的关键点是:
必须从同步环境内(就是synchronized 代码块内)调用wait()、notify()、notifyAll()方法。线程不能调用对象上等待或通知的方法,除非它拥有那个对象的锁。
void notify()
          唤醒在此对象监视器上等待的单个线程。 有重载的。
void notifyAll()
          唤醒在此对象监视器上等待的所有线程。
void wait()
          导致当前的线程等待,直到其他线程调用此对象的 notify() 方法或 notifyAll() 方法。
    最经典的例子就是生产者,消费者的例子。
代码如下,百度来的
package com.ajita;

/**
 * Java线程:并发协作-生产者消费者模型
 * 
 * @author leizhimin 2009-11-4 14:54:36
 */
public class ProducerAndConsumer {
	public static void main(String[] args) {
		Godown godown = new Godown(30);
		Consumer c1 = new Consumer(50, godown);
		Consumer c2 = new Consumer(20, godown);
		Consumer c3 = new Consumer(30, godown);
		Producer p1 = new Producer(10, godown);
		Producer p2 = new Producer(10, godown);
		Producer p3 = new Producer(10, godown);
		Producer p4 = new Producer(10, godown);
		Producer p5 = new Producer(10, godown);
		Producer p6 = new Producer(10, godown);
		Producer p7 = new Producer(80, godown);

		c1.start();
		c2.start();
		c3.start();
		p1.start();
		p2.start();
		p3.start();
		p4.start();
		p5.start();
		p6.start();
		p7.start();
	}
}

/**
 * 仓库
 */
class Godown {
	public static final int max_size = 100; // 最大库存量
	public int curnum; // 当前库存量

	Godown() {
	}

	Godown(int curnum) {
		this.curnum = curnum;
	}

	/**
	 * 生产指定数量的产品
	 * 
	 * @param neednum
	 */
	public synchronized void produce(int neednum) {
		// 测试是否需要生产
		while (neednum + curnum > max_size) {
			System.out.println("要生产的产品数量" + neednum + "超过剩余库存量"
					+ (max_size - curnum) + ",暂时不能执行生产任务!");
			try {
				// 当前的生产线程等待
				wait();
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
		// 满足生产条件,则进行生产,这里简单的更改当前库存量
		curnum += neednum;
		System.out.println("已经生产了" + neednum + "个产品,现仓储量为" + curnum);
		// 唤醒在此对象监视器上等待的所有线程
		notifyAll();
	}

	/**
	 * 消费指定数量的产品
	 * 
	 * @param neednum
	 */
	public synchronized void consume(int neednum) {
		// 测试是否可消费
		while (curnum < neednum) {
			try {
				// 当前的生产线程等待
				wait();
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
		// 满足消费条件,则进行消费,这里简单的更改当前库存量
		curnum -= neednum;
		System.out.println("已经消费了" + neednum + "个产品,现仓储量为" + curnum);
		// 唤醒在此对象监视器上等待的所有线程
		notifyAll();
	}
}

/**
 * 生产者
 */
class Producer extends Thread {
	private int neednum; // 生产产品的数量
	private Godown godown; // 仓库

	Producer(int neednum, Godown godown) {
		this.neednum = neednum;
		this.godown = godown;
	}

	public void run() {
		// 生产指定数量的产品
		godown.produce(neednum);
	}
}

/**
 * 消费者
 */
class Consumer extends Thread {
	private int neednum; // 生产产品的数量
	private Godown godown; // 仓库

	Consumer(int neednum, Godown godown) {
		this.neednum = neednum;
		this.godown = godown;
	}

	public void run() {
		// 消费指定数量的产品
		godown.consume(neednum);
	}
}

    本例仅仅是生产者消费者模型中最简单的一种表示,本例中,如果消费者消费的仓储量达不到满足,而又没有生产者,则程序会一直处于等待状态,这当然是不对的。实际上可以将此例进行修改,修改为,根据消费驱动生产,同时生产兼顾仓库,如果仓不满就生产,并对每次最大消费量做个限制,这样就不存在此问题了,当然这样的例子更复杂,更难以说明这样一个简单模型。

你可能感兴趣的:(synchronized)