スレッドとは
スレッドは、オペレーティングシステムのスケジューリングの最小単位です。プロセスには複数のスレッドが存在する可能性があります。これらのスレッドは、独自のカウンター、スタック、およびローカル変数を持つことができ、共有メモリ変数にアクセスできます。マルチスレッドの利点は、応答時間とスループットを向上させることができることです。
マルチスレッドを使用する
プロセスが実行されているときは、少なくとも1つのスレッドが実行されています。
public class Test {
public static void main(String[] args) {
System.out.println(Thread.CurrentThread().getName()); // 输出main
}
}
上記のプログラムの出力はmainですが、実際にはmainというスレッドがmainメソッドを実行しています。出力mainはmainメソッドとは関係ありません。
スレッドを継承する
マルチスレッドを実装するには、2つの方法があります。1つはThreadクラスを継承する方法で、もう1つはRuannableインターフェイスを実装する方法です。Threadクラスの構造を見ることができます
public class Thread implents Runnable
独自のスレッドクラスMyThreadを作成し、runメソッドをオーバーライドします。
public class MyThread extends Thread {
@Override
public void run() {
super.run();
System.out.println("Mythread");
}
}
public class Run() {
public static void main(String[] args) {
Mythread myThread = new MyThread();
myThread.start():
System.out.println("运行结束");
}
}
上記の出力は
运行结束
MyThread
Runnableインターフェースを実装する
最初に作成されたスレッドクラスにすでに親クラスがある場合、Javaは単一の継承のみをサポートするため、スレッドを継承することはできません。次に、ピンチでRunnableインターフェイスを実装できます。
public class MyRunnable imiplements Runnable {
@Override
public void run() {
System.out.println("运行中");
}
}
それはどのように機能しますか?Threadのコンストラクターを見てみましょう。
Runnableインターフェースを追加することでスレッドを作成できます。
public class Run {
public static void main(String[] args) {
Thread thread = new Thread(runnable);
thread.start();
System.out.println("运行结束!");
}
}
インスタンス変数とスレッドセーフ
カスタムスレッドクラスのインスタンス変数は、他のスレッドと共有することも共有しないこともできます。それらを区別することは、スレッドの安全性にとって非常に重要です。
スレッドがデータを共有しない場合
public MyThread extends Thread {
private int count = 5;
public MyThread(String name) {
super();
this.setName(nanme); //线程的名字
}
@Override
public void run(){
super.run();
while(count > 0 ) {
count--;
System.out.println("由"+this.currentThread().getName() +" 计算. count=" + count);
}
}
}
public class Run {
public static void main(String[] args) {
MyThread a = new MyThread("A");
MyThread b = new MyThread("B");
MyThread c = new MyThread("C");
a.start();
b.start();
c.start();
}
}
データを共有する方法は?
public class MyThread extends Thread {
private int count = 5;
@Override
public void run() {
super.run();
while(count > 0) {
count--;
System.out.println("由" + this.currentThread().getName() + "计算,count =" + count);
}
}
}
public class Run() {
public static void main(String[] args) {
MyThread mythread = new MyThread();
Thread a = new Thread(mythread, "A");
Thread b = new Thread(mythread, "B");
Thread c = new Thread(mythread, "C");
a.start();
b.start();
c.start();
}
}
上記のスレッドはMyThreadのカウントデータを共有しているため、上記のコードはスレッドセーフではありません。この問題を解決する最も簡単な方法は、同期を通じてスレッドセーフを実現することです。
currentThreadメソッド
ThreadクラスにはcurrentThreadメソッドがあります。このメソッドは、現在のスレッドの名前など、現在のスレッドの情報を表示できます。
isAliveメソッド
現在のスレッドがアクティブであるかどうか、およびアクティブ状態とは何かを判断するために使用されます。アクティブ状態とは、スレッドが開始され、終了されていないことを意味します。
sleep()メソッド
メソッドsleepメソッドは、現在実行中のスレッドを指定されたミリ秒数以内にスリープさせることを意味します。現在実行中のスレッドは、this.currentThread()の結果を参照します。
停止スレッド
Javaで進行中のスレッドを終了するには、次の方法があります。
- 終了フラグを使用して、スレッドを正常に終了させます。つまり、runメソッドが完了するとスレッドが終了します。
- stopメソッドを使用してスレッドを強制的に終了しますが、stopとsuspendはどちらも期限切れのメソッドであるため、お勧めしません。
- スレッドを中断するには、interruptメソッドを使用します
止められない糸
次の例では、interrupt()メソッドを呼び出してスレッドを停止しますが、interruptメソッドを使用した場合の効果は、for + breakとは異なります。interruptメソッドは単に呼び出され当前线程中打一个停止的标记
ます。
public class MyThread extends Thread {
@Override
public void run() {
super.run();
for(int i = 0 ; i < 50000; i++) {
System.out.println("i = " + (i + 1));
}
}
}
public class Run {
public static void main(String[] args) {
try {
MyThread my_thread = new MyThread();
my_thread .start();
Thread.sleep(2000);
my_thread.interrupt();
} catch(InterruptException e) {
System.out.println("main catch");
e.printStackTrace();
}
}
}
その結果、500,000行のログが引き続き出力されるため、interruptメソッドを呼び出してもスレッドは停止しません。
スレッドが停止しているかどうかを確認します
Threadクラスには、次の2つのメソッドがあります。
- this.interrupted()、現在のスレッドが中断されているかどうかをテストします
- this.isInterrupted()、スレッドが中断されているかどうかをテストします
this.interrupted()
メソッドの説明を見てみましょう。現在のスレッドが中断されているかどうかをテストします。現在のスレッドは、このメソッド呼び出しを実行するスレッドを参照しています。
public class MyThread extends Thread {
@Override
public void run() {
super.run();
for(int i = 0; i < 50000; i++) {
System.out.println("i = " + ( i + 1));
}
}
}
public class Run {
public static void main(String[] args) {
try {
MyThread thread = new MyThread();
thread.start();
thread.interrupt();
System.out.println("是否停止1 ? ="+ thread.interrupted());
System.out.println("是否停止2 ? =" + thread.interrupted());
} catch(InterruptedException e) {
System.out.prinln("main catch");
e.printStackTrace();
}
System.out.println("end");
}
}
thread.interrupted()を使用して、現在のスレッドが中断されているかどうかをテストします。ここでは、現在のスレッドがメインスレッドであり、中断されたことはありません。したがって、印刷結果は两个false
です。
次のコードを見てください。
public class Run2 {
public static void main(String[] args) {
Thread.currentThread().interrupt();
System.out.println("是否停止1 ? " + Thread.interrrupted()); // true
System.out.println("是否停止2 ? " + Thread.interrrupted()); // false
System.out.println("end");
}
}
Interrupted()メソッドは、現在のスレッドが停止しているかどうかを判別しますが、なぜ2番目のスレッドがfalseなのですか?中断されたメソッドの手動の説明を見てください。
現在のスレッドが中断されているかどうかをテストします。この方法により、スレッド中断ステータスがクリアされます。つまり、メソッドが2回続けて呼び出された場合、2番目の呼び出しはfalseを返します(最初の呼び出しが中断ステータスをクリアした後、中断がチェックされる前に2番目の呼び出しが完了した後、現在のスレッドは次の場合を除いて再度中断されます。 )。
現在のスレッドが中断されているかどうかをテストします。スレッドの中断ステータスは、このメソッドによってクリアされます。つまり、このメソッドが2回続けて呼び出された場合、2番目の呼び出しはfalseを返します(最初の呼び出しが中断されたステータスをクリアした後、2番目の呼び出しがそれを調べる前に、現在のスレッドが再度中断されない限り)
つまり、interrupted()メソッドには状態をクリアするメソッドがあるため、2回目の呼び出しでinterrupted()メソッドによって返される値はfalseです。
次のように宣言されているinterrupted()メソッドを見てから、isInterrupted()メソッドを見てみましょう。
public boolean isInterrupted();
宣言から、isInterrupted()メソッドが静的ではないことがわかります。
public class Run3 {
public static void main(String[] args) {
try {
MyThread thread = new MyThread();
thread.start();
Thread.sleep(1000);
thread.interrupt();
System.out.println("是否停止1 ?==" + thread.isInterrupted()); // true
System.out.println("是否停止2? ==" + thread.isInterrupted()); // true
} catch(InterruptedException e) {
System.out.println("main catch");
e.printStackTrace();
}
System.out.println("end")
}
}
this.isInterrrupted()がステータスフラグをクリアしなかったため、2つのtrueが出力されていることがわかります。
2つの方法の説明を要約しましょう。
- this.interrupted():現在のスレッドがすでに中断状態にあるかどうかをテストします。実行後、状態フラグをfalseにクリアする機能があります。
- this.isInterrupted():スレッドThreadオブジェクトがすでに中断されているかどうかをテストしますが、ステータスフラグをクリアしません。
停止できるスレッド-例外メソッド
public calss MyThread extends Thread {
@Override
public void run() {
super.run();
for(int i = 0; i < 50000; i++) {
if(this.interrupted()) {
System.out.println("已经是停止状态!我要退出!");
break;
}
System.out.println("i=" + (i + 1));
}
}
}
public class Run {
public static void main(String[] args) {
try {
MyThread thread =new MyThread();
thread.start();
Thread.sleep(2000);
thread.interrupt();
} catch(InterruptedException e) {
System.out.println("main catch");
e.printStackTrace();
}
}
}
上記の例ではスレッドを停止していますが、forステートメントの下にステートメントがある場合は、引き続き実行されます。
public calss MyThread extends Thread {
@Override
public void run() {
super.run();
for(int i = 0; i < 50000; i++) {
if(this.interrupted()) {
System.out.println("已经是停止状态!我要退出!");
break;
}
System.out.println("i=" + (i + 1));
}
System.out.println("我被输出,如果此代码是for又继续运行,线程并未停止!");
}
}
public class Run {
public static void main(String[] args) {
try {
MyThread thread = new MyThread() ;
thread.start();
Thread.sleep(2000);
thread.interrupt();
} catch(InterruptedException e) {
System.out.println("main catch");
e.printStackTrace();
}
}
}
上記のコードを我被输出,如果此代码是for又继续运行,线程并未停止!
出力できます。時間内にスレッドを停止してforループから飛び出すと、forに続くステートメントも実行されることを説明します。これは、問題が何であるかを示しています。thread.interruptは、スレッドが停止されることを示す単なるフラグですが、this.interrupted()は、このフラグを検出してフラグサービスをクリアすることしかできません。
では、スレッドが停止したとはどういう意味ですか?
public calss MyThread extends Thread {
@Override
public void run() {
try {
super.run();
for(int i = 0; i < 50000; i++) {
if(this.interrupted()) {
System.out.println("已经是停止状态!我要退出!");
throw new InterruptedException();
break;
}
System.out.println("i=" + (i + 1));
}
System.out.println("我被输出,如果此代码是for又继续运行,线程并未停止!");
} catch (InterrruptedException e) {
System.out.println("进MyThread.java类run方法中的catch了");
}
}
}
public class Run {
public static void main(String[] args) {
try {
MyThread thread = new MyThread() ;
thread.start();
Thread.sleep(2000);
thread.interrupt();
} catch(InterruptedException e) {
System.out.println("main catch");
e.printStackTrace();
}
}
}
眠りにつく
スレッドがスリープ状態の場合、どのような影響がありますか?
public calss MyThread extends Thread {
@Override
public void run() {
try {
super.run();
System.out.println("run begin")
Thread.sleep(20000); //睡觉20s
System.out.println("run end");
} catch (InterrruptedException e) {
System.out.println("在睡觉中被停止,进入catch!" + this.isInterrupted()); // 结果打印false。
}
}
}
public class Run {
public static void main(String[] args) {
try {
MyThread thread = new MyThread() ;
thread.start();
Thread.sleep(2000);
thread.interrupt();
} catch(InterruptedException e) {
System.out.println("main catch");
e.printStackTrace();
}
}
}
結果の観点から、スレッドがスリープ状態で停止すると、catchステートメントが入力され、停止状態の値がクリアされてfalseになります。
スレッドを一時停止します
スレッドを一時停止すると、スレッドは実行を再開できます。Javaマルチスレッドでは、suspend()メソッドを使用してスレッドを一時停止し、resume()メソッドを使用してスレッドの実行を再開できます。
public class MyThread extends Thread {
private long i = 0;
public long getI() {
return i;
}
public void setI(int i) {
this.i = i;
}
@Override
public void run() {
while(true) {
i++;
}
}
}
public class Run {
public static void main(String[] args) {
try {
MyThread thread = new MyThread();
thread.start();
Thread.sleep(5000);
thread.suspend();
System.out.println("A=" + System.currentTimeIillis() + " i =" + thread.getI());
Thread.sleep(5000);
System.out.println("A=" + System.currentTimeMIillis() + " i =" + thread.getI());
// B段
thread.resume();
Thread.sleep(5000);
// C段
thread.suspend();
System.out.println("B= " + System.currentTimeMillis() + " i = " + thread.getI());
Thread.sleep(5000);
System.out.println("B = " + System.currentTimeMillis() + " i = " + thread.getI() );
} catch(InterruptedException e) {
e.printStackTrace();
}
}
}
サスペンドおよびレジュームメソッドのデメリット-排他的
スレッドが特定のリソースのロックを取得したが、それが一時停止した場合、後続のスレッドはロックを取得できません。
降伏法
このメソッドは、現在のCPUリソースを放棄し、それを他のタスクに割り当ててCPU実行時間を占有するために使用されます。しかし、あきらめる時間は不確かです。あきらめてすぐにタイムスライスを取得した可能性があります。
現在のスレッドがプロセッサの現在の使用を譲ることをいとわないというスケジューラへのヒント。スケジューラーはこのヒントを自由に無視できます。