ホームページ  >  記事  >  Java  >  Javaスレッド間の通信方法の詳細説明

Javaスレッド間の通信方法の詳細説明

高洛峰
高洛峰オリジナル
2017-01-05 14:35:221682ブラウズ

このまとめは、主にテキストとコードを組み合わせたスレッド間の通信方法についての私の理解をまとめたものです。そのため、具体的な内容は以下のとおりです。

① 同期

ここで言う同期とは、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 クラスの同じオブジェクトを保持しているため、2 つのスレッドは異なるメソッドを呼び出す必要がありますが、同期して実行されます。例: スレッド 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 に渡すとき、「有用な」作業は行わず、特定の条件が真であるかどうかを常にテストしているだけだからです。これは、現実の生活において、人が他のことをするのではなく、電話の画面を見つめ続けるのと似ています。電話がかかってくると、電話が鳴って電話がかかってきたことを知らせます。

③待機/通知メカニズム

コードは次のとおりです:

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 はリストに要素を追加し、リストのサイズを変更します。

AとBはどのようにコミュニケーションをとりますか?言い換えれば、スレッド A は、list.size() がすでに 5 であることをどのようにして知るのでしょうか?

ここではObjectクラスのwait()メソッドとnotify()メソッドが使用されています。

条件が満たされない場合(list.size() !=5)、スレッドAはwait()を呼び出してCPUを放棄し、ブロッキング状態に入ります。 ---②ポーリング中のようにCPUを占有することはありません

条件が満たされると、スレッドBはnotify()を呼び出してスレッドAに通知します。いわゆる通知スレッドAは、スレッドAをウェイクアップしてランナブルに入れます。州。

この方法の利点の 1 つは、CPU 使用率が向上することです。

しかし、いくつかの欠点もあります。たとえば、スレッド B が最初に実行され、一度に 5 つの要素を追加して、notify() を呼び出して通知を送信しますが、スレッド A が実行されて wait( が呼び出されるとき、この時点ではスレッド A はまだ実行中です。 )、そうすると決して目覚めることはありません。スレッド B はすでに通知を送信しており、今後は通知を送信しないためです。これは、通知が早すぎるとプログラムの実行ロジックが中断されることを示しています。

上記がこの記事の全内容です。Java プログラミングを学習する皆さんのお役に立てれば幸いです。

Java スレッド間の通信方法のより詳細な説明については、PHP 中国語 Web サイトに注目してください。

声明:
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。