首頁 > Java > java教程 > 主體

Java 中的同步是什麼?

WBOY
發布: 2024-08-30 16:18:11
原創
1203 人瀏覽過

Java 中的同步是一種 Java 功能,它限制多個執行緒同時嘗試存取公共共享資源。這裡的共享資源是指外部文件內容、類別變數或資料庫記錄。

開始您的免費軟體開發課程

網頁開發、程式語言、軟體測試及其他

同步在多執行緒程式設計中被廣泛使用。 「Synchronized」這個關鍵字使您的程式碼能夠只允許單個執行緒對其進行操作,而在此期間不會受到任何其他執行緒的干擾。

為什麼我們需要 Java 中的同步?

  • Java 是一種多執行緒程式語言。這意味著兩個或多個執行緒可以同時運行以完成任務。當執行緒同時運行時,很有可能出現您的程式碼可能提供意外結果的情況。
  • 你可能想知道,如果多執行緒會導致錯誤的輸出,那麼為什麼它被認為是 Java 中的重要特性?
  • 多執行緒透過並行運行多個執行緒使您的程式碼更快,減少程式碼執行時間並提供高效能。然而,由於通常稱為競爭條件的情況,使用多執行緒環境會導致輸出不準確。

什麼是競爭條件?

當兩個或多個執行緒並行運行時,它們往往會在該時間點存取和修改共享資源。執行緒調度演算法決定執行緒執行的順序。

因此,由於線程調度程序單獨控制線程,因此無法預測線程的執行順序。這會影響程式碼的輸出並導致輸出不一致。由於多個執行緒相互競爭來完成操作,因此該條件稱為「競爭條件」。

例如,讓我們考慮以下程式碼:

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:

目前正在執行的執行緒 執行緒1 目前執行緒值3

目前正在執行的執行緒 執行緒3 目前執行緒值2

目前正在執行的執行緒 執行緒2 目前執行緒值3

輸出2:

目前正在執行的執行緒 thread 3 Current Thread value 3

目前正在執行的執行緒 執行緒2 目前執行緒值3

目前正在執行的執行緒 執行緒1 目前執行緒值3

輸出3:

目前正在執行的執行緒 執行緒2 目前執行緒值3

目前正在執行的執行緒 執行緒1 目前執行緒值3

目前正在執行的執行緒 thread 3 Current Thread value 3

輸出4:

目前正在執行的執行緒 執行緒1 目前執行緒值2

目前正在執行的執行緒 thread 3 Current Thread value 3

目前正在執行的執行緒 thread 2 Current Thread value 2

Java 中的同步是什麼?

  • 從上面的範例中,您可以得出結論,執行緒是隨機執行的,而且該值不正確。按照我們的邏輯,該值應該會增加 1。但是,這裡大多數情況下輸出值為 3,少數情況下為 2。
  • 這裡的「myVar」變數是多個執行緒正在執行的共享資源。線程同時存取和修改“myVar”的值。讓我們看看如果我們註解掉其他兩個執行緒會發生什麼。

Java 中的同步是什麼?

本例的輸出為:

目前正在執行的執行緒thread 1 Current Thread value 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中文網其他相關文章!

相關標籤:
來源:php
本網站聲明
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn
熱門教學
更多>
最新下載
更多>
網站特效
網站源碼
網站素材
前端模板