JAVA 다중 스레드 간의 통신 방식을 깊이 이해하다

소개
본고는 JAVA 다중 스레드 중의 스레드 간의 통신 방식에 대한 나의 이해를 요약하고 주로 코드와 문자를 결합하는 방식으로 스레드 간의 통신을 토론하기 때문에 책 속의 예시 코드를 발췌하였다.
2. 스레드 간의 통신 방식
① 동기화
여기서 말하는 동기화는 여러 개의 라인이synchronized 키워드를 통해 라인 간의 통신을 실현하는 것을 가리킨다.
참조 예:

public class MyObject {

  synchronized public void methodA() {
    //do something....
  }

  synchronized public void methodB() {
    //do some other thing
  }
}

public class ThreadA extends Thread {

  private MyObject object;
// 
  @Override
  public void run() {
    super.run();
    object.methodA();
  }
}

public class ThreadB extends Thread {

  private MyObject object;
// 
  @Override
  public void run() {
    super.run();
    object.methodB();
  }
}

public class Run {
  public static void main(String[] args) {
    MyObject object = new MyObject();

    // A B  :object
    ThreadA a = new ThreadA(object);
    ThreadB b = new ThreadB(object);
    a.start();
    b.start();
  }
}
스레드 A와 스레드 B는 같은 MyObject 클래스의 대상인object를 가지고 있기 때문에 이 두 스레드는 서로 다른 방법을 호출해야 하지만, 그것들은 동시에 실행된다. 예를 들어 스레드 B는 스레드 A가methodA() 방법을 실행한 후에methodB() 방법을 실행할 수 있다.이렇게 하면 스레드 A와 스레드 B가 통신을 실현할 수 있다.
이런 방식은 본질적으로'공유 메모리'식의 통신이다.여러 개의 루틴은 같은 공유 변수에 접근해야 하며, 자물쇠를 얻은 사람이 실행할 수 있다.
② while 폴링 방식
코드는 다음과 같습니다.

import java.util.ArrayList;
import java.util.List;

public class MyList {

  private List<String> list = new ArrayList<String>();
  public void add() {
    list.add("elements");
  }
  public int size() {
    return list.size();
  }
}

import mylist.MyList;

public class ThreadA extends Thread {

  private MyList list;

  public ThreadA(MyList list) {
    super();
    this.list = list;
  }

  @Override
  public void run() {
    try {
      for (int i = 0; i < 10; i++) {
        list.add();
        System.out.println(" " + (i + 1) + " ");
        Thread.sleep(1000);
      }
    } catch (InterruptedException e) {
      e.printStackTrace();
    }
  }
}

import mylist.MyList;

public class ThreadB extends Thread {

  private MyList list;

  public ThreadB(MyList list) {
    super();
    this.list = list;
  }

  @Override
  public void run() {
    try {
      while (true) {
        if (list.size() == 5) {
          System.out.println("==5,  b ");
          throw new InterruptedException();
        }
      }
    } catch (InterruptedException e) {
      e.printStackTrace();
    }
  }
}

import mylist.MyList;
import extthread.ThreadA;
import extthread.ThreadB;

public class Test {

  public static void main(String[] args) {
    MyList service = new MyList();

    ThreadA a = new ThreadA(service);
    a.setName("A");
    a.start();

    ThreadB b = new ThreadB(service);
    b.setName("B");
    b.start();
  }
}
이런 방식으로 스레드 A는 끊임없이 조건을 바꾸고, 스레드ThreadB는 끊임없이 while 문장을 통해 이 조건(list.size()=5)의 성립 여부를 검사하여 스레드 간의 통신을 실현했다.그러나 이런 방식은 CPU 자원을 낭비할 수 있다.자원을 낭비하는 이유는 JVM 스케줄러가 CPU를 스레드 B에 넘겨 실행할 때 유용한 작업은 하지 않고 조건이 성립되었는지 끊임없이 테스트하기 때문이다.마치 현실에서 누군가가 핸드폰 화면에 전화가 왔는지 안 왔는지 계속 보고 있는 것과 같다. 다른 일을 하고 있는데 전화가 왔을 때 벨을 울리면 전화가 왔음을 알리는 것이다.
③ wait/notify 메커니즘
코드는 다음과 같습니다.

import java.util.ArrayList;
import java.util.List;

public class MyList {

  private static List<String> list = new ArrayList<String>();

  public static void add() {
    list.add("anyString");
  }

  public static int size() {
    return list.size();
  }
}


public class ThreadA extends Thread {

  private Object lock;

  public ThreadA(Object lock) {
    super();
    this.lock = lock;
  }

  @Override
  public void run() {
    try {
      synchronized (lock) {
        if (MyList.size() != 5) {
          System.out.println("wait begin "
              + System.currentTimeMillis());
          lock.wait();
          System.out.println("wait end "
              + System.currentTimeMillis());
        }
      }
    } catch (InterruptedException e) {
      e.printStackTrace();
    }
  }
}


public class ThreadB extends Thread {
  private Object lock;

  public ThreadB(Object lock) {
    super();
    this.lock = lock;
  }

  @Override
  public void run() {
    try {
      synchronized (lock) {
        for (int i = 0; i < 10; i++) {
          MyList.add();
          if (MyList.size() == 5) {
            lock.notify();
            System.out.println(" ");
          }
          System.out.println(" " + (i + 1) + " !");
          Thread.sleep(1000);
        }
      }
    } catch (InterruptedException e) {
      e.printStackTrace();
    }
  }
}

public class Run {

  public static void main(String[] args) {

    try {
      Object lock = new Object();

      ThreadA a = new ThreadA(lock);
      a.start();

      Thread.sleep(50);

      ThreadB b = new ThreadB(lock);
      b.start();
    } catch (InterruptedException e) {
      e.printStackTrace();
    }
  }
}
스레드 A는 조건이 충족될 때(list.size () = 5) 작업을 수행합니다.스레드 B는list에 요소를 추가하여list의size를 변경합니다.
A, B는 어떻게 통신합니까?즉, 라인 A가list를 어떻게 알았는지.사이즈 () 벌써 5인데?
Object 클래스의wait () 및 notify () 메서드가 사용됩니다.
조건이 충족되지 않으면 (list.size ()!=5), 스레드 A 호출wait()는 CPU를 포기하고 차단 상태로 들어갑니다. ---②while 폴링처럼 CPU를 사용하지 않음
조건이 충족되면 스레드 B는 notify () 알림 스레드 A를 호출합니다. 알림 스레드 A란 스레드 A를 깨우고 실행 가능한 상태로 만드는 것입니다.
이런 방식의 장점 중 하나는 CPU의 이용률이 높아졌다는 것이다.
그러나 단점도 있다. 예를 들어 스레드 B가 먼저 실행되고 한꺼번에 5개의 요소를 추가하고 notify () 를 호출하여 알림을 보냈는데 이때 스레드 A가 실행되었다.스레드 A가wait () 를 실행하고 호출할 때, 영원히 깨어날 수 없습니다.왜냐하면 라인 B가 이미 통지를 보냈기 때문에 이후에 더 이상 통지를 보내지 않을 것이다.이것은 알림이 너무 이르면 프로그램의 실행 논리를 혼란시킬 수 있다는 것을 설명한다.
이상에서 JAVA 다중 스레드 간의 통신 방식을 깊이 있게 이해한 것은 바로 편집자가 여러분에게 공유한 모든 내용입니다. 여러분께 참고가 되고 저희를 많이 사랑해 주시기 바랍니다.

좋은 웹페이지 즐겨찾기