如何知道方法是否是线程安全的

use*_*115 6 java

假设我有一个检查db中的id的方法,如果该id没有退出,则插入具有该id的值。我如何知道这是否是线程安全的,以及如何确保其线程安全。我是否可以使用任何一般规则来确保它不包含竞争条件,并且通常是线程安全的。

public TestEntity save(TestEntity entity) {
       if (entity.getId() == null) {
            entity.setId(UUID.randomUUID().toString());
        }
        Map<String, TestEntity > map = dbConnection.getMap(DB_NAME);
        map.put(entity.getId(), entity);
        return map.get(entity.getId());
}
Run Code Online (Sandbox Code Playgroud)

GMc*_*GMc 2

这是一道线有多长的问题...

如果一个方法在声明中使用synchronized关键字,那么它就是线程安全的。

然而,即使你的setId和getId方法使用了synchronized关键字,你上面设置id的过程(如果它之前没有被初始化)也不是。..但即便如此,这个问题仍然有一个“视情况而定”的方面。如果两个线程不可能使用未初始化的 id 获取相同的对象,那么您就是线程安全的,因为您永远不会尝试同时修改 id。

鉴于您问题中的代码,完全有可能对同一个对象同时调用线程安全 getid 两次。它们一一获得返回值(null)并立即被抢占以让另一个线程运行。这意味着两者都将再次一一运行线程安全的 setId 方法。

您可以将整个 save 方法声明为同步,但如果这样做,整个方法将是单线程的,这首先就违背了使用线程的目的。您往往希望将同步代码减少到最低限度,以最大限度地提高并发性。

您还可以在关键 if 语句周围放置一个同步块,并最大限度地减少处理的单线程部分,但是您还需要小心代码的其他部分是否也可能设置 Id(如果不是)之前已初始化。

另一种有各种优点和缺点的可能性是将 Id 的初始化放入 get 方法中并使该方法同步,或者在构造函数中创建对象时简单地分配 Id。

我希望这有帮助...

编辑...上面讲的是java语言的特性。有几个人提到了 java 类库中的设施(例如 java.util.concurrent),它们也提供了对并发的支持。所以这是一个很好的补充,但也有完整的包以各种方式解决并发和其他相关的并行编程范例(例如并行性)。

为了完成这个列表,我将添加AkkaCats-effect(并发)等工具。

更不用说专门讨论该主题的书籍和课程了。

我刚刚重读了您的问题并注意到您在询问数据库。答案又是视情况而定。Rdbms 通常允许您在事务中使用记录锁来执行此类操作。有些(如 Teradata)使用特殊子句,例如将rowhashlocking row for write select * from some table where pi_cols = 'somevalues'锁定给您,直到您更新它或某些其他条件。这称为悲观锁定。

其他的(特别是 nosql)有乐观锁定。这是当您读取记录时(就像您使用 getid 暗示的那样),没有机会锁定记录。然后您进行条件更新。条件更新有点像这样:write the id as x provided that when you try to do so the Id is still null (or whatever the value was when you checked)。这些类型的操作通常通过 API 进行。

您还可以在 RDBM 中进行乐观锁定,如下所示: SQL Update tbl Set x = 'some value', Last_update_timestamp = current_timestamp() Where x = bull AND last_update_timestamp = 'same value as when I last checked' 在本示例中,where 子句的第二部分是关键位,它基本上表示“仅在没有其他人这样做的情况下才更新记录,并且我相信其他人都会更新最后的更新”当他们这样做时”。“信任”位有时可以用触发器代替。

数据库引擎保证这些类型的数据库操作(如果可用)是“线程安全的”。

这让我回到了这个答案开头的“一根绳子有多长”的观察结果......

  • 抱歉,“同步”并不是使方法线程安全的唯一方法,也不是普遍最好的方法。底层方法可能已经同步,使用其他原语(例如可重入读/写锁),具有无锁线程安全性,或者与具有每线程连接且外部一致的数据库进行交互。 (4认同)
  • @user3310115我无法真正详细说明,因为完整的讨论线程安全编程不适合评论甚至答案,但是“java.util.concurrent”中有一些功能允许使用构建块构建线程安全程序除了简单的互斥(例如同步块)。当然,在方法上进行同步是实现线程安全的一种廉价而快速的方法,但它不是最有效的......但您可以首先考虑可能发生的所有可能的并发修改,看看是否有任何有害的修改。 (4认同)
  • 说真的,伙计们,有关于如何在 Java 中进行(线程安全)并发编程的完整书籍。你可以将其浓缩为 StackOverflow 问答……更不用说一两条评论了……这种想法是异想天开。 (2认同)