Java中的方法和块同步

线程主要通过共享对字段和引用字段引用的对象的访问来进行通信。这种形式的通信非常有效,但会导致两种错误:线程干扰和内存一致性错误。需要一些同步结构来防止这些错误。下面的示例显示了需要同步的情况。

null

需要同步

考虑下面的例子:

// Java program to illustrate need
// of Synchronization
import java.io.*;
class Multithread
{
private int i = 0 ;
public void increment()
{
i++;
}
public int getValue()
{
return i;
}
}
class GfG
{
public static void main (String[] args)
{
Multithread t = new Multithread();
t.increment();
System.out.println(t.getValue());
}
}


输出:

1

在上述示例中,执行三个操作:

  1. 获取变量i的值。
  2. 增加获取的值。
  3. 并将增加的i值存储到其位置。

在这里

  • 第一个线程获取i的值(当前值i为0)并将其增加1,因此变量i的值变为1。
  • 现在,第二个线程访问i的值,该值将为0,因为第一个线程没有将其存储回其位置。 第二个线程也会增加它,并将其存储回其位置。而且还要储存起来。
  • 变量i的最终值为1。但这两个线程的作用应该是2。这就是为什么我们需要同步对共享变量i的访问。

Java是多线程语言,多个线程并行运行以完成它们的执行。我们需要同步共享资源,以确保一次只有一个线程能够访问共享资源。 如果一个对象由多个线程共享,那么就需要进行同步,以避免对象的状态被破坏。当对象是可变的时,需要同步。如果共享对象是不可变的,或者共享同一对象的所有线程只读取对象的状态,而不进行修改,则不需要对其进行同步。

Java编程语言提供了两种同步习惯用法:

  • 方法同步
  • 语句同步(块同步)

方法同步

同步方法提供了一种简单的策略来防止线程干扰和内存一致性错误。如果一个对象对多个线程可见,则对该对象字段的所有读取或写入都通过 同步的 方法

同步方法的两次调用不可能交错。如果一个线程正在执行synchronized方法,那么在同一个对象上调用synchronized方法的所有其他线程都必须等待,直到第一个线程处理完该对象。

示例:这显示了如果多个线程在不同步的情况下访问getLine()方法。

// Example illustrates multiple threads are executing
// on the same Object at same time without synchronization.
import java.io.*;
class Line
{
// if multiple threads(trains) will try to
// access this unsynchronized method,
// they all will get it. So there is chance
// that Object's  state will be corrupted.
public void getLine()
{
for ( int i = 0 ; i < 3 ; i++)
{
System.out.println(i);
try
{
Thread.sleep( 400 );
}
catch (Exception e)
{
System.out.println(e);
}
}
}
}
class Train extends Thread
{
// reference to Line's Object.
Line line;
Train(Line line)
{
this .line = line;
}
@Override
public void run()
{
line.getLine();
}
}
class GFG
{
public static void main(String[] args)
{
// Object of Line class that is shared
// among the threads.
Line obj = new Line();
// creating the threads that are
// sharing the same Object.
Train train1 = new Train(obj);
Train train2 = new Train(obj);
// threads start their execution.
train1.start();
train2.start();
}
}


输出

0
0
1
1
2
2

可能有两列火车(多于两列)需要同时使用同一列火车,因此有发生碰撞的可能性。因此,为了避免冲突,我们需要同步多个应用程序要运行的行。

示例:同步访问同一对象上的getLine()方法

// Example that shows multiple threads
// can execute the same method but in
// synchronized way.
class Line
{
// if multiple threads(trains) trying to access
// this synchronized method on the same Object
// but only one thread will be able
// to execute it at a time.
synchronized public void getLine()
{
for ( int i = 0 ; i < 3 ; i++)
{
System.out.println(i);
try
{
Thread.sleep( 400 );
}
catch (Exception e)
{
System.out.println(e);
}
}
}
}
class Train extends Thread
{
// Reference variable of type Line.
Line line;
Train(Line line)
{
this .line = line;
}
@Override
public void run()
{
line.getLine();
}
}
class GFG
{
public static void main(String[] args)
{
Line obj = new Line();
// we are creating two threads which share
// same Object.
Train train1 = new Train(obj);
Train train2 = new Train(obj);
// both threads start executing .
train1.start();
train2.start();
}
}


输出:

0
1
2
0
1
2

块同步

如果我们只需要执行一些后续的代码行,而不是方法中的所有代码行(指令),那么我们应该只同步存在所需指令的代码块。 例如,假设有一个方法包含100行代码,但只有10行(一行接一行)代码包含关键代码部分,即这些行可以修改(更改)对象的状态。因此,我们只需要同步这10行代码方法,以避免对象状态中的任何修改,并确保其他线程可以在同一方法中执行其余的代码行,而不会出现任何中断。

import java.io.*;
import java.util.*;
public class Geek
{
String name = "" ;
public int count = 0 ;
public void geekName(String geek, List<String> list)
{
// Only one thread is permitted
// to change geek's name at a time.
synchronized ( this )
{
name = geek;
count++; // how many threads change geek's name.
}
// All other threads are permitted
// to add geek name into list.
list.add(geek);
}
}
class GFG
{
public static void main (String[] args)
{
Geek gk = new Geek();
List<String> list = new ArrayList<String>();
gk.geekName( "mohit" , list);
System.out.println(gk.name);
}
}


输出:

mohit

要点:

  • 当线程进入同步方法或块时,它会获得锁,一旦完成任务并退出同步方法,它就会释放锁。
  • 当线程进入同步实例方法或块时,它获取对象级锁;当线程进入同步静态方法或块时,它获取类级锁。
  • 如果同步块中使用的对象为null,Java同步将引发null指针异常。例如,如果在 已同步(实例) , 例子 如果为null,则会引发null指针异常。
  • 在爪哇, wait()、notify()和notifyAll() 是同步中使用的重要方法。
  • 你不能应用java 同步的 关键字和变量。
  • 不要在同步块上的非最终字段上同步,因为对非最终字段的引用可能会随时更改,然后不同的线程可能会在不同的对象上同步,即根本不同步。

优势

  • 多线程: 由于java是多线程语言,同步是在共享资源上实现互斥的好方法。
  • 实例和静态方法: 同步实例方法和同步静态方法都可以并发执行,因为它们用于锁定不同的对象。

局限性

  • 并发限制: Java同步不允许并发读取。
  • 降低效率: Java synchronized方法运行速度非常慢,可能会降低性能,所以在绝对必要时应该同步该方法,否则就不同步,并且只对代码的关键部分同步块。

本文由 尼茨海伦德拉 .如果你喜欢GeekSforgek,并想贡献自己的力量,你也可以使用 贡献极客。组织 或者把你的文章寄到contribute@geeksforgeeks.org.看到你的文章出现在Geeksforgeks主页上,并帮助其他极客。

如果您发现任何不正确的地方,或者您想分享有关上述主题的更多信息,请写下评论。

© 版权声明
THE END
喜欢就支持一下吧
点赞12 分享