通过ConcurrentHashMap的putIfAbsent方法实现对象锁

putIfAbsent方法往map中存储对象时,如果对象不存在则返回null,如果对象已经存在则返回value值,根据ConcurrentHashMap的并发特性,结合这个方法,可以实现一个简单的本地对象锁。

import java.util.concurrent.ConcurrentHashMap;


/**
 * 使用 ConcurrentHashMap的putIfAbsent方法,实现对象锁
 * @author zqz
 */
public class MapLock {
	
	private final static Object val = new Object();
	
	/**锁缓存**/
	public static final ConcurrentHashMap lockCache = new ConcurrentHashMap(64);
	
	/**
	 * 通过锁的方式执行操作,没有获取到锁则休眠50毫秒
	 * @param lock  锁对象
	 * @throws InterruptedException
	 */
	public static void doWork(Object lock) throws InterruptedException{
		doWork(lock,50);
	}
	
	/**
	 * 通过锁的方式执行操
	 * @param lock       锁对象
	 * @param sleepTime  获取锁失败,休眠的毫秒值 
	 * @throws InterruptedException
	 */
	public static void doWork(Object lock,long sleepTime) throws InterruptedException {
		Object isNull = lockCache.putIfAbsent(lock, val);
		if (isNull != null) {
			//没有获取到锁
			System.out.println(Thread.currentThread().getId()+"等待锁"+sleepTime+"ms");
			Thread.sleep(sleepTime);
			MapLock.doWork(lock,sleepTime);
		} else {
			//获取到锁
			System.out.println(Thread.currentThread().getId()+"获取锁,开始执行");
			Thread.sleep(100);
			System.out.println(Thread.currentThread().getId()+"执行完成,耗时100毫秒,释放锁");
			//释放锁
			lockCache.remove(lock);
		}
	}
	
	
	/**
	 * 测试
	 * @param args
	 * @throws InterruptedException
	 */
	public static void main(String[] args) throws InterruptedException {
		//需要注意的是,如果lock对象是自定义对象,那么要重写它的equals方法和hashCode方法;
		String lock = "testLock";
		new TestThread(lock).start();
		new TestThread(lock).start();
		new TestThread(lock).start();
		new TestThread(lock).start();
	}
	
	/**
	 * 测试线程类
	 * @author zqz
	 */
	static class TestThread extends Thread{
		private Object lock;
		
		TestThread(Object lock){
			this.lock = lock;
		}
		
		public void run() {
			try {
				MapLock.doWork(lock);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
	}
	
}

测试结果

10获取锁,开始执行
12等待锁50ms
11等待锁50ms
13等待锁50ms
12等待锁50ms
13等待锁50ms
11等待锁50ms
10执行完成,耗时100毫秒,释放锁
13获取锁,开始执行
12等待锁50ms
11等待锁50ms
11等待锁50ms
12等待锁50ms
13执行完成,耗时100毫秒,释放锁
11等待锁50ms
12获取锁,开始执行
11等待锁50ms
12执行完成,耗时100毫秒,释放锁
11等待锁50ms
11获取锁,开始执行
11执行完成,耗时100毫秒,释放锁

注意:如果锁对象是一个自定义对象,那么要重写它的hashcode和equals方法,想想为什么?


应用场景:

例如防止缓存穿透,有100台服务器,每台服务器同时有10个线程去访问同一个过期的缓存key,就会导致有100*10=1000个线程执行查询数据库更新这个缓存,对于数据库这是非常恐怖的,通常做法是使用分布式锁,但是使用分布式锁,就会变成同时有1000个线程先去竞争分布式锁,如果这时候使用本地锁,将要查询的key作为锁对象,控制本机同一时刻更新同一个缓存key只能有一个线程去执行这个操作,这样竞争分布式锁的线程数就变成了100了;

你可能感兴趣的:(java)