Java多线程程序非阻塞式锁定实现

上一篇 / 下一篇  2012-06-21 10:55:42 / 个人分类:Java

 Java对 多线程程序的锁定已经有良好的支持,通常使用synchronized修饰一个方法或者一段代码。但是有一个问题,多个线程同时调用同一个方法的时候,所 有线程都被排队处理了。该被调用的方法越耗时,线程越多的时候,等待的线程等待的时间也就越长,甚至于几分钟或者几十分钟。对于Web等对反应时间要求很 高的系统来说,这是不可以接受的。本文就介绍一种自己实现的锁定方法,可以在没有拿到锁之后马上返回,告诉客户稍候重试。

g] A~9d} P/H0  某一段程序同一时刻需要保证只能单线程调用,那么策略很简单,最先到的线程获取锁成功,在它释放之前其它线程都会获取失败。首先要构造一个全局的系统锁仓库,代码如下:51Testing软件测试网Tt)J!K[#V?%{ ?9W@:Y ?+y

51Testing软件测试网x+g]a4sW

/*51Testing软件测试网 P4B'j E j4YP4M\Hc
 * LockStore.java  2012-5-1551Testing软件测试网)P'd [^*^){p(a{'I1L
 */

p8i)n)ZT#a'Mx^r z051Testing软件测试网.H7Jb&rm_

import java.util.Date;
B iI g6m^P`k1z0import java.util.HashMap;
~o:Qxq{'^Lil G0import java.util.Map;
51Testing软件测试网.X!Z8C)j{9Tbx

_/V{/m@#a t"F$H`0/**51Testing软件测试网'W~s!I K2EL]
 * 公用的内存锁仓库. 分为获取锁和释放锁两种操作。51Testing软件测试网9p!Zf*L]e
 *51Testing软件测试网#`~kOnj}lt
 * @version 1.051Testing软件测试网BDN4tUtw
 */
