Java 同步

原文:https://www.studytonight.com/java/synchronization.php

同步是通过多线程请求处理资源可访问性的过程。同步的主要目的是避免线程干扰。当多个线程试图访问共享资源时,我们需要确保一次只有一个线程使用资源。实现这一点的过程称为同步。java 中的 synchronization 关键字创建了一个称为临界区的代码块。

一般语法:

synchronized (object)
{
  //statement to be synchronized
}

每个具有关键代码段的 Java 对象都会获得一个与该对象相关联的锁。要进入临界区,线程需要获得相应对象的锁。

为什么我们需要同步?

如果我们不使用同步,让两个或多个线程同时访问一个共享资源,会导致结果失真。

举个例子,假设我们有两个不同的线程 T1T2 ,T1 开始执行,并将某些值保存在一个文件临时. txt 中,当 T1 返回时,该文件将用于计算一些结果。同时,T2 开始,在 T1 返回之前,T2 更改 T1 保存在文件临时. txt 中的值(临时. txt 是共享资源)。现在显然 T1 会返回错误的结果。

为了防止此类问题,引入了同步。有了上述情况下的同步,一旦 T1 开始使用临时. txt 文件,该文件将被锁定(锁定模式),在 T1 返回之前,其他线程无法访问或修改。

使用同步方法

使用同步方法是实现同步的一种方式。但是让我们先看看当我们在程序中不使用同步时会发生什么。

没有同步的示例

在这个例子中,我们没有使用同步并创建多个线程来访问显示方法并产生随机输出。

class First
{
  public void display(String msg)
  {
    System.out.print ("["+msg);
    try
    {
      Thread.sleep(1000);
    }
    catch(InterruptedException e)
    {
      e.printStackTrace();
    }
    System.out.println ("]");
  }
}

class Second extends Thread
{
  String msg;
  First fobj;
  Second (First fp,String str)
  {
    fobj = fp;
    msg = str;
    start();
  }
  public void run()
  {
    fobj.display(msg);
  }
}

public class Syncro
{
  public static void main (String[] args)
  {
    First fnew = new First();
    Second ss = new Second(fnew, "welcome");
    Second ss1= new Second(fnew,"new");
    Second ss2 = new Second(fnew, "programmer");
  }
}

[欢迎[新[程序员] ] ]

在上面的程序中,类 First 的对象 fnew 被所有三个运行线程(ss、ss1 和 ss2)共享,以调用共享方法(void 显示)。因此结果是不同步的,这种情况被称为比赛状态..

同步关键字

要同步上述程序,我们必须同步对共享的 display() 方法的访问,使其一次只对一个线程可用。这是通过使用关键字与 display()方法同步来完成的。

synchronized void display (String msg)

示例:同步方法的实现

class First
{
  synchronized public void display(String msg)
  {
    System.out.print ("["+msg);
    try
    {
      Thread.sleep(1000);
    }
    catch(InterruptedException e)
    {
      e.printStackTrace();
    }
    System.out.println ("]");
  }
}

class Second extends Thread
{
  String msg;
  First fobj;
  Second (First fp,String str)
  {
    fobj = fp;
    msg = str;
    start();
  }
  public void run()
  {
    fobj.display(msg);
  }
}

public class MyThread
{
  public static void main (String[] args)
  {
    First fnew = new First();
    Second ss = new Second(fnew, "welcome");
    Second ss1= new Second(fnew,"new");
    Second ss2 = new Second(fnew, "programmer");
  }
}

[欢迎][程序员][新]

使用同步块

如果想要同步对一个类的对象的访问或者只同步一个方法的一部分,那么我们可以使用同步块。它能够使对象和方法的任何部分同步。

例子

在这个例子中,我们使用了同步块,这将使显示方法一次可用于单线程。

  class First
{
  public void display(String msg)
  {
    System.out.print ("["+msg);
    try
    {
      Thread.sleep(1000);
    }
    catch(InterruptedException e)
    {
      e.printStackTrace();
    }
    System.out.println ("]");
  }
}

class Second extends Thread
{
  String msg;
  First fobj;
  Second (First fp,String str)
  {
    fobj = fp;
    msg = str;
    start();
  }
  public void run()
  {
    synchronized(fobj)      //Synchronized block
    {
      fobj.display(msg);
    }
  }
}

public class MyThread
{
  public static void main (String[] args)
  {
    First fnew = new First();
    Second ss = new Second(fnew, "welcome");
    Second ss1= new Second (fnew,"new");
    Second ss2 = new Second(fnew, "programmer");
  }
}

[欢迎][新][程序员]

由于同步块,这个程序给出了预期的输出。

同步关键字和同步块之间的区别

当我们对一个方法使用 synchronized 关键字时,它会为整个方法获取对象中的锁。这意味着在调用了同步方法的当前线程完成执行之前,没有其他线程可以使用任何同步方法。

synchronized 块仅在 synchronized 关键字后的括号之间获取对象中的锁。这意味着在同步块退出之前,没有其他线程可以获取锁定对象的锁。但是其他线程可以访问该方法的其余代码。

同步方法和同步块哪个更好?

在 Java 中,synchronized 关键字会导致性能损失。Java 中的同步方法非常慢,会降低性能。因此,我们必须在必要时使用 java 中的同步关键字,否则,我们应该使用仅用于同步关键部分的 Java synchronized block。