スレッドの同期とデッドロック
マルチスレッド処理では、Runnableを使用して複数のスレッドによって操作されるリソースを記述でき、Threadは各スレッドオブジェクトを記述します。したがって、複数のスレッドが同じリソースにアクセスする場合、処理が適切に行われないと、データエラー操作が発生します。
同期の問題は
簡単なチケット販売プログラムを作成し、チケット販売の処理操作を実現するためにいくつかのスレッドオブジェクトを作成します。
class MyThreadTic implements Runnable{
private int ticket = 10; //总票数
@Override
public void run() {
// TODO Auto-generated method stub
while (true) {
if(this.ticket > 0) {
System.out.println(Thread.currentThread().getName() + "买票,ticket="+ this.ticket--);
} else {
System.out.println("票已卖完!");
break;
}
}
}
}
public class TicketDemo {
public static void main(String[] args) {
MyThreadTic mt = new MyThreadTic();
new Thread(mt, "票贩子A").start();
new Thread(mt, "票贩子B").start();
new Thread(mt, "票贩子C").start();
}
}
この時点でのプログラムは3つのスレッドオブジェクトを作成し、これらの3つのスレッドオブジェクトは10枚のチケットを販売します。現時点では、プログラムがチケットを販売する際に問題(偽の外観)はなく、遅延が追加された後に問題が明らかになります(チケットの数が負になります)。このとき、スレッドデータの同期を確保する必要があります。
スレッドの同期
同期の問題を解決するための鍵は「ロック」です。つまり、スレッドが操作を実行すると、他のスレッドは外部で待機します。
このロックを実現するには、synchronizedキーワードを使用して実現します。このキーワードを使用して、同期メソッドまたは同期コードブロックを定義します。同期コードブロック内のコードでは、1つのスレッドのみを実行できます。
synchronized(同步对象){
同步代码操作;
}
例:通常、現在のオブジェクトは、同期オブジェクトを処理するときに同期に使用できます。
class MyThreadTic implements Runnable{
private int ticket = 10; //总票数
@Override
public void run() {
while (true) {
synchronized (this) {
//每一次只允许一个线程进行访问
if(this.ticket > 0) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "买票,ticket="+ this.ticket--);
} else {
System.out.println("票已卖完!");
break;
}
}
}
}
}
public class TicketDemo {
public static void main(String[] args) {
MyThreadTic mt = new MyThreadTic();
new Thread(mt, "票贩子A").start();
new Thread(mt, "票贩子B").start();
new Thread(mt, "票贩子C").start();
}
}
同期を追加すると、プログラムの全体的なパフォーマンスが低下し、同期によって実際にパフォーマンスが低下します。
例:使用同期方法これを解決するには、メソッド定義でsynchronizedキーワードを使用するだけです。
class MyThreadTic implements Runnable{
private int ticket = 10; //总票数
public synchronized boolean sale() {
if(this.ticket > 0) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "买票,ticket="+ this.ticket--);
return true;
} else {
System.out.println("票已卖完!");
return false;
}
}
@Override
public void run() {
while (this.sale()) {
;
}
}
}
public class TicketDemo {
public static void main(String[] args) {
MyThreadTic mt = new MyThreadTic();
new Thread(mt, "票贩子A").start();
new Thread(mt, "票贩子B").start();
new Thread(mt, "票贩子C").start();
}
}
今後、システムクラスライブラリを調べてみると、システム内の多くのクラスで使用されている同期プロセスが同期方式を採用していることがわかりました。
スレッドのデッドロック
デッドロックは、マルチスレッド同期のプロセスで発生する可能性のある問題です。いわゆるデッドロックとは、複数のスレッドが互いに待機している状態を指します。
デッドロックを観察する
class Person{
public synchronized void say(Dog dog) {
System.out.println("out!");
dog.get();
}
public synchronized void get() {
System.out.println("Nice!");
}
}
class Dog{
public synchronized void say(Person per) {
System.out.println("wangwang!");
per.get();
}
public synchronized void get() {
System.out.println("shit!");
}
}
public class DeadLock implements Runnable {
private Person per = new Person();
private Dog dog = new Dog();
@Override
public void run() {
per.say(dog);
}
public DeadLock() {
new Thread(this).start();
dog.say(per);
}
public static void main(String[] args) {
new DeadLock();
}
}
デッドロックの主な原因は、お互いを待って、お互いがリソースを放棄するのを待っていることです。デッドロックは、実際には開発中の不確定な状態です。不適切なコード処理によってデッドロックが不規則に発生することがあります。これは、通常の開発ではデバッグの問題です。
複数のスレッドが同じリソースにアクセスする場合、それらを同期する必要があり、同期が多すぎるとデッドロックが発生します。