Java 同步
同步是通过多线程请求处理资源可访问性的过程。同步的主要目的是避免线程干扰。当多个线程试图访问共享资源时,我们需要确保一次只有一个线程使用资源。实现这一点的过程称为同步。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。