写系统代码的时候遇到一个对象锁的问题。场景描述如下:
我有一个系统系统需要按照集群名称发送任务给执行任务的系统。这个分发任务给执行任务的系统的过程是并发的。我需要控制一下发送任务的数量,为改数量设定一个阈值。比如:一次只能发送10个任务。
在这个过程中,我需要针对集群名称(String clusterName)进行加锁。因为我会同时给很多集群发送任务,我只需要确保给每个集群分发任务的时候是加锁串行的,但是每个不同集群之间不存在资源竞争,不需要加锁。
我开始想给String clusterName加锁,但是网上文章说这样对字符串加锁非常不好。
参考:
http://www.blogjava.net/qujinlong123/archive/2007/05/31/121200.html
但是我确实需要一个关于集群名字的锁。下面有个类似的例子实现:
https://segmentfault.com/q/1010000007893810
给字符串加锁,如果该字符串执行 split() 或者 intern() 等操作时都会被锁住。
那该如何处理呢? 而且为 “hello” 加锁和 new String("hello") 加锁是不行的,它们是2个对象。虽然它们比较equals是相等的。
我采用为这个字符串(String clusterName)进行一次封装的形式进行加锁。
如下:
/** * <pre> * Copyright test.com CDC [2000-2017] * </pre> */ package com.test.tmp; import com.google.common.base.Preconditions; import com.google.common.collect.Maps; import java.util.concurrent.ConcurrentMap; import java.util.concurrent.TimeUnit; /** * <pre> * Test.java * @author kanpiaoxue<br> * @version 1.0 * Create Time 2017年1月17日 下午7:47:40<br> * Description : TODO 类实现描述 * </pre> */ public class Test { public static final ConcurrentMap<String, ClusterLock> map = Maps.newConcurrentMap(); /** * <pre> * @param args * </pre> * * @throws Exception */ public static void main(String[] args) throws Exception { new Thread(new Runnable() { @Override public void run() { String clusterName = new String("luowenlei"); ClusterLock v = new ClusterLock(clusterName); ClusterLock tmp = map.putIfAbsent(clusterName, v); if (null == tmp) { System.out.println(Thread.currentThread() + " -- first"); tmp = v; } ClusterLock clusterNameForLock = tmp; synchronized (clusterNameForLock) { System.out.println("locked ..."); try { TimeUnit.SECONDS.sleep(10L); } catch (InterruptedException e) { } System.out.println("unlocked ..."); } } }).start(); TimeUnit.SECONDS.sleep(1L); new Thread(new Runnable() { @Override public void run() { String clusterName = new String("luowenlei"); ClusterLock v = new ClusterLock(clusterName); ClusterLock tmp = map.putIfAbsent(clusterName, v); if (null == tmp) { System.out.println(Thread.currentThread()); tmp = v; } System.out.println(Thread.currentThread() + " -- second"); ClusterLock clusterNameForLock = tmp; synchronized (clusterNameForLock) { System.out.println("locked lock1 ..."); System.out.println("unlocked lock1 ..."); } } }).start(); } } class ClusterLock { private String clusterName; /** * <pre> * @param clusterName * </pre> */ public ClusterLock(String clusterName) { super(); this.clusterName = clusterName; } /* * (non-Javadoc) * @see java.lang.Object#hashCode() */ @Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + ((clusterName == null) ? 0 : clusterName.hashCode()); return result; } /* * (non-Javadoc) * @see java.lang.Object#equals(java.lang.Object) */ @Override public boolean equals(Object obj) { if (this == obj) return true; if (obj == null) return false; if (getClass() != obj.getClass()) return false; ClusterLock other = (ClusterLock) obj; if (clusterName == null) { if (other.clusterName != null) return false; } else if (!clusterName.equals(other.clusterName)) return false; return true; } /* * (non-Javadoc) * @see java.lang.Object#toString() */ @Override public String toString() { return "ClusterLock [clusterName=" + clusterName + "]"; } }
正确打印的日志信息如下:
写道
Thread[Thread-0,5,main] -- first
locked ...
Thread[Thread-1,5,main] -- second
unlocked ...
locked lock1 ...
unlocked lock1 ...
locked ...
Thread[Thread-1,5,main] -- second
unlocked ...
locked lock1 ...
unlocked lock1 ...
这样就解决了为字符串加锁的弊端的问题,同时做到了为集群加锁的功能。