【Java并发】Lock 和 Condition

版权声明:转载请注明出处: https://blog.csdn.net/qq_21687635/article/details/84638360

Lock 接口

在Lock接口出现之前,Java程序是靠synchronized关键字来实现锁功能的,它提供了与synchronized关键字类似的同步功能,只是在使用时需要显式地获取和释放锁,但是提供了多种synchronized关键字所不具备的同步特性。

Lock接口提供的synchronized关键字不具备的主要特性:

  • 尝试非阻塞地获取锁
  • 获取到的锁的线程能够响应中断
  • 在指定的截止时间之前获取锁

一个生动的例子

public class LockExample {
	public static void main(String[] args) {
		Lock lock = new ReentrantLock();
		lock.lock();
		try {

		} finally {
			lock.unlock();
		}
	}
}

ReentrantLock

重入锁ReentranLock,就是支持重进入的锁,表示该锁能够支持一个线程对资源的重复加锁。除此之外,该锁还支持获取锁时的公平和非公平性选择。

不支持重入的锁,当自己获取锁之后,再获取锁的时候,该线程就会被自己阻塞,synchronized关键字也支持重进入。

公平性与否是针对获取锁而言的,如果一个锁是公平的,那么锁的获取顺序就应该符合请求的绝对时间顺序,也就是FIFO。

一个生动的例子

public class FairAndUnfairTest {

	private static ReentrantLock2 fairLock = new ReentrantLock2(true);
	private static ReentrantLock2 unfairLock = new ReentrantLock2(false);

	@Test
	public void unfairTest() {
		testLock(unfairLock);
	}

	@Test
	public void fairTest() {
		testLock(fairLock);
	}

	private void testLock(ReentrantLock2 lock) {
		for (int i = 0; i < 5; i++) {
			Job job = new Job(lock);
			job.setName(String.valueOf(i));
			job.start();
		}
	}

	private static class Job extends Thread {
		private ReentrantLock2 lock;

		public Job(ReentrantLock2 lock) {
			this.lock = lock;
		}

		@Override
		public void run() {
			lock.lock();
			try {
				System.out.println("Lock By[" + Thread.currentThread() + "], Waiting by " + lock.getQueuedThreads());
				System.out.println("Lock By[" + Thread.currentThread() + "], Waiting by " + lock.getQueuedThreads());
			} finally {
				lock.unlock();
			}
		}

		@Override
		public String toString() {
			return super.getName();
		}
	}

	private static class ReentrantLock2 extends ReentrantLock {
		private static final long serialVersionUID = 1L;

		public ReentrantLock2(boolean fair) {
			super(fair);
		}

		public Collection<Thread> getQueuedThreads() {
			List<Thread> arrayList = new ArrayList<>(super.getQueuedThreads());
			Collections.reverse(arrayList);
			return arrayList;
		}
	}
}

输出的结果可能是:

公平锁 非公平锁
Lock By[0], Waiting by [] Lock By[0], Waiting by []
Lock By[0], Waiting by [1, 2] Lock By[0], Waiting by [1, 2]
Lock By[1], Waiting by [2, 4] Lock By[1], Waiting by [2]
Lock By[1], Waiting by [2, 4, 3] Lock By[1], Waiting by [2]
Lock By[2], Waiting by [4, 3] Lock By[3], Waiting by [2]
Lock By[2], Waiting by [4, 3] Lock By[3], Waiting by [2]
Lock By[4], Waiting by [3] Lock By[4], Waiting by [2]
Lock By[4], Waiting by [3] Lock By[4], Waiting by [2]
Lock By[3], Waiting by [] Lock By[2], Waiting by []
Lock By[3], Waiting by [] Lock By[2], Waiting by []

每条线程输出两条信息,从结果可以看出,公平性锁每次都是从同步队列中的第一个节点获取锁,而非公平锁不一定。

ReentrantReadWriteLock

排他锁就是在同一个时刻只允许一个线程进行访问,而读写锁在同一时刻可以允许多个读线程访问,但是在写线程访问时,所有的读线程和写线程均被阻塞。

读写锁维护了一个读锁和一个写锁,支持公平性选择、重进入、锁降级(遵循获取写锁、获取读锁再释放写锁的次序,写锁可以降级为读锁)。

一个生动的例子

public class ReentrantReadWriteLockExample {
	private static Map<String, Object> map = new HashMap<String, Object>();
	private static ReentrantReadWriteLock rwl = new ReentrantReadWriteLock();
	private static Lock r = rwl.readLock();
	private static Lock w = rwl.writeLock();

	public static final Object get(String key) {
		r.lock();
		try {
			return map.get(key);
		} finally {
			r.unlock();
		}
	}

	public static final Object put(String key, Object value) {
		w.lock();
		try {
			return map.put(key, value);
		} finally {
			w.unlock();
		}
	}

	public static final void clear() {
		w.lock();
		try {
			map.clear();
		} finally {
			w.unlock();
		}
	}
}

用一个非线程安全的HashMap作为缓存的实现,同时使用读写锁的读锁和写锁来保证HashMap是线程安全的。

Condition

任意一个Java对象,都有一组监视器方法,主要包括wait()、wait(long)、notify()以及notifyAll()方法,这些方法与synchronized关键字配合,可以实现等待/通知模式。

Condition接口也提供了类似Object的监视器方法,与Lock配合可以实现等待/通知模式。它支持的特性如下:

  • 多个等待队列
  • 在等待状态中不响应中断
  • 等待到将来的某个时间。

一个生动的例子

public class ConditionExample {
	private Object[] items;

	private int addIndex, removeIndex, count;
	private Lock lock = new ReentrantLock();
	private Condition notEmpty = lock.newCondition();
	private Condition notFull = lock.newCondition();

	public ConditionExample(int size) {
		items = new Object[size];
	}

	public void add(Object t) throws InterruptedException {
		lock.lock();
		try {
			while (count == items.length) {
				notFull.await();
			}

			items[addIndex] = t;
			if (++addIndex == items.length) {
				addIndex = 0;
			}
			count++;
			notEmpty.signal();
		} finally {
			lock.unlock();
		}
	}

	public Object remove() throws InterruptedException {
		lock.lock();
		try {
			while (count == 0) {
				notEmpty.await();
			}
			Object result = items[removeIndex];
			if (++removeIndex == items.length) {
				removeIndex = 0;
			}
			count--;
			notFull.signal();
			return result;
		} finally {
			lock.unlock();
		}
	}
}

参考

  1. Java并发编程的艺术[书籍]

猜你喜欢

转载自blog.csdn.net/qq_21687635/article/details/84638360