V+ze%eE4Qwc+j7W0public final class LockStore {
DL(XG2Ma:fzr0 // volatile保证所有线程看到的锁相同
6Lv_K2LI p~9C~0 private static volatile Map<String, Date> locks = new HashMap<String, Date>();

Z$\Y0_^;E;{\051Testing软件测试网a-}m~ R"nGc T

 private LockStore() {

Xo ]|h z!GY8} F0

`@AI?/v*~0 }

S&s.WJ;c@}0

1P!E.RK;H-Z2v0 /**51Testing软件测试网{.mV$Y!Xnmw
  * 根据锁名获取锁
.m4^ np].HP0  *51Testing软件测试网4eog `IS
  * @param lockName
s&N7X9bE.nJ+^0  *            锁名
-q?!o/S*OJt;s6FuWZ0  * @return 是否锁定成功51Testing软件测试网7BDciN-aP;O6n
  */
AQ6{5en$Z6W:SW$g0 public synchronized static Boolean getLock(String lockName) {51Testing软件测试网&qs-d!X.L0j I
  Boolean locked = false;

0b{;A[X.s r*_051Testing软件测试网8iO5p&m/Bbm4u

  if (StringUtils.isEmpty(lockName)) {51Testing软件测试网yo*`\b5N)RW
   throw new RuntimeException("Lock name can't be empty");
9f ]$m.P3]0  }

z!O4~Z4m)V(pT051Testing软件测试网ru E6g3RA

  Date lockDate = locks.get(lockName);
/nFE/Z;lCV Cm0  if (lockDate == null) {
#VB$~ ere@nS0   locks.put(lockName, new Date());51Testing软件测试网5^2UtRM(El({
   locked = true;51Testing软件测试网!xi R e$d m.qFO
  }
51Testing软件测试网7t_+fw*Xh-`

HI4n;bc6l(Q0  return locked;51Testing软件测试网4[w2dCI9x
 }
51Testing软件测试网BU*@|^P

51Testing软件测试网.| U IR'?

 /**
F*lOP'F7^l0  * 根据锁名释放锁51Testing软件测试网5\,M!C hH7E
  *
C*Y0P4^`J ?*d0  * @param lockName
3ar-`@pMK0  *            锁名51Testing软件测试网D,j-{t2MG1O2sYib
  */
:@j0E3A5LyO4E0 public synchronized static void releaseLock(String lockName) {51Testing软件测试网h6Pp+Y9cK
  if (StringUtils.isEmpty(lockName)) {
sx!L%l#@ e kH\Ov0   throw new RuntimeException("Lock name can't be empty");
q7HkS1XQ+Q0  }
51Testing软件测试网$\1I-e/nX!DI BB

c4I)?Pv+ompu^0  Date lockDate = locks.get(lockName);
s&G \$o~ e4|KP@'oIz0  if (lockDate != null) {51Testing软件测试网m5VI8TT ["uL
   locks.remove(lockName);51Testing软件测试网$^9I(oM$tw)@*c }
  }
6fw7T.RZ;x.D/\e(El0 }
51Testing软件测试网0B(\W0tA7oT~

ft my"xEp0 /**
(}9D%k"p9ZL6g v}0  * 获取上次成功锁定的时间
/`*|tzy0  *51Testing软件测试网2Qy:Z"M*H q
  * @param lockName51Testing软件测试网:u%@6|Lg~-D`J]
  *            锁名
G7d6~ j T@["n9T~0  * @return 如果还没有锁定返回NULL
'`3BM}!q ny I0  */51Testing软件测试网%RqA[ch#sp
 public synchronized static Date getLockDate(String lockName) {
w A P} |,b/RH}n T{0  if (StringUtils.isEmpty(lockName)) {51Testing软件测试网L#PkV s |_
   throw new RuntimeException("Lock name can't be empty");
.nG[(`6r o0  }
51Testing软件测试网N5Gh&T#_ADQj

51Testing软件测试网`"S5_)XSW

  Date lockDate = locks.get(lockName);

.aU rU7R ?4] U0

)V;IpC E |"V#~0  return lockDate;51Testing软件测试网|lX p6B0t;@
 }51Testing软件测试网 aJ {K$[8|
}
51Testing软件测试网5w'\.X%T,r_

51Testing软件测试网;wV~qaNM'X[+V

 锁仓库提供了三个方法,都是静态的,可以在系统内任意地方调用。 这里要提的是锁名,是一个字符串,可以随意构造,通常是需要锁定的方法名+需要单线程处理的标识。比如部门ID。这样不同的部门有不同的锁,独立运行,同一个部门同一个锁,单线程处理。具体使用如下:

l;rL*} cS0

)O+|w+as ~051Testing软件测试网_2S)m X ]7c*v6@2L

&E9@ l,NqU+g0/*51Testing软件测试网8J5N%Rx4W
 * LockTest.java  2012-6-19
5S"XA k.aw}F0 */
51Testing软件测试网$v(LUp@&F#E F

51Testing软件测试网1}\TWy1@ ?2M-D

import java.text.SimpleDateFormat;
a.b;E,t$\ UAr`6u0import java.util.Date;

+OS3e0D/O!O1_ `#\051Testing软件测试网5}6o QC'E7Cy

/**
6nH&Xyz0?1M)^'^0 * 锁仓库的使用51Testing软件测试网~8E7[ y/k"f&Ev g4y P2y
 *
h/?|Q,?{%`|0 * @version 1.0
m GmG(Sk$J0 */51Testing软件测试网.C{;l Vex6`
public class LockTest {51Testing软件测试网&T2F,K1\?-ITYhw5s
 public Boolean doSomething(String departmentId, StringBuffer message) {51Testing软件测试网Nd:ab5ia%mT1np
  // 同一个部门同时只能有一个处理, 不同部门可以并行处理51Testing软件测试网Y_ G;c'G
  String lockName = "doSomething_" + departmentId;
RD3VF+gO'T0  Boolean result;
`7TL3`k0  if (LockStore.getLock(lockName)) {51Testing软件测试网)o%m\+n|Yz I
   try {
&i9P6Iv7I7{0    // do things here51Testing软件测试网^2K5l7\%?9u?1U M
   } finally {
mI&q _?.A0    LockStore.releaseLock(lockName);51Testing软件测试网#EY$I b)Ob2H
    result = true;51Testing软件测试网#VC4_.Yu;PR Z'o
   }51Testing软件测试网9Ac1J-x*j_ C$Uv$n
  } else {
T?V{ g"`6Al [:q C0   Date lastLockDate = LockStore.getLockDate(lockName);51Testing软件测试网.v? CE)`|&Md6kP h5y
   String messageStr = "您所在的部门已经在处理中, 启动时间为:"
g1O$OjPEC`0     + getDateDetailDesc(lastLockDate);51Testing软件测试网 L#mM{ Y0vQb-X b/s
   message.append(messageStr);
O%[+Xh ^o:_0   result = false;51Testing软件测试网$\;L Z"s*N5d
  }
51Testing软件测试网 f {eYlZ9Q

4tU Gg?S*u0  return result;
!C9Tni/_,P!_)[H X0 }
51Testing软件测试网:P-q@r[C

51Testing软件测试网~8jz fv2`v

 /*51Testing软件测试网!\_X B'ox7s"i s7q
  * 获取日期的具体时间描述
8sz _R ];n0  */
5C;v8Ji;l0 private String getDateDetailDesc(Date date) {51Testing软件测试网6nf:KB1rh!W [
  SimpleDateFormat sdf = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss");51Testing软件测试网v O%\(Q\K%\\+Z:]
  return sdf.format(date);
'L(guTl^,iu?(K0 }
hz \$dh/Q#Y(q0}

FE5I9F$o1bLp0
51Testing软件测试网{2^8gY9rR

  通过以上设计,系统内部任何耗时且需要保证单线程的地方都可以用该方法实现非阻塞式的访问,提高用户体验。甚至于有的调用本身就要求这样的设计,只需处理一次,比如做日终。锁名的自定义带来了锁粒度的灵活设定,可以在运行时根据参数实现任意级别的锁定。

OR%l+nn-^0

TAG:

 

评分:0

我来说两句

Open Toolbar