1. 멀티 스레드 동기화:
1.1. 동기화 메커니즘:
멀티 스레드에서는 제한된 리소스에 액세스하려고 하는 여러 스레드가 있을 수 있으므로 이를 방지해야 합니다. 따라서 동기화 메커니즘이 도입됩니다. 즉, 스레드가 리소스를 사용할 때 리소스가 잠기므로 잠금이 해제될 때까지 다른 스레드가 해당 리소스에 액세스할 수 없습니다.
1.2. 공유 멤버 변수의 예:
멤버 변수 및 지역 변수:
멤버 변수:
변수가 멤버 변수인 경우 여러 스레드가 동일한 변수에 액세스할 수 있습니다. object 멤버 변수에 대해 작업하기 위해 이러한 여러 스레드는 하나의 멤버 변수를 공유합니다.
지역 변수:
변수가 지역 변수이고 여러 스레드가 동일한 개체에서 작동하는 경우 각 스레드는 지역 변수의 복사본을 갖게 됩니다. 그들 사이의 지역 변수는 서로 영향을 미치지 않습니다.
다음은 예시입니다.
Runnable을 구현하는 스레드 클래스:
class MyThread3 implements Runnable{ //两个线程操作同一个对象,共享成员变量 //int i; @Override public void run() { //两个线程操作同一个对象,各自保存局部变量的拷贝 int i = 0; while(i<100){ System.out.println(i); i++; try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } } } }
메인 메서드에서 동일한 개체를 작동하려면 두 개의 스레드를 사용합니다.
public static void main(String[] args) { MyThread3 myThread = new MyThread3(); //下面两个线程对同一个对象(Runnable的实现类对象)进行操作 Thread thread = new Thread(myThread); Thread thread2 = new Thread(myThread); //各自保存局部变量的拷贝,互不影响,输出200个数字 thread.start(); thread2.start(); }
여기서 i를 멤버 변수로 바꾸면 100개의 숫자가 출력됩니다.
1.3. 공유 리소스로 인한 읽기 오류
예를 들어 두 스레드가 Number 객체를 공유하고 Number 클래스의 getNumber 메서드를 통해 데이터를 가져오면 데이터를 읽고 다시 쓰는 경우가 발생합니다. 작업:
먼저 Number 클래스를 만듭니다:
class Number{ private int number = 10; public String getNumber(int i){ if(number > 0){ try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } number -= i; return "取出"+i+"成功,剩余数量:"+number; } return "取出"+i+"失败,剩余数量:"+number; } }
스레드 클래스의 개인 속성에는 Number 클래스에 대한 참조가 포함됩니다:
class MyThread4 extends Thread{ //两个线程操作同一个对象,共享成员变量 Number number; public MyThread4(Number number){ this.number = number; } @Override public void run() { System.out.println(number.getNumber(8)); } }
동일한 Number 클래스 인스턴스에 대한 참조를 포함하는 두 개의 스레드 클래스를 기본 함수에 생성합니다.
public static void main(String[] args) { Number number = new Number(); //两个线程操作同一个对象,共享对象number的成员变量number MyThread4 myThread = new MyThread4(number); MyThread4 myThread2 = new MyThread4(number); myThread.start(); myThread2.start(); }
이런 방식으로 첫 번째 스레드가 Number의 숫자 변수를 읽을 때 먼저 저장한 다음 0.1을 절전 모드로 전환합니다. 초 후에 두 번째 스레드가 숫자 변수를 읽어서 저장합니다. 이때 두 스레드는 수정 시 동일한 숫자가 두 번 수정됩니다.
2. 동기화 메커니즘 구현:
동기화는 항상 다중 스레드 동시 프로그래밍에서 베테랑 역할을 해왔지만 Java SE1이 도입되면서 많은 사람들이 이를 무거운 잠금이라고 부릅니다. 6 동기화가 다양한 최적화를 수행한 후 어떤 경우에는 그다지 무겁지 않습니다."
Java의 모든 개체는 잠금으로 사용할 수 있습니다.
동기화 메서드의 경우 잠금은 현재 인스턴스 개체입니다.
정적 동기화 메서드의 경우 잠금은 현재 개체의 클래스 개체입니다.
동기화 메서드 블록의 경우 잠금은 동기화된 괄호에 구성된 개체입니다.
스레드가 동기화된 코드 블록에 액세스하려고 하면 먼저 잠금을 획득해야 합니다.
2.1. 동기화된 메서드를 생성하려면 동기화된 키워드를 사용하세요.
이 키워드로 수정된 메서드를 사용하세요. 각 객체에는 잠금, 즉 모니터가 있습니다. 객체의 동기화 메소드에 액세스하면 메소드뿐만 아니라 객체도 잠겨 있음을 의미합니다. 동기화된 메서드가 스레드에 의해 실행되면 다른 스레드는 동기화된 메서드가 실행될 때까지 객체의 동기화된 메서드에 액세스할 수 없습니다(그러나 다른 동기화되지 않은 메서드는 호출할 수 있음).
정적 동기화된 메서드 호출 상황:
객체의 정적 동기화 메서드가 호출되면 동기화된 메서드가 있는 개체가 아니라 동기화된 메서드가 있는 개체에 해당하는 Class 개체가 호출됩니다. 이렇게 하면 다른 스레드가 다른 스레드를 호출할 수 없습니다. 하지만 비정적 동기화 메서드를 호출할 수 있습니다. 결론: 정적 동기화 메서드를 실행하여 메서드가 있는 개체를 잠그고 비정적 동기화 메서드를 실행하여 메소드에 해당하는 Class 객체를 잠급니다.
다음은 정적 메소드를 호출하는 스레드의 예입니다. 메소드가 위치한 객체에 해당하는 Class 객체가 잠겨 있으므로 다른 스레드에서는 호출할 수 없습니다. 메소드가 위치한 객체의 다른 정적 동기화 메소드:
/** * 定义一个类,包含了线程类需要调用的方法 */ class Compute1{ //这时如果某个线程调用该方法, //将锁定synchronized方法所在对象对应的class对象, //而不是锁定synchronized方法所在对象 public synchronized static void execute(){ for(int i = 0; i<100; i++){ try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("compute1:execute1 " + i++); } } public synchronized static void execute2(){ for(int i = 0; i<100; i++){ try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("compute1:execute2 " + i++); } } }실행이 완료될 때까지 한 번에 하나의 정적 메서드만 호출할 수 있습니다. 2.2. 동기화를 사용하여 동기화된 코드 블록을 생성합니다.
동기화를 달성하기 위한 실행 가능 표시 역할을 하는 동기화된 동기화 코드 블록을 사용합니다. 효과:
public static void main(String[] args) { Compute1 com = new Compute1(); Thread thread1 = new Thread1(com); Thread thread2 = new Thread2(com); thread1.start(); thread2.start(); }
동기화 방법을 사용하는 것과 동일한 효과를 얻기 위해 동기화된 동기화 코드 블록을 사용하려면 다음 참조를 잠글 수 있습니다.
2.3. 동기화 방법 및 동기화 동기화 코드 블록:
/** * 定义一个类,包含了线程类需要调用的方法 */ class Compute1{ //通过同步代码块锁定object1对象进行锁定了其他同样的synchronized代码块 private Object object1 = new Object(); public void execute(){ synchronized(object1){ for(int i = 0; i<100; i++){ try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("compute1:execute1 " + i++); } } } public synchronized void execute2(){ synchronized(object1){ for(int i = 0; i<100; i++){ try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("compute1:execute2 " + i++); } } } }
동기화 방법은 특정 시간에 하나의 스레드만 동기화 방법을 실행할 수 있는 대략적인 동시성 제어입니다.
synchronized(this){ … }동기화된 동기화 코드 블록은 세분화된 동시성 제어입니다. 코드 블록 외부의 코드는 동시에 다른 스레드에서 액세스할 수 있습니다.