lock.tryLock()线程安全吗?

问题描述 投票:1回答:2

我尝试了带有Synchronization,lock.lock()和lock.tryLock()的线程竞赛程序,我发现使用同步和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实际输出:每次运行的随机数

java multithreading concurrency java-threads reentrantlock
2个回答
2
投票

tryLock是线程安全的,并且您面临的问题也不是竞争条件(即,原子地递增和递减计数器)。实际上,如果您查看tryLock方法的文档,那么了解程序的这种行为并不难。这是文档的摘录。

如果没有其他线程持有该锁,则获取该锁,并返回立即将其值设置为true,将锁定保持计数设置为1。如果当前线程已经拥有此锁,则保持计数为加一,该方法返回true。如果锁由另一个线程,则此方法将立即返回值错误。

因此,您每10000次调用递增和递减,并期望以0结尾。但是我们不知道这10000次尝试中有多少次可以成功。那是完全不确定的,并且在不同的运行中会得到不同的结果。但是,最好的方法是使用Atomic变量执行此操作。这是它的外观。

private static class Resources {
    private final LongAdder counter = new LongAdder();

    public int getCounter() {
        return counter.intValue();
    }

    public void increment() {
        counter.increment();
    }

    public void decrement() {
        counter.decrement();
    }
}

2
投票

tryLock方法是线程安全的。它可靠地完成了它应该做的事情(根据javadoc)。

但是,您使用trylock的方式导致incrementdecrement的非线程安全实现。如果要在这种情况下使用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 8及更高版本的java.util.concurrent.atomic.LongAdderjavadoc),
  • [java.util.concurrent.atomic.AtomicLongjavadoc)Java以来​​的所有Java版本。

显然,如果有很多争用,LongAdder的效果会更好:


请注意,线程安全实际上是很难精确定义的概念。您需要从算法的[[correct行为的行为规范开始。然后,您可以说该算法的实现。

定义:当在具有一个处理器的系统上运行时,如果算法符合规范是正确的,那么当有多个处理器时,如果根据规范也是

始终]正确,则它是线程安全的

由此,我们看到:

    如果在单处理器情况下算法不符合其行为规范,则线程安全是没有意义的,并且
  • 如果算法不能使用多个处理器,则线程安全是没有意义的。
© www.soinside.com 2019 - 2024. All rights reserved.