我有一个计数器,多个线程访问getCount
方法。代码如下:
public class ThreadSafeMethod {
public static int counter = 0;
public static int getCount() {
return counter++;
}
}
方法线程安全吗?我的理解是,因为counter++
不是自动的,所以不安全。那怎么安全呢?如果我们添加synchronized
关键字,将同步哪个对象?
答案 0 :(得分:5)
当你说它不是线程安全的时,你的分析是正确的,因为操作不是原子的。检索值和增量不是线程安全的。对此方法的多次调用(无论是否有参数)都可以访问相同的非局部变量。
向此方法添加synchronized
使其具有线程安全性。添加到static
方法时,Class
对象是被锁定的对象。
使其成为线程安全的替代方法是将int
替换为AtomicInteger
,{{1}}具有自己的原子getAndIncrement
方法。
答案 1 :(得分:3)
否,无参数方法不本质上是线程安全的 - 缺少参数在此示例中没有区别。
counter
变量的读取(并写入)不保证在线程之间是原子或一致
最简单的改变就是make the method synchronized
:
public static synchronized int getCount() {
return counter++;
}
(最简单的并不总是“最好”或“正确”,但这里就足够了,假设没有其他代码触及公共counter
变量。)
请参阅this answer了解静态方法的同步是如何工作的 - 可以想象,它本身就是用作监视器的类。
答案 2 :(得分:1)
在静态函数上使用synchronized关键字会将函数“锁定”到一个线程,以确保两个线程不会混淆同一个变量。根据您的建议,我相信在该函数中访问或修改的任何内容都是线程安全的。
答案 3 :(得分:1)
正如您所说counter++
操作是非原子的,因此一次授予多个线程访问权限将导致未定义的行为。在线程安全性方面,问题几乎总是对共享资源(如静态变量)进行同步访问。
线程在声明方法synchronized
时获取的锁属于该类。假设我们在一个类中有两个方法
public synchronized void foo() {...}
public synchronized void bar() {...}
如果一个帖子进入foo()
,它会获取该类的锁定,而任何其他尝试访问foo()
OR bar()
的帖子都会阻止,直到第一个线程已经完成。为了解决这个问题,我们可以锁定方法中的单独对象。
// Declare 2 objects to use as locks within the class
private Object fooLock = new Object();
private Objecy barLock = new Object();
// lock on these objects within the method bodies
public void foo {
synchronized(fooLock) { /* do foo stuff */ }
}
public void bar() {
synchronized(barLock) {/* do bar stuff */}
}
现在,2个主题可以同时访问foo()
和bar()
。
网上有很多关于线程安全的资料,如果你想了解更多关于使用锁/执行器服务和东西的多线程,我会推荐this套教程。