> Java > java지도 시간 > Java의 동기화란 무엇입니까?

Java의 동기화란 무엇입니까?

WBOY
풀어 주다: 2024-08-30 16:18:11
원래의
1236명이 탐색했습니다.

Java의 동기화는 여러 스레드가 공통으로 공유되는 리소스에 동시에 액세스하는 것을 제한하는 Java 기능입니다. 여기서 공유 리소스는 외부 파일 내용, 클래스 변수 또는 데이터베이스 레코드를 의미합니다.

무료 소프트웨어 개발 과정 시작

웹 개발, 프로그래밍 언어, 소프트웨어 테스팅 등

동기화는 멀티스레드 프로그래밍에서 널리 사용됩니다. "동기화"는 해당 기간 동안 다른 스레드의 간섭 없이 단일 스레드만 작동할 수 있도록 코드를 제공하는 키워드입니다.

Java에서 동기화가 필요한 이유는 무엇인가요?

  • Java는 멀티스레드 프로그래밍 언어입니다. 이는 작업 완료를 위해 두 개 이상의 스레드가 동시에 실행될 수 있음을 의미합니다. 스레드가 동시에 실행되면 코드가 예상치 못한 결과를 제공할 수 있는 시나리오가 발생할 가능성이 높습니다.
  • 멀티스레딩으로 인해 잘못된 출력이 발생할 수 있는데 왜 멀티스레딩이 Java에서 중요한 기능으로 간주되는지 궁금할 것입니다.
  • 멀티스레딩은 여러 스레드를 병렬로 실행하여 코드 실행 시간을 줄이고 고성능을 제공함으로써 코드 속도를 높여줍니다. 하지만 멀티스레딩 환경을 활용하게 되면 일반적으로 경쟁 조건(Race Condition)이라고 알려진 조건으로 인해 부정확한 출력이 발생하게 됩니다.

경쟁 조건이란 무엇인가요?

두 개 이상의 스레드가 병렬로 실행되면 해당 시점에 공유 리소스에 액세스하고 수정하는 경향이 있습니다. 스레드 스케줄링 알고리즘은 스레드가 실행되는 순서를 결정합니다.

이 때문에 스레드 스케줄러가 단독으로 스레드를 제어하므로 스레드가 실행되는 순서를 예측할 수 없습니다. 이는 코드 출력에 영향을 미치고 결과가 일관되지 않게 됩니다. 작업을 완료하기 위해 여러 스레드가 서로 경주하므로 이러한 조건을 "경주 조건"이라고 합니다.

예를 들어 아래 코드를 살펴보겠습니다.

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();
}
}
로그인 후 복사

위 코드를 연속적으로 실행하면 다음과 같이 출력됩니다.

Ourput1:

현재 실행 중인 스레드 스레드 1 현재 스레드 값 3

현재 실행 중인 스레드 스레드 3 현재 스레드 값 2

현재 실행 중인 스레드 스레드 2 현재 스레드 값 3

출력2:

현재 실행 중인 스레드 스레드 3 현재 스레드 값 3

현재 실행 중인 스레드 스레드 2 현재 스레드 값 3

현재 실행 중인 스레드 스레드 1 현재 스레드 값 3

출력3:

현재 실행 중인 스레드 스레드 2 현재 스레드 값 3

현재 실행 중인 스레드 스레드 1 현재 스레드 값 3

현재 실행 중인 스레드 스레드 3 현재 스레드 값 3

출력4:

현재 실행 중인 스레드 스레드 1 현재 스레드 값 2

현재 실행 중인 스레드 스레드 3 현재 스레드 값 3

현재 실행 중인 스레드 스레드 2 현재 스레드 값 2

Java의 동기화란 무엇입니까?

  • 위의 예를 보면 스레드가 무작위로 실행되고 있으며 값도 잘못되었다는 결론을 내릴 수 있습니다. 우리 논리에 따르면 값은 1씩 증가해야 합니다. 그러나 여기서 출력 값은 대부분의 경우 3이고 일부 경우에는 2입니다.
  • 여기서 "myVar" 변수는 여러 스레드가 실행되는 공유 리소스입니다. 스레드는 "myVar" 값에 동시에 액세스하고 수정하고 있습니다. 다른 두 스레드를 주석 처리하면 어떤 일이 발생하는지 살펴보겠습니다.

Java의 동기화란 무엇입니까?

이 경우의 출력은 다음과 같습니다.

현재 실행 중인 스레드 스레드 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 중국어 웹사이트의 기타 관련 기사를 참조하세요!

관련 라벨:
원천:php
본 웹사이트의 성명
본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.
인기 튜토리얼
더>
최신 다운로드
더>
웹 효과
웹사이트 소스 코드
웹사이트 자료
프론트엔드 템플릿