前書き: 今日のインターネット業界では、マルチスレッド テクノロジが非常に重要なスキルになっています。コンピュータ ハードウェアの発展に伴い、マルチスレッドによるプログラムのパフォーマンスの向上を期待して、マルチスレッド テクノロジに注目するプログラマーがますます増えています。広く使用されているプログラミング言語として、Java は豊富なマルチスレッド サポートも提供します。この記事では、読者が Java マルチスレッド テクノロジをより深く理解し、習得できるように、Java マルチスレッドの基本概念、原則、実装方法、および日常生活におけるアプリケーションを詳細に紹介します。
1.スレッドの属性
1. スレッドの中断
スレッドを中断するには 3 つの方法があります
- 糸が切れて自然に終わる
- メソッド内でキャッチされない例外が発生し、スレッドが終了されました。
- stop メソッドを使用する (非推奨)
- スレッドの割り込みステータスを設定するには、interrupt メソッドを使用します。割り込みステータスが設定されているかどうかを確認するには、まず静的 Thread.currentThread メソッドを呼び出して現在のスレッドを取得し、次に isInterrupted メソッドを呼び出します。ただし、スレッドがブロックされている場合は、割り込みステータスを確認できません。ここで割り込み例外が発生します。スリープ呼び出しまたは待機呼び出しによってブロックされているスレッドで割り込みメソッドが呼び出される場合、そのブロック呼び出し (つまり、スリープ呼び出しまたは待機呼び出し) は InterruptedException によって中断されます。
class test implements Runnable {
public void run() {
try{
while(true){
System.out.println("在运行");
Thread.sleep(1000);
}catch(InterruptedException e){
Thread.currentThread().interrupt();
}
}
}
2. デーモンスレッド
スレッドをデーモン スレッド (daemonthread) に変換します。このようなスレッドには魔法はありません。デーモン スレッドの唯一の目的は、他のスレッドにサービスを提供することです。タイマー スレッドは一例であり、定期的に「タイマー ティック」シグナルを他のスレッドに送信します。また、期限切れのキャッシュ アイテムをクリアするスレッドもデーモン スレッドです。デーモン スレッドだけが残ると、仮想マシンは終了します。デーモンスレッドだけが残っている場合は、プログラムを実行し続ける必要がないためです。次の例を考えてみましょう。
class Counter {
//线程个数
private int count;
public synchronized int getCount() {
return count;
}
//增加线程个数
public synchronized void incrementCount() {
count++;
}
//减少线程个数
public synchronized void decrementCount() {
count--;
}
}
//显示当前线程个数
class Display implements Runnable {
private Counter counter;
public Display(Counter counter) {
this.counter = counter;
}
@Override
public void run() {
while (true) {
try {
System.out.println("Number of threads: " + counter.getCount());
Thread.sleep(1000);
} catch (InterruptedException e) {
break;
}
}
}
}
public class Main {
public static void main(String[] args) throws InterruptedException {
final Counter counter = new Counter();
Thread displayThread = new Thread(new Display(counter));
displayThread.start();
for (int i = 0; i < 10; i++) {
Thread.sleep(400);
new Thread(new Runnable() {
@Override
public void run() {
//每创建一个线程就要更新counter
counter.incrementCount();
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
//每结速一个线程也要更新counter
counter.decrementCount();
}
}).start();
}
Thread.sleep(10000);
System.out.println("main线程已退出");
}
}
このコードから、カウンター スレッドがデーモン スレッドとして設定されていない場合、メイン スレッドの終了後も、カウンター スレッドが実行されている限り、プログラムは引き続き実行されることがわかります。メインスレッドが終了するとカウンタは不要になりますが、この時に活躍するのがデーモンスレッドです。 displayThread.setDaemon(true); を通じて、displayThread スレッドをデーモン スレッドに設定します。
2. 相互排他によるスレッドの同期
実際のマルチスレッド アプリケーションのほとんどでは、2 つ以上のスレッドが同じデータへのアクセスを共有する必要があります。 2 つのスレッドが同じオブジェクトにアクセスし、各スレッドがオブジェクトの状態を変更するメソッドを呼び出した場合はどうなりますか? ご想像のとおり、2 つのスレッドは相互にバックアップします。スレッドがデータにアクセスする順序によっては、オブジェクトが破損する可能性があります。この状況は競合状態と呼ばれることがよくあります。
マルチスレッド環境では、データの一貫性と整合性を確保するために、共有リソースへの同期された相互排他的なアクセスが必要です。 Java には、同期を実現するための synchronized キーワードが用意されており、synchronized キーワードによって変更されたメソッドまたはコード ブロックには、同時に 1 つのスレッドのみがアクセスできます。さらに、Java は、相互排他的アクセスを実装するための Lock インターフェイスとその実装クラス (ReentrantLock など) も提供します。
まず例を見てみましょう:
class test{
public static void main(String[] args) {
account account = new account(5000);
Thread a = new Thread(()->{
while (account.getCash()>=500){
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
account.drawMoney(500);
System.out.println("余额:"+account.getCash());
}
});
Thread b = new Thread(()->{
while (account.getCash()>=1000){
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
account.drawMoney(1000);
System.out.println("余额:"+account.getCash());
}
});
a.start();
b.start();
}
}
class account{
//账户余额
private int cash;
public account(int cash){
this.cash=cash;
}
//取钱
public void drawMoney(int money){
this.cash-=money;
}
public int getCash() {
return cash;
}
public void setCash(int cash) {
this.cash = cash;
}
}
実行結果:
上記の例では、a と b は同じ口座から同時にお金を引き出します。これは、次のように仮定すると、口座オブジェクトを同時に操作できるためです。この時点で残高は 1,000 元しかなく、二人とも非常に利己的でもう終わりだと思っていましたが、最終的には二人ともお金を手に入れましたが、銀行は 500 元を失いました。したがって、同じオブジェクトへのこの種の同時アクセスは、特に変更操作が必要な場合には非常に危険です。
解決策 1:
同期されたアノテーションを追加する
同期されたメソッド: メソッドの直前に直接追加します
public synchronized void drawMoney(int money){
this.cash-=money;
}
同期ブロック
Thread a = new Thread(()->{
synchronized (account){
while (true){
if (account.getCash() >= 1000) {
try {
Thread.sleep(1000);
account.drawMoney(1000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
System.out.println("余额:" + account.getCash());
}
else break;
}
});
synchronized(変更対象のオブジェクト){ 同期するコード } どのメソッドを使用しても、
Thread b = new Thread(() -> {
while (true) {
if (account.getCash() >= 500) {
try {
//获得锁
lock.lock();
Thread.sleep(1000);
account.drawMoney(500);
} catch (InterruptedException e) {
throw new RuntimeException(e);
} finally {
//释放锁
lock.unlock();
}
System.out.println("余额:" + account.getCash());
}
else break;
}
});
この号はここまでです。文章が良いと思われる場合は、次の号に注目してください。
次号のプレビュー:
スレッド間通信
スレッド プールの使用
Java マルチスレッドの生活への応用