ホームページ  >  記事  >  Java  >  Java における同期とは何ですか?

Java における同期とは何ですか?

WBOY
WBOYオリジナル
2024-08-30 16:18:111172ブラウズ

Java の同期は、複数のスレッドが共通の共有リソースに同時にアクセスしようとすることを制限する Java の機能です。ここでの共有リソースは、外部ファイルの内容、クラス変数、またはデータベース レコードを指します。

無料ソフトウェア開発コースを始めましょう

Web 開発、プログラミング言語、ソフトウェア テスト、その他

同期はマルチスレッド プログラミングで広く使用されています。 「同期」は、その期間中、他のスレッドからの干渉なしに 1 つのスレッドのみが動作できるようにする機能をコードに提供するキーワードです。

Java で同期が必要なのはなぜですか?

  • Java はマルチスレッド プログラミング言語です。これは、タスクの完了に向けて 2 つ以上のスレッドを同時に実行できることを意味します。スレッドが同時に実行されると、コードが予期しない結果をもたらすシナリオが発生する可能性が高くなります。
  • マルチスレッドが誤った出力を引き起こす可能性があるのなら、なぜそれが Java の重要な機能とみなされているのかと疑問に思われるかもしれません。
  • マルチスレッドは、複数のスレッドを並行して実行することでコードを高速化し、コードの実行時間を短縮し、高いパフォーマンスを提供します。ただし、マルチスレッド環境を利用すると、一般に競合状態として知られる状態により、不正確な出力が発生します。

競合状態とは何ですか?

2 つ以上のスレッドが並行して実行されると、その時点で共有リソースにアクセスして変更する傾向があります。スレッド スケジューリング アルゴリズムは、スレッドが実行される順序を決定します。

このため、スレッド スケジューラが単独で制御するため、スレッドが実行される順序を予測することはできません。これはコードの出力に影響し、出力の一貫性が失われます。複数のスレッドが操作を完了するために互いに競合しているため、この状態は「競合状態」と呼ばれます。

たとえば、以下のコードを考えてみましょう:

class Modify:
package JavaConcepts;
public class Modify implements Runnable{
private int myVar=0;
public int getMyVar() {
return myVar;
}
public void setMyVar(int myVar) {
this.myVar = myVar;
}
public void increment() {
myVar++;
}
@Override
public void run() {
// TODO Auto-generated method stub
this.increment();
System.out.println("Current thread being executed "+ Thread.currentThread().getName() + "Current Thread value " + this.getMyVar());
}
}
class RaceCondition:
package JavaConcepts;
public class RaceCondition {
public static void main(String[] args) {
Modify mObj = new Modify();
Thread t1 = new Thread(mObj, "thread 1");
Thread t2 = new Thread(mObj, "thread 2");
Thread t3 = new Thread(mObj, "thread 3");
t1.start();
t2.start();
t3.start();
}
}

上記のコードを連続して実行すると、出力は次のようになります:

私たちの出力 1:

実行中の現在のスレッド thread 1 現在のスレッド値 3

実行中の現在のスレッド thread 3 現在のスレッド値 2

実行中の現在のスレッド thread 2 現在のスレッド値 3

出力 2:

実行中の現在のスレッド thread 3 現在のスレッド値 3

実行中の現在のスレッド thread 2 現在のスレッド値 3

実行中の現在のスレッド thread 1 現在のスレッド値 3

出力 3:

実行中の現在のスレッド thread 2 現在のスレッド値 3

実行中の現在のスレッド thread 1 現在のスレッド値 3

実行中の現在のスレッド thread 3 現在のスレッド値 3

出力 4:

実行中の現在のスレッド thread 1 現在のスレッド値 2

実行中の現在のスレッド thread 3 現在のスレッド値 3

実行中の現在のスレッド thread 2 現在のスレッド値 2

