Java 多线程编程之 notify notifyAll wait lock unlock 算法

写了一个类来理解java 同步机制的算法。这个类并不适合实战,而仅仅是算法层面进行理解。

package multithread;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.atomic.AtomicBoolean;

/**
 * The above specifications allow us to determine several properties having to
 * do with the interaction of waits, notification, and interruption.
 * 
 * If a thread is both notified and interrupted while waiting, it may either:
 * 
 * return normally from wait, while still having a pending interrupt (in other
 * words, a call to Thread.interrupted would return true)
 * 
 * return from wait by throwing an InterruptedException
 * 
 * The thread may not reset its interrupt status and return normally from the
 * call to wait.
 * 
 * Similarly, notifications cannot be lost due to interrupts. Assume that a set
 * s of threads is in the wait set of an object m, and another thread performs a
 * notify on m. Then either:
 * 
 * at least one thread in s must return normally from wait, or
 * 
 * all of the threads in s must exit wait by throwing InterruptedException
 * 
 * Note that if a thread is both interrupted and woken via notify, and that
 * thread returns from wait by throwing an InterruptedException, then some other
 * thread in the wait set must be notified.
 * 
 * @author wa505
 *
 */
public class Monitor {

	private AtomicBoolean mLock = new AtomicBoolean();

	private volatile Thread mHeldThread;

	private volatile int mHeldCount;

	/**
	 * Every object, in addition to having an associated monitor, has an associated
	 * wait set. A wait set is a set of threads. When an object is first created,
	 * its wait set is empty. Elementary actions that add threads to and remove
	 * threads from wait sets are atomic. Wait sets are manipulated solely through
	 * the methods Object.wait, Object.notify, and Object.notifyAll. Wait set
	 * manipulations can also be affected by the interruption status of a thread,
	 * and by the Thread class's methods dealing with interruption. Additionally,
	 * the Thread class's methods for sleeping and joining other threads have
	 * properties derived from those of wait and notification actions.
	 */
	private List<Waitor> mWaitSet = new ArrayList<>();

	private static class Waitor {
		Thread thread;
		int waitCount;
	}

	private boolean isInWaitingSet() {
		Thread thread = Thread.currentThread();
		synchronized (mWaitSet) {
			for (Waitor waitor : mWaitSet) {
				if (waitor.thread == thread) {
					return true;
				}
			}
		}
		return false;
	}

	/**
	 * Notification actions occur upon invocation of methods notify and notifyAll.
	 * 
	 * Let thread t be the thread executing either of these methods on object m, and
	 * let n be the number of lock actions by t on m that have not been matched by
	 * unlock actions. One of the following actions occurs:
	 * 
	 * If n is zero, then an IllegalMonitorStateException is thrown.
	 * 
	 * This is the case where thread t does not already possess the lock for target
	 * m.
	 * 
	 * If n is greater than zero and this is a notify action, then if m's wait set
	 * is not empty, a thread u that is a member of m's current wait set is selected
	 * and removed from the wait set.
	 * 
	 * There is no guarantee about which thread in the wait set is selected. This
	 * removal from the wait set enables u's resumption in a wait action. Notice,
	 * however, that u's lock actions upon resumption cannot succeed until some time
	 * after t fully unlocks the monitor for m.
	 * 
	 * If n is greater than zero and this is a notifyAll action, then all threads
	 * are removed from m's wait set, and thus resume.
	 * 
	 * Notice, however, that only one of them at a time will lock the monitor
	 * required during the resumption of wait.
	 * 
	 * 
	 */
	public void snotifyAll() {
		if (Thread.currentThread() == mHeldThread) {
			if (mHeldCount > 0) {
				synchronized (mWaitSet) {
					mWaitSet.clear();
				}
			}
		}
	}

	public void snotify() {
		if (Thread.currentThread() == mHeldThread) {
			if (mHeldCount > 0) {
				synchronized (mWaitSet) {
					mWaitSet.remove(0);
				}
			}
		}
	}

