这里写自定义目录标题
- 1. 前言
- 2. new String()
- 3. StringBulider和StringBuffer
- 4. 解决方法
1. 前言
String是Java中的一种特殊类型,String在创建后存入字符串常量池。
利用这个特征,可以使用String作为同步锁。例如,在更新用户信息时,可以使用用户的名称作为同步锁,不同的用户可以使用不同的锁来提高并发性能。这个特征扩展适当的场景非常多。
2. new String()
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48import java.util.concurrent.BrokenBarrierException; import java.util.concurrent.CyclicBarrier; public class TestStringSync { private static Integer CNT = 0; public static void main(String[] args) { final String lock = new String(ObjectId.get().toString()); run(lock); } private static void run(String lock) { final Integer threadNum = 10; final CyclicBarrier cb = new CyclicBarrier(threadNum, new Runnable() { public void run() { System.out.println("threadNum : " + threadNum); } }); for(int i = 0; i< threadNum; i++) { String tmpLock = new String(lock); new TestThread(cb, tmpLock.toString()).start(); } } static class TestThread extends Thread { private CyclicBarrier cbLock; private String lock; public TestThread(CyclicBarrier cbLock, String lock) { this.cbLock = cbLock; this.lock = lock; } public void run() { try { cbLock.await(); } catch (InterruptedException e) { e.printStackTrace(); } catch (BrokenBarrierException e) { e.printStackTrace(); } try { Thread.sleep(10); } catch (InterruptedException e) { e.printStackTrace(); } synchronized(lock) { //这里直接使用String对象本身作为锁 CNT = CNT+1; System.out.println("Value:" + CNT); } } } }
1
2
3
4
5
6
7
8
9
10
11
12threadNum : 10 Value:2 Value:2 Value:2 Value:2 Value:4 Value:5 Value:5 Value:4 Value:4 Value:4
从结果可以看出,每个线程创建前使用new String(lock)会产生不同的锁,造成线程同步失败。所以在使用的时候要特别注意这点,new String(lock)是会产生不同的对象,他们所指向的对象锁是不同的。
3. StringBulider和StringBuffer
1
2
3
4
5
6
7StringBuilder tmpLock = new StringBuilder(); tmpLock.append("user name"); tmpLock.append("org name"); for(int i = 0; i< threadNum; i++) { new TestThread(cb, tmpLock.toString()).start(); }
1
2
3
4
5
6
7
8
9
10
11
12threadNum : 10 Value:2 Value:2 Value:2 Value:3 Value:2 Value:3 Value:2 Value:3 Value:2 Value:2
可见,这个锁还是不行。原因是StringBuiler的toString方法中返回的是new String,代码如下:
1
2
3
4
5
6@Override public String toString() { // Create a copy, don't share the array return new String(value, 0, count); }
这就导致线程拿到的还是不同的字符串对象。
4. 解决方法
针对上面举的例子可以发现,使用String作为同步锁必须注意产生不同对象的问题,必须保证线程拿到的是同一个String对象。做法最简单的就是使用同一个String对象,但这个有时很难保证。特别是我们很多的时候代码是分布式环境下的。
比如,我们将用户名存在了redis里,线程每次同步的时候去redis里取一下数据,这样就很有可能导致产生新的String对象。这个时候就得使用intern()方法。上面的代码修改为:
1
2
3
4
5synchronized(lock.intern()) { CNT = CNT+1; System.out.println("Value:" + CNT); }
这样就是直接获取的是字符串的值本身,而不是取的String的对象,以此保证同一个字符串拿到的是同一个String对象,自然在同一个进程中就是同一个对象锁了。
1
2
3
4
5
6
7
8
9
10
11
12threadNum : 10 Value:1 Value:2 Value:3 Value:4 Value:5 Value:6 Value:7 Value:8 Value:9 Value:10
来源:http://t.zoukankan.com/5207-p-9592516.html
String.intern():https://blog.csdn.net/tyyking/article/details/82496901
最后
以上就是懵懂荔枝最近收集整理的关于synchronized(string)1. 前言2. new String()3. StringBulider和StringBuffer4. 解决方法的全部内容,更多相关synchronized(string)1.内容请搜索靠谱客的其他文章。
发表评论 取消回复