问题描写
业务有一个需求,我把问题描写一下:
通过署理IP会见海外某网站N,每个IP对应一个牢靠的网站N的COOKIE,COOKIE有失效时间。 并发下,取IP是有必然计策的,取到IP之后拿IP对应的COOKIE,发明COOKIE高出失效时间,则挪用剧本会见网站N获取一次数据。 为了防备多线程取到同一个IP,同时发明该IP对应的COOKIE失效,同时去挪用剧本更新COOKIE,针对IP加了锁。为了担保锁的全局独一性,在锁前面加了标识业务的前缀,利用synchronized(lock){...}的方法,锁住"锁前缀+IP",这样担保多线程取到同一个IP,也只有一个IP会更新COOKIE。
不知道这个问题有没有说清楚,没说清楚不要紧,写一段测试代码:
public class StringThread implements Runnable { private static final String LOCK_PREFIX = "XXX---"; private String ip; public StringThread(String ip) { this.ip = ip; } @Override public void run() { String lock = buildLock(); synchronized (lock) { System.out.println("[" + JdkUtil.getThreadName() + "]开始运行了"); // 休眠5秒模仿剧本挪用 JdkUtil.sleep(5000); System.out.println("[" + JdkUtil.getThreadName() + "]竣事运行了"); } } private String buildLock() { StringBuilder sb = new StringBuilder(); sb.append(LOCK_PREFIX); sb.append(ip); String lock = sb.toString(); System.out.println("[" + JdkUtil.getThreadName() + "]构建了锁[" + lock + "]"); return lock; } }
简朴说就是,传入一个IP,只管构建一个全局独一的字符串(这么做的原因是,假如字符串的独一性不强,例如说锁的”192.168.1.1″,假如别的一段业务代码也是锁的这个字符串”192.168.1.1″,这就意味着两段没什么关联的代码块却要串行执行,代码块执行时间短还好,代码块执行时间长影响极其大),针对字符串加锁。
预期的功效是并发下,好比5条线程传入同一个IP,它们构建的锁都是字符串”XXX—192.168.1.1″,那么这5条线程针对synchronized块,该当串行执行,即一条运行完毕再运行别的一条,可是实际上并不是这样。
写一段测试代码,开5条线程看一下结果:
public class StringThreadTest { private static final int THREAD_COUNT = 5; @Test public void testStringThread() { Thread[] threads = new Thread[THREAD_COUNT]; for (int i = 0; i < THREAD_COUNT; i++) { threads[i] = new Thread(new StringThread("192.168.1.1")); } for (int i = 0; i < THREAD_COUNT; i++) { threads[i].start(); } for (;;); } }
执行功效为:
[Thread-1]构建了锁[XXX---192.168.1.1] [Thread-1]开始运行了 [Thread-3]构建了锁[XXX---192.168.1.1] [Thread-3]开始运行了 [Thread-4]构建了锁[XXX---192.168.1.1] [Thread-4]开始运行了 [Thread-0]构建了锁[XXX---192.168.1.1] [Thread-0]开始运行了 [Thread-2]构建了锁[XXX---192.168.1.1] [Thread-2]开始运行了 [Thread-1]竣事运行了 [Thread-3]竣事运行了 [Thread-4]竣事运行了 [Thread-0]竣事运行了 [Thread-2]竣事运行了
看到Thread-0、Thread-1、Thread-2、Thread-3、Thread-4这5条线程尽量构建的锁都是同一个”XXX-192.168.1.1″,可是代码却是并行执行的,这并不切合我们的预期。
关于这个问题,一方面确实是我大意了觉得是代码其他什么处所同步节制呈现了问题,一方面也反应出我对String的领略还不足深入,因此专门写一篇文章来记录一下这个问题并写清楚发生这个问题的原因和该当如何办理。
问题原因
这个问题既然呈现了,那么该当从功效开始推导起,软件开发,找到问题的原因。先看一下synchronized部门的代码:
@Override public void run() { String lock = buildLock(); synchronized (lock) { System.out.println("[" + JdkUtil.getThreadName() + "]开始运行了"); // 休眠5秒模仿剧本挪用 JdkUtil.sleep(5000); System.out.println("[" + JdkUtil.getThreadName() + "]竣事运行了"); } }
因为synchronized锁工具的时候,劳务派遣管理系统,担保同步代码块中的代码执行是串行执行的前提条件是锁住的工具是同一个,因此既然多线程在synchronized部门是并行执行的,那么可以猜测出多线程下传入同一个IP,构建出来的lock字符串并不是同一个。