Java 同步

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

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

一般语法:

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

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

为什么我们需要同步?

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

举个例子,假设我们有两个不同的线程 T1 和 T2 ,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。