Penyegerakan dalam Java ialah ciri Java yang menyekat berbilang urutan daripada mencuba mengakses sumber yang biasa dikongsi pada masa yang sama. Di sini sumber yang dikongsi merujuk kepada kandungan fail luaran, pembolehubah kelas atau rekod pangkalan data.
Mulakan Kursus Pembangunan Perisian Percuma Anda
Pembangunan web, bahasa pengaturcaraan, ujian perisian & lain-lain
Penyegerakan digunakan secara meluas dalam pengaturcaraan berbilang benang. "Disegerakkan" ialah kata kunci yang menyediakan kod anda dengan keupayaan untuk membenarkan hanya satu utas beroperasi padanya tanpa gangguan daripada mana-mana urutan lain dalam tempoh tersebut.
Apabila dua atau lebih utas berjalan selari, mereka cenderung untuk mengakses dan mengubah suai sumber yang dikongsi pada masa itu. Algoritma penjadualan utas menentukan urutan urutan urutan dilaksanakan.
Disebabkan ini, seseorang tidak boleh meramalkan urutan urutan akan dilaksanakan kerana penjadual benang mengawalnya semata-mata. Ini menjejaskan output kod dan menghasilkan output yang tidak konsisten. Memandangkan berbilang benang berlumba antara satu sama lain untuk menyelesaikan operasi, syarat ini dirujuk sebagai "keadaan perlumbaan".
Sebagai Contoh, mari kita pertimbangkan kod di bawah:
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(); } }
Apabila menjalankan kod di atas secara berturut-turut, outputnya adalah seperti berikut:
Ourput1:
Benang semasa sedang dilaksanakan utas 1 Nilai Benang Semasa 3
Benang semasa sedang dilaksanakan utas 3 Nilai Benang Semasa 2
Benang semasa sedang dilaksanakan utas 2 Nilai Benang Semasa 3
Output2:
Benang semasa sedang dilaksanakan utas 3 Nilai Benang Semasa 3
Benang semasa sedang dilaksanakan utas 2 Nilai Benang Semasa 3
Benang semasa sedang dilaksanakan utas 1 Nilai Benang Semasa 3
Output3:
Benang semasa sedang dilaksanakan utas 2 Nilai Benang Semasa 3
Benang semasa sedang dilaksanakan utas 1 Nilai Benang Semasa 3
Benang semasa sedang dilaksanakan utas 3 Nilai Benang Semasa 3
Output4:
Benang semasa sedang dilaksanakan utas 1 Nilai Benang Semasa 2
Benang semasa sedang dilaksanakan utas 3 Nilai Benang Semasa 3
Benang semasa sedang dilaksanakan utas 2 Nilai Benang Semasa 2
Keluaran dalam kes ini ialah:
Benang semasa sedang dilaksanakan utas 1 Nilai Benang Semasa 1
Ini bermakna apabila satu utas sedang berjalan, output adalah seperti yang dijangkakan. Walau bagaimanapun, apabila berbilang utas sedang berjalan, nilai sedang diubah suai oleh setiap utas. Oleh itu, seseorang perlu mengehadkan bilangan utas yang berfungsi pada sumber yang dikongsi kepada satu utas pada satu masa. Ini dicapai menggunakan penyegerakan.
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
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
There are two types of thread synchronization, one being mutually exclusive and the other inter-thread communication.
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
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(); } }
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()); } }
For synchronized threads, inter-thread communication is an important task. Inbuilt methods that help achieve inter-thread communication for synchronized code are namely:
A thread on invoking the wait() method releases the lock on the object and goes into a waiting state. It has two method overloads:
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()
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()
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.
Atas ialah kandungan terperinci Apakah Penyegerakan dalam Java?. Untuk maklumat lanjut, sila ikut artikel berkaitan lain di laman web China PHP!