Java における同期とは何ですか?

  • 上記の例から、スレッドはランダムに実行されており、値も正しくないと結論付けることができます。私たちのロジックによれば、値は 1 ずつ増加するはずです。ただし、ここでの出力値は、ほとんどの場合 3 で、少数の場合は 2 になります。
  • ここで、「myVar」変数は、複数のスレッドが実行されている共有リソースです。スレッドは同時に「myVar」の値にアクセスし、変更しています。他の 2 つのスレッドをコメントアウトするとどうなるかを見てみましょう。

Java における同期とは何ですか?

この場合の出力は次のとおりです:

実行中の現在のスレッド thread 1 現在のスレッド値 1

これは、単一スレッドが実行されている場合、出力は期待どおりであることを意味します。ただし、複数のスレッドが実行されている場合、値は各スレッドによって変更されます。したがって、共有リソース上で動作するスレッドの数を、一度に 1 つのスレッドに制限する必要があります。これは同期を使用して実現されます。

Understanding What is Synchronization in Java

  • Synchronization in Java is achieved with the help of the keyword “synchronized”. This keyword can be used for methods or blocks, or objects but cannot be used with classes and variables. A synchronized piece of code allows only one thread to access and modify it at a given time.
  • However, a synchronized piece of code affects code performance as it increases the waiting time of other threads trying to access it. So a piece of code should be synchronized only when there is a chance for a race condition to occur. If not, one should avoid it.

How does Synchronization in Java work internally?

  • Internally synchronization in Java has been implemented with the help of the lock (also known as a monitor) concept. Every Java object has its own lock. In a synchronized block of code, a thread needs to acquire the lock before being able to execute that particular block of code. Once a thread acquires the lock, it can execute that piece of code.
  • On completion of execution, it automatically releases the lock. If another thread requires to operate on the synchronized code, it waits for the current thread operating on it to release the lock. This process of acquiring and releasing locks is internally taken care of by the Java virtual machine. A program is not responsible for acquiring and release of locks by the thread. The remaining threads can, however, execute any other non-synchronized piece of code simultaneously.

Let us synchronize our previous example by synchronizing the code inside the run method using the synchronized block in class “Modify” as below:

class Modify:
package JavaConcepts;
public class Modify implements Runnable{
private int myVar=0;
public int getMyVar() {
return myVar;
}
public void setMyVar(int myVar) {
this.myVar = myVar;
}
public void increment() {
myVar++;
}
@Override
public void run() {
// TODO Auto-generated method stub
synchronized(this) {
this.increment();
System.out.println("Current thread being executed "
+ Thread.currentThread().getName() + " Current Thread value " + this.getMyVar());
}
}
}

The code for the class “RaceCondition” remains the same. Now on running the code, the output is as follows:

Output1:

The current thread being executed thread 1 Current Thread value 1

The current thread being executed thread 2 Current Thread value 2

The current thread being executed thread 3 Current Thread value 3

Output2:

The current thread being executed thread 1 Current Thread value 1

The current thread being executed thread 3 Current Thread value 2

The current thread being executed thread 2 Current Thread value 3

Java における同期とは何ですか?

Notice that our code is providing the expected output. Here every thread is incrementing the value by 1 for the variable “myVar” (in class “Modify”).

Note: Synchronization is required when multiple threads are operating on the same object. If multiple threads are operating on multiple objects, then synchronization is not required.

For Example, let us modify the code in the class “RaceCondition” as below and work with the previously unsynchronized class “Modify”.

package JavaConcepts;
public class RaceCondition {
public static void main(String[] args) {
Modify mObj = new Modify();
Modify mObj1 = new Modify();
Modify mObj2 = new Modify();
Thread t1 = new Thread(mObj, "thread 1");
Thread t2 = new Thread(mObj1, "thread 2");
Thread t3 = new Thread(mObj2, "thread 3");
t1.start();
t2.start();
t3.start();
}
}

Output:

The current thread being executed thread 1 Current Thread value 1

The current thread being executed thread 2 Current Thread value 1

The current thread being executed thread 3 Current Thread value 1

Java における同期とは何ですか?

