JavaRush /Java 博客 /Random-ZH /线程同步。Java中的同步运算符

线程同步。Java中的同步运算符

已在 Random-ZH 群组中发布
你好!今天我们继续考虑多线程编程的特点,讲讲线程同步。
线程同步。 操作员已同步 - 1
什么是“同步”?在编程领域之外,这指的是允许两个设备或程序一起工作的某种设置。例如,智能手机和计算机可以与谷歌帐户同步,网站上的个人帐户可以与社交网络上的帐户同步,以便使用它们登录。线程同步具有类似的含义:它设置线程之间如何交互。在之前的讲座中,我们的线程彼此分开生活和工作。一个在计数,第二个在睡觉,第三个在控制台上显示一些东西,但他们之间没有交互。在实际程序中,这种情况很少见。例如,多个线程可以主动处理同一组数据并更改其中的某些内容。这会产生问题。想象一下,多个线程正在将文本写入同一位置,例如文本文件或控制台。在这种情况下,该文件或控制台成为共享资源。线程不知道彼此的存在,因此它们只是在线程调度程序分配给它们的时间内写下它们可以管理的所有内容。在最近的课程讲座中,我们举了一个例子来说明这会导致什么结果,让我们记住这一点: 线程同步。 同步操作员 - 2原因在于线程使用共享资源(控制台),而彼此之间没有协调操作。如果线程调度程序已为 Thread-1 分配时间,它会立即将所有内容写入控制台。其他线程已经成功写入或尚未成功写入的内容并不重要。正如您所看到的,结果是灾难性的。因此,在多线程编程中,引入了一个特殊的概念互斥量(来自英文“mutex”,“互斥”——“互斥”)互斥体的目的是提供一种机制,以便在特定时间只有一个线程可以访问对象。如果 Thread-1 已获取对象 A 的互斥体,则其他线程将无法访问它来更改其中的任何内容。在对象 A 的互斥体被释放之前,剩余的线程将被迫等待。现实生活中的例子:假设您和其他 10 个陌生人正在参加培训。你们需要轮流表达想法和讨论某件事。但是,由于你们是第一次见面,为了不经常打断对方,也不至于陷入喧闹,你们采用了“说话球”规则:只有一个人可以说话——那个有球的人。他的手。这样,讨论就变得充分且富有成果。所以,互斥锁本质上就是这样一个球。如果一个对象的互斥锁在一个线程手中,其他线程将无法访问该对象。您不需要执行任何操作来创建互斥体:它已经内置到类中Object,这意味着 Java 中的每个对象都拥有它。

Java 中同步运算符的工作原理

让我们来熟悉一个新关键字——synchronized。它标记了我们代码的某一部分。如果一个代码块被标记为synchronized关键字,则意味着该代码块一次只能由一个线程执行。同步可以通过不同的方式实现。例如,创建一个完整的同步方法:
public synchronized void doSomething() {

   //...method logic
}
或者编写一段代码,在某个对象上执行同步:
public class Main {

   private Object obj = new Object();

   public void doSomething() {

       //...some logic available to all threads

       synchronized (obj) {

           //logic that is only available to one thread at a time
       }
   }
}
意思很简单。如果一个线程进入一个标有“synchronized”一词的代码块,它会立即获取该对象的互斥量,并且尝试进入同一块或方法的所有其他线程都将被迫等待,直到前一个线程完成其工作并释放该对象。监视器。 线程同步。 同步运算符 - 3顺便一提!在课程讲座中,您已经看到过同步的示例,但它们看起来有所不同:
public void swap()
{
   synchronized (this)
   {
       //...method logic
   }
}
这个主题对你来说是新的,当然一开始你会对语法感到困惑。所以,赶紧记住,以免以后写法的时候出现混乱。这两种写法的意思是一样的:
public void swap() {

   synchronized (this)
   {
       //...method logic
   }
}


public synchronized void swap() {

   }
}
在第一种情况下,您在进入方法后立即创建同步代码块。它由 object 同步this,即由当前对象同步。在第二个示例中,您将“synchronized”一词放在整个方法上。不再需要显式指示对其执行同步的任何对象。一旦整个方法被标记了一个单词,该方法将自动为该类的所有对象同步。我们先不去深入讨论哪种方法更好。现在,选择你最喜欢的:) 最重要的是要记住:只有当一个方法内部的所有逻辑同时由一个线程执行时,你才能声明一个同步方法。例如,在这种情况下,doSomething()使方法同步将是一个错误:
public class Main {

   private Object obj = new Object();

   public void doSomething() {

       //...some logic available to all threads

       synchronized (obj) {

           //logic that is only available to one thread at a time
       }
   }
}
正如您所看到的,该方法的一部分包含不需要同步的逻辑。其中的代码可以由多个线程同时执行,并且所有关键位置都分配给单独的同步块。一会儿。让我们仔细看看讲座中交换姓名的示例:
public void swap()
{
   synchronized (this)
   {
       //...method logic
   }
}
请注意:同步是使用 进行的this。也就是说,对于一个特定的对象MyClass。想象一下,我们有 2 个线程(Thread-1Thread-2)并且只有一个对象MyClass myClass。在这种情况下,如果Thread-1调用该方法myClass.swap(),则对象的互斥体将很忙,并且Thread-2当您尝试调用它时,myClass.swap()它将挂起等待互斥体变为空闲。如果我们有 2 个线程和 2 个对象MyClass-myClass1并且myClass2- 在不同的对象上,我们的线程可以轻松地同时执行同步方法。第一个线程执行以下操作:
myClass1.swap();
第二个的作用是:
myClass2.swap();
在这种情况下,方法内的synchronized关键字swap()不会影响程序的运行,因为同步是针对特定对象进行的。在后一种情况下,我们有 2 个对象,因此,线程不会给彼此带来问题。毕竟,两个对象有2个不同的互斥体,并且它们的捕获不相互依赖。

静态方法中同步的特点

但是如果您需要同步静态方法怎么办?
class MyClass {
   private static String name1 = "Olya";
   private static String name2 = "Lena";

   public static synchronized void swap() {
       String s = name1;
       name1 = name2;
       name2 = s;
   }

}
目前尚不清楚在这种情况下什么将充当互斥体。毕竟,我们已经决定每个对象都有一个互斥体。但问题是,要调用静态方法,MyClass.swap()我们不需要对象:方法是静态的!那么,下一步是什么?:/ 其实这个没有问题。Java 的创建者照顾了一切:) 如果包含关键“多线程”逻辑的方法是静态的,同步将由类执行。为了更清楚起见,上面的代码可以重写为:
class MyClass {
   private static String name1 = "Olya";
   private static String name2 = "Lena";

   public static void swap() {

       synchronized (MyClass.class) {
           String s = name1;
           name1 = name2;
           name2 = s;
       }
   }

}
原则上,您可以自己想到这一点:由于没有对象,因此同步机制必须以某种方式“硬连线”到类本身中。就是这样:您还可以跨类同步。
评论
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION