繁体   English   中英

多线程:通过锁定同步

[英]Multithreading: synchronize by locking

并发采集:

ConcurrentMap<LocalDate, A> ex = new ConcurrentHashMap<>();

class A {
   AtomicLong C;
   AtomicLong D
}

如何通过锁定“C”和“D”来同步? 也就是说,我需要同时更改“C”和“B”,并保证当我更改另一个时,第一个不会因外部操作而改变。 谢谢你。

你在解决什么

您正在描述您想要:

  • 允许调用者修改 object 上的某些内容
  • 防止任何其他调用者(其他线程)同时修改内容

解决方案说明

此解决方案使用synchronized ,尽管 Java 中提供了许多其他机制来支持这一点(其中一些在 Java 教程的锁定对象部分中介绍)。

“同步”的工作方式是使用“同步”关键字指定一些代码,以及要同步的 object。 当您的代码运行时,JVM 将保证,对于所有同步的代码——在同一个 object 上——一次只能运行一个线程。


您可以制作一个同步代码块,如下所示。 注意:这定义了一个名为“lock”的Object ,但它只是在阅读代码时为了清晰起见而选择的名称——你可以随意命名。

Object lock;
synchronized (lock) {
    ... // all things here run only when "lock" is available
}

您还可以将整个方法指定为同步,如下所示:

public void synchronized print() {
    System.out.println("hello");
}

第二个示例的行为与第一个示例类似——它还锁定了 object——但乍一看并不清楚 object 是什么; 也就是说,JVM 如何知道要在哪个 object 上同步? 如果在 object 实例上调用方法本身,则此方法有效,并且在这种情况下,锁变为this 我将在下面展示一个示例。

Java 教程中有很好的信息关于同步方法


解决方案#1:使用Object lock同步块

以下是关于 class AllowOneEditAtATime的一些注意事项:

  • 它有两个私人成员: onetwo
  • 因为它们是私有的,所以不能直接更改——所以不允许这样做:
     AllowOneEditAtATime a = new AllowOneEditAtATime(); a.one = new AtomicLong(1); // cannot change "one" directly because it is private
  • 定义了private Object lock ——这意味着两个不同的同步代码块将锁定。 在同一个 object 上同步不同的代码块是完全可以的。 这是您所追求的主要技术。
  • setOne()中使用同步块,在“锁定”上同步
  • 在另一个方法中使用另一个同步块 - setTwo() - 也在“锁定”上同步
  • 因为setOne()setTwo()都在同一个 object 上同步,所以一次将允许其中一个运行
class AllowOneEditAtATime1 {

    private Object lock;
    private AtomicLong one;
    private AtomicLong two;

    public void setOne(AtomicLong newOne) {
        synchronized (lock) {
            one = newOne;
        }
    }

    public void setTwo(AtomicLong newTwo) {
        synchronized (lock) {
            two = newTwo;
        }
    }
}

解决方案#2:同步块,使用this

解决方案#1 可以正常工作,但没有必要(在这种情况下)创建整个 object 只是为了锁定。 相反,您可以相信此代码仅在有人调用new AllowOneEditAtATime()之后运行,这意味着始终存在一个 object 实例,这意味着您可以在代码内部使用this this关键字指的是 object 实例本身,即AllowOneEditAtATime的实际实例。

所以这里有一个使用this的变体(不再是Object lock ):

class AllowOneEditAtATime2 {

    private AtomicLong one;
    private AtomicLong two;

    public void setOne(AtomicLong newOne) {
        synchronized (this) {
            one = newOne;
        }
    }

    public void setTwo(AtomicLong newTwo) {
        synchronized (this) {
            two = newTwo;
        }
    }
}

解决方案#3:同步方法,隐式锁

解决方案#2 工作正常,但由于我们使用this作为锁,并且代码路径适合这样做,我们可以使用同步方法而不是同步代码块。

这意味着我们可以替换它:

public void setTwo(AtomicLong newTwo) {
    synchronized (this) {
        two = newTwo;
    }
}

有了这个:

public synchronized void setOne(AtomicLong newOne) {
    one = newOne;
}

在幕后,整个setOne()方法在this上自动同步,因此根本不需要包含synchronized (this) {.. } 在解决方案 #2 中,两种方法都这样做,因此都可以替换。 通过同步这两种方法,它们都将在 object 实例 ( this ) 上同步,这与解决方案 #2 类似,但代码更少。

class AllowOneEditAtATime3 {

    private AtomicLong one;
    private AtomicLong two;

    public synchronized void setOne(AtomicLong newOne) {
        one = newOne;
    }

    public synchronized void setTwo(AtomicLong newTwo) {
        two = newTwo;
    }
}

上述任何一个都可以工作,其他同步机制也可以。 与所有事情一样,有多种方法可以解决问题。

如需进一步阅读, 并发课程(在 Java 教程中)有很好的信息,可能值得您花时间阅读。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM