我尝试了使用Synchronization,lock.lock((和lock.tryLock((的线程竞赛程序,我发现使用sync和lock.lock((工作正常,但是lock.tryLock((本身不是线程安全的。此方法无法获取锁,因此会产生意外结果
import java.util.concurrent.locks.ReentrantLock;
public class Main {
public static void main(String[] args) throws InterruptedException {
for (int i = 0; i < 10; i++) {
Resources resources = new Resources();
Thread userThread1 = new Increment(resources);
Thread userThread2 = new Decrement(resources);
userThread1.start();
userThread2.start();
userThread1.join();
userThread2.join();
System.out.println(resources.getCounter());
}
}
private static abstract class UserThread extends Thread {
protected Resources resources;
public UserThread(Resources resources) {
this.resources = resources;
}
}
private static class Increment extends UserThread {
public Increment(Resources resources) {
super(resources);
}
public void run() {
for (int i = 0; i < 10000; i++) {
resources.increemnt();
}
}
}
private static class Decrement extends UserThread {
public Decrement(Resources resources) {
super(resources);
}
public void run() {
for (int i = 0; i < 10000; i++) {
resources.decrement();
}
}
}
private static class Resources {
public ReentrantLock getLock() {
return lock;
}
private ReentrantLock lock = new ReentrantLock();
public int getCounter() {
return counter;
}
private int counter = 0;
public void increemnt() {
if (lock.tryLock()) {
try {
counter++;
} finally {
lock.unlock();
}
}
}
public void decrement() {
if (lock.tryLock()) {
try {
counter--;
} finally {
lock.unlock();
}
}
}
}
}
预期: 0,0,0,0,0,0,0,0,0,0,0实际输出:每次运行时的随机输出
tryLock
方法是线程安全的。 它可靠地完成它应该做的事情(根据javadoc
(。但是,使用trylock
的方式会导致increment
和decrement
的非线程安全实现。 如果您打算在这种情况下使用trylock
,则需要执行以下操作:
try {
while (!lock.tryLock()) { } // Try to get the lock until you get it
counter++;
} finally {
lock.unlock();
}
但这是一个坏主意,因为您实际上正忙于等待锁。 使用Lock
更好的解决方案是:
try {
lock.lock(); // Block until the lock is acquired.
counter++;
} finally {
lock.unlock();
}
但是,如果您追求的是无锁解决方案,那么您应该使用原子类型:
java.util.concurrent.atomic.LongAdder
(javadoc( for Java 8 及更高版本,java.util.concurrent.atomic.AtomicLong
(javadoc(自Java 5(包括Java 5(以来的所有Java版本。
显然,如果存在大量争用,LongAdder
表现更好:
- LongAdder 的性能如何优于 AtomicLong
请注意,线程安全实际上是一个难以精确定义的概念。 您需要从算法正确行为的行为规范开始。 然后你可以说是算法的实现。
定义:当算法在具有一个处理器的系统上运行时根据规范是正确的,那么当有多个处理器时,如果根据规范也始终正确,则该算法是线程安全的。
由此,我们看到:
- 如果算法在单处理器情况下不符合其行为规范,则线程安全是没有意义的,并且
- 如果算法不能使用多个处理器,则线程安全是没有意义的。