Types of  Synchronization in Java

There are two types of thread synchronization, one being mutually exclusive and the other inter-thread communication.

1. Mutually Exclusive

  • In this case, threads obtain the lock before operating on an object, thereby avoiding working with objects that have had their values manipulated by other threads.
  • This can be achieved in three ways:
i. Synchronized Method

We can make use of the “synchronized” keyword for a method, thus making it a synchronized method. Every thread that invokes the synchronized method will obtain the lock for that object and release it once its operation is completed. In the above example, we can make our “run()” method as synchronized by using the “synchronized” keyword after the access modifier.

@Override
public synchronized void run() {
// TODO Auto-generated method stub
this.increment();
System.out.println("Current thread being executed "
+ Thread.currentThread().getName() + " Current Thread value " + this.getMyVar());
}

The output for this case will be:

The current thread being executed thread 1 Current Thread value 1

The current thread being executed thread 3 Current Thread value 2

The current thread being executed thread 2 Current Thread value 3

ii. Static synchronized method

In order to synchronize static methods, one needs to acquire its class level lock. After a thread obtains the class level lock, only then it will be able to execute a static method. While a thread holds the class level lock, no other thread can execute any other static synchronized method of that class. However, the other threads can execute any other regular method or regular static method or even non-static synchronized method of that class.

For example, let us consider our “Modify” class and make changes to it by converting our “increment” method to a static synchronized method. The code changes are as below:

package JavaConcepts;
public class Modify implements Runnable{
private static int myVar=0;
public int getMyVar() {
return myVar;
}
public void setMyVar(int myVar) {
this.myVar = myVar;
}
public static synchronized void increment() {
myVar++;
System.out.println("Current thread being executed " + Thread.currentThread().getName() + " Current Thread value " + myVar);
}
@Override
public void run() {
// TODO Auto-generated method stub
increment();
}
}
iii. Synchronized block

One of the main disadvantages of the synchronized method is that it increases threads waiting time, impacting the performance of the code. Therefore, to synchronize only the required lines of code in place of the entire method, one needs to make use of a synchronized block. Using synchronized block reduces the waiting time of the threads and improves performance as well. In the previous example, we have already made use of synchronized block while synchronizing our code for the first time.

Example:

public void run() {
// TODO Auto-generated method stub
synchronized(this) {
this.increment();
System.out.println("Current thread being executed "
+ Thread.currentThread().getName() + " Current Thread value " + this.getMyVar());
}
}

2. Thread Co-ordination

For synchronized threads, inter-thread communication is an important task. Inbuilt methods that help achieve inter-thread communication for synchronized code are namely:

  • wait()
  • notify()
  • notifyAll()
Note: These methods belong to the object class and not the thread class. For a thread to be able to invoke these methods on an object, it should be holding the lock on that object. Also, these methods cause a thread to release its lock on the object on which it is being invoked.
i. wait()

A thread on invoking the wait() method releases the lock on the object and goes into a waiting state. It has two method overloads:

  • public final void wait()throws InterruptedException
  • public final void wait(long timeout)throws InterruptedException
  • public final void wait(long timeout, int Nanos) throws InterruptedException
ii. notify()

A thread sends a signal to another thread in the waiting state by making use of the notify() method. It sends the notification to only one thread such that this thread can resume its execution. Which thread will receive the notification among all the threads in the waiting state depends on the Java Virtual Machine.

public final void notify()
iii. notifyAll()

When a thread invokes the notifyAll() method, every thread in its waiting state is notified. These threads will be executed one after the other based on the order decided by the Java Virtual Machine.

public final void notifyAll()

Conclusion

In this article, we have seen how working in a multi-threaded environment can lead to data inconsistency due to a race condition, how synchronization helps us overcome this by limiting a single thread to operate on a shared resource at a time. Also, how synchronized threads communicate with each other.

以上がJava における同期とは何ですか?の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

声明:
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。
前の記事:アブロファイル次の記事:アブロファイル