CodeGym /Java 博客 /随机的 /线程同步。同步运算符
John Squirrels
第 41 级
San Francisco

线程同步。同步运算符

已在 随机的 群组中发布
你好!今天我们将继续考虑多线程编程的特性并讨论线程同步。 线程同步。 同步运算符 - 1

Java 中的同步是什么?

在编程领域之外,它意味着允许两个设备或程序一起工作的安排。例如,智能手机和计算机可以与 Google 帐户同步,网站帐户可以与社交网络帐户同步,以便您可以使用它们进行登录。线程同步具有类似的含义:它是线程与线程交互的一种安排彼此。在之前的课程中,我们的线程彼此分开生活和工作。一个执行计算,第二个睡觉,第三个在控制台上显示一些东西,但他们没有互动。在实际程序中,这种情况很少见。多个线程可以主动处理和修改同一个数据集。这会产生问题。想象一下,多个线程将文本写入同一个地方,例如,文本文件或控制台。在这种情况下,文件或控制台成为共享资源。线程不知道彼此的存在,因此它们只是在线程调度程序分配给它们的时间内写入它们可以写入的所有内容。在最近的一节课中,我们看到了一个例子,说明了这会导致什么结果。现在让我们回忆一下: 线程同步。 同步运算符 - 2原因在于线程正在使用共享资源(控制台),而没有相互协调它们的操作。如果线程调度程序将时间分配给 Thread-1,它会立即将所有内容写入控制台。其他线程已经或尚未设法写入的内容无关紧要。如您所见,结果令人沮丧。这就是为什么他们在多线程编程中引入了一个特殊的概念,互斥量(互斥)互斥量的目的就是提供一种机制,让某个时间只有一个线程可以访问一个对象。如果 Thread-1 获得对象 A 的互斥锁,其他线程将无法访问和修改该对象。其他线程必须等到对象 A 的互斥体被释放。这是生活中的一个例子:假设您和其他 10 个陌生人正在参加一项练习。轮流,你需要表达你的想法并讨论一些事情。但是因为你们是第一次见面,为了不经常打断对方而大发雷霆,你们使用了“会说话的球”:只有拿球的人才能说话。通过这种方式,您最终会进行良好而富有成果的讨论。本质上,球是一个互斥量。如果一个对象的互斥锁在一个线程手中,其他线程就不能使用该对象。Object类,这意味着 Java 中的每个对象都有一个。

同步运算符是如何工作的

让我们认识一个新的关键字:synchronized。它用于标记某个代码块。如果代码块标有synchronized关键字,则该代码块一次只能由一个线程执行。可以用不同的方式实现同​​步。例如,通过声明要同步的整个方法:

public synchronized void doSomething() {

   // ...Method logic
}
或者编写一个代码块,其中使用某个对象执行同步:

public class Main {

   private Object obj = new Object();

   public void doSomething() {

       // ...Some logic available simultaneously to all threads

       synchronized (obj) {

           // Logic available to just one thread at a time
       }
   }
}
意思很简单。如果一个线程进入标有synchronized关键字的代码块,它会立即捕获对象的互斥体,所有其他试图进入同一块或方法的线程都被迫等待,直到前一个线程完成其工作并释放监视器。 线程同步。 同步运算符 - 3顺便一提!在课程中,您已经看过 的示例synchronized,但它们看起来有所不同:

public void swap()
{
   synchronized (this)
   {
       // ...Method logic
   }
}
这个话题对你来说是新的。而且,当然,语法上会有混淆。因此,请立即记住它,以免以后因不同的书写方式而感到困惑。这两种写法意思是一样的:

public void swap() {

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


public synchronized void swap() {

   }
}
在第一种情况下,您将在进入方法后立即创建一个同步代码块。它由对象同步this,即当前对象。在第二个示例中,您将synchronized关键字应用于整个方法。这使得无需显式指示用于同步的对象。由于整个方法都标有关键字,因此该方法将自动为类的所有实例同步。我们不会深入讨论哪种方式更好。现在,选择你最喜欢的方式:) 最主要的是要记住:只有当它的所有逻辑一次由一个线程执行时,你才能声明一个方法同步。例如,将以下方法同步是错误的doSomething()

public class Main {

   private Object obj = new Object();

   public void doSomething() {

       // ...Some logic available simultaneously to all threads

       synchronized (obj) {

           // Logic available to just one thread at a time
       }
   }
}
如您所见,部分方法包含不需要同步的逻辑。该代码可以同时由多个线程运行,并且所有关键位置都设置在一个单独的synchronized块中。还有一件事。让我们仔细检查名称交换课程中的示例:

public void swap()
{
   synchronized (this)
   {
       // ...Method logic
   }
}
注意:同步是使用this. 即,使用特定的MyClass对象。假设我们有 2 个线程(Thread-1Thread-2)并且只有一个MyClass myClass对象。在这种情况下,如果Thread-1调用该myClass.swap()方法,对象的互斥量将处于忙碌状态,并且在尝试调用该myClass.swap()方法Thread-2将挂起,等待互斥量被释放。如果我们有 2 个线程和 2 个MyClass对象(myClass1myClass2),我们的线程可以轻松地同时对不同对象执行同步方法。第一个线程执行这个:

myClass1.swap();
第二个执行这个:

myClass2.swap();
在这种情况下,方法synchronized中的关键字swap()不会影响程序的运行,因为同步是使用特定对象执行的。在后一种情况下,我们有 2 个对象。因此,线程不会互相造成问题。毕竟,两个对象有 2 个不同的互斥量,获取一个与获取另一个是独立的

静态方法中同步的特殊功能

但是如果你需要同步一个静态方法呢?

class MyClass {
   private static String name1 = "Ally";
   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 = "Ally";
   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