	/**
	 * Wait actions occur upon invocation of wait(), or the timed forms wait(long
	 * millisecs) and wait(long millisecs, int nanosecs).
	 * 
	 * A call of wait(long millisecs) with a parameter of zero, or a call of
	 * wait(long millisecs, int nanosecs) with two zero parameters, is equivalent to
	 * an invocation of wait().
	 * 
	 * A thread returns normally from a wait if it returns without throwing an
	 * InterruptedException.
	 * 
	 * Let thread t be the thread executing the wait method on object m, and let n
	 * be the number of lock actions by t on m that have not been matched by unlock
	 * actions. One of the following actions occurs:
	 * 
	 * If n is zero (i.e., thread t does not already possess the lock for target m),
	 * then an IllegalMonitorStateException is thrown.
	 * 
	 * If this is a timed wait and the nanosecs argument is not in the range of
	 * 0-999999 or the millisecs argument is negative, then an
	 * IllegalArgumentException is thrown.
	 * 
	 * If thread t is interrupted, then an InterruptedException is thrown and t's
	 * interruption status is set to false.
	 * 
	 * Otherwise, the following sequence occurs:
	 * 
	 * Thread t is added to the wait set of object m, and performs n unlock actions
	 * on m.
	 * 
	 * Thread t does not execute any further instructions until it has been removed
	 * from m's wait set. The thread may be removed from the wait set due to any one
	 * of the following actions, and will resume sometime afterward:
	 * 
	 * A notify action being performed on m in which t is selected for removal from
	 * the wait set.
	 * 
	 * A notifyAll action being performed on m.
	 * 
	 * An interrupt action being performed on t.
	 * 
	 * If this is a timed wait, an internal action removing t from m's wait set that
	 * occurs after at least millisecs milliseconds plus nanosecs nanoseconds elapse
	 * since the beginning of this wait action.
	 * 
	 * An internal action by the implementation. Implementations are permitted,
	 * although not encouraged, to perform "spurious wake-ups", that is, to remove
	 * threads from wait sets and thus enable resumption without explicit
	 * instructions to do so.
	 * 
	 * Notice that this provision necessitates the Java coding practice of using
	 * wait only within loops that terminate only when some logical condition that
	 * the thread is waiting for holds.
	 * 
	 * Each thread must determine an order over the events that could cause it to be
	 * removed from a wait set. That order does not have to be consistent with other
	 * orderings, but the thread must behave as though those events occurred in that
	 * order.
	 * 
	 * For example, if a thread t is in the wait set for m, and then both an
	 * interrupt of t and a notification of m occur, there must be an order over
	 * these events. If the interrupt is deemed to have occurred first, then t will
	 * eventually return from wait by throwing InterruptedException, and some other
	 * thread in the wait set for m (if any exist at the time of the notification)
	 * must receive the notification. If the notification is deemed to have occurred
	 * first, then t will eventually return normally from wait with an interrupt
	 * still pending.
	 * 
	 * Thread t performs n lock actions on m.
	 * 
	 * If thread t was removed from m's wait set in step 2 due to an interrupt, then
	 * t's interruption status is set to false and the wait method throws
	 * InterruptedException.
	 * 
	 * 
	 * 
	 * 
	 * 
	 * Interruption actions occur upon invocation of Thread.interrupt, as well as
	 * methods defined to invoke it in turn, such as ThreadGroup.interrupt.
	 * 
	 * Let t be the thread invoking u.interrupt, for some thread u, where t and u
	 * may be the same. This action causes u's interruption status to be set to
	 * true.
	 * 
	 * Additionally, if there exists some object m whose wait set contains u, then u
	 * is removed from m's wait set. This enables u to resume in a wait action, in
	 * which case this wait will, after re-locking m's monitor, throw
	 * InterruptedException.
	 * 
	 * Invocations of Thread.isInterrupted can determine a thread's interruption
	 * status. The static method Thread.interrupted may be invoked by a thread to
	 * observe and clear its own interruption status.
	 */
	public void swait() {
		if (Thread.currentThread() == mHeldThread) {
			if (mHeldCount > 0) {
				Waitor w = new Waitor();
				w.thread = mHeldThread;
				w.waitCount = mHeldCount;
				synchronized (mWaitSet) {
					mWaitSet.add(w);
				}
				for (int i = 0; i < mHeldCount; ++i) {
					unLock();
				}
				while (isInWaitingSet()) {
					try {
						Thread.sleep(0);
					} catch (InterruptedException e) {
						mWaitSet.remove(w);
					}
				}
				for (int i = 0; i < w.waitCount; ++i) {
					lock();
				}
			}
		}
	}

	/**
	 * <p>
	 * The Java programming language provides multiple mechanisms for communicating
	 * between threads. The most basic of these methods is synchronization, which is
	 * implemented using monitors. Each object in Java is associated with a monitor,
	 * which a thread can lock or unlock. Only one thread at a time may hold a lock
	 * on a monitor. Any other threads attempting to lock that monitor are blocked
	 * until they can obtain a lock on that monitor. A thread t may lock a
	 * particular monitor multiple times; each unlock reverses the effect of one
	 * lock operation.
	 * </p>
	 * <p>
	 * The synchronized statement (§14.19) computes a reference to an object; it
	 * then attempts to perform a lock action on that object's monitor and does not
	 * proceed further until the lock action has successfully completed. After the
	 * lock action has been performed, the body of the synchronized statement is
	 * executed. If execution of the body is ever completed, either normally or
	 * abruptly, an unlock action is automatically performed on that same monitor.
	 * </p>
	 * <p>
	 * A synchronized method (§8.4.3.6) automatically performs a lock action when it
	 * is invoked; its body is not executed until the lock action has successfully
	 * completed.
	 * <li>If the method is an instance method, it locks the monitor associated with
	 * the instance for which it was invoked (that is, the object that will be known
	 * as this during execution of the body of the method).
	 * <li>If the method is static, it locks the monitor associated with the Class
	 * object that represents the class in which the method is defined.
	 * <li>If execution of the method's body is ever completed, either normally or
	 * abruptly, an unlock action is automatically performed on that same monitor.
	 * </p>
	 * <p>
	 * The Java programming language neither prevents nor requires detection of
	 * deadlock conditions. Programs where threads hold (directly or indirectly)
	 * locks on multiple objects should use conventional techniques for deadlock
	 * avoidance, creating higher-level locking primitives that do not deadlock, if
	 * necessary.
	 * </p>
	 * <p>
	 * Other mechanisms, such as reads and writes of volatile variables and the use
	 * of classes in the java.util.concurrent package, provide alternative ways of
	 * synchronization.
	 * </p>
	 */
	public void lock() {
		if (Thread.currentThread() != mHeldThread) {
			while (!mLock.compareAndSet(false, true)) {
				try {
					Thread.sleep(0);
				} catch (InterruptedException e) {

				}
			}
			mHeldThread = Thread.currentThread();
			mHeldCount++;
		} else {
			mHeldCount++;
		}
	}

	public void unLock() {
		if (Thread.currentThread() == mHeldThread) {
			if (mHeldCount > 0) {
				mHeldCount--;
				if (mHeldCount == 0) {
					mHeldThread = null;
					mLock.getAndSet(false);
				}
			}
		}
	}
}


猜你喜欢

转载自daojin.iteye.com/blog/2391952