第20章の概要

 20.1 スレッドの概要。

20.2 スレッドの作成


2.1 Thread クラスの継承
Thread クラスは、java.lang パッケージ内のクラスです。このクラスからインスタンス化されたオブジェクトはスレッドを表します。プログラマは、スレッドを開始するにはスレッドを作成する必要があります。新しいスレッド例。 Thread クラスで一般的に使用される 2 つのコンストラクターは次のとおりです。

public Thread(): 新しいスレッド オブジェクトを作成します。
public Thread(String threadName): threadName という名前のスレッド オブジェクトを作成します。
Thread クラスを継承して新しいスレッドを作成するための構文は次のとおりです。

パブリック クラス ThreadTest extends Thread{ }

スレッドの実際の関数を完了するコードは、クラスの run( メソッドに配置されます。クラスが Thread クラスを継承する場合、クラス内の run() メソッドをオーバーライドして、次のコードを作成できます。スレッド関数を run() メソッドに実装し、Thread クラスの start() メソッドを呼び出してスレッドを実行します。つまり、run() メソッドを呼び出します。
Thread オブジェクトには次のものが必要です。実行するタスク。タスクは開始時にスレッドを参照します。作業を実行するには、作業の機能コードが run() メソッドに記述されます。run() メソッドは次の構文形式を使用する必要があります。

public void run(){ }

 

2.2 Runnable インターフェイスを実装する (単一継承なので、インターフェイスを使用します)
インターフェイス構文を実装します。

public class Thread extends オブジェクトは Runnable を実装します

Runnable インターフェイスを実装するプログラムは、Thread オブジェクトを作成し、Runnable オブジェクトを Thread オブジェクトに関連付けます。 Thread    
クラスには次の 2 つのコンストラクターがあります。

☑パブリック スレッド(実行可能なターゲット)
☑パブリック スレッド(実行可能なターゲット、文字列名)

これら 2 つの構築メソッドのパラメータには Runnable インスタンスがあります。上記の構築メソッドを使用して、Runnable インスタンスと Thread インスタンスを関連付けることができます。
Runnable インターフェイスを使用して新しいスレッドを開始する手順は次のとおりです。

(1) Runnable オブジェクトを作成します。
(2) パラメータを使用して、Runnable オブジェクトのコンストラクタ メソッドの Thread インスタンスを作成します。 (3) start() メソッドを呼び出してスレッドを開始します。
Runnable インターフェイスを通じてスレッドを作成する場合、プログラマはまず Runnable インターフェイスを実装するクラスを作成し、次にそのクラスのオブジェクトをインスタンス化して Runnable オブジェクトを作成する必要があります。その後、対応するコンストラクター Thread インスタンスを作成し、最後にそのインスタンスを使用して Thread クラスの start() メソッドを呼び出し、スレッドを開始します。

 

 import java.awt.Container;
 
import javax.swing.JFrame;
import javax.swing.JLabel;
 
public class SwingAndThread extends JFrame{
	int count =0;
	public SwingAndThread(){
		setBounds(300,200,250,100);	//绝对定位窗体大小与位置	
		Container container = getContentPane();	//主容器	
		container.setLayout(null);	//使窗体不使用任何布局管理器	
		lcon icon = new lmagelcon("/D20/src/1.gif");	//图标对象	
		JLabel jl = new JLabel(icon);	//显示图标的标签	
		jl.setBounds(10,10,200,50);	//设置标签的位置与大小	
		Thread t = new Thread(){	//定义匿名线程对象	
		public void run() {
		while (true) {
		jl.setBounds(count,10,200,50);	//将标签的横坐标用变量表示	
		try{
			Thread.sleep(500);	//使线程休眠 500毫秒	
		} catch (InterruptedException e){
		e.printStackTrace();}
		count += 4;	//使横坐标每次增加4	
		if (count >= 200){
		count = 10;	//当图标到达标签的最右边时,使其回到标签最左边	
		}
		}
		}
		};	t.start();	container.add(jl);	//启动线程	//将标签添加到容器中	
		setVisible(true);	//使窗体可见	
		setDefaultCloseOperation(EXIT_ON_CLOSE);	//设置窗体的关闭方式
	}
 
	public static void main(String[] args) {
		new SwingAndThread();
 
	}
 
}

20.3 スレッドのライフサイクル

スレッドにはライフ サイクルがあり、これには 7 つの状態、つまり、誕生状態、準備完了状態、実行状態、待機状態、スリープ状態、ブロッキング状態、および終了状態が含まれます。誕生状態は、スレッドが作成される状態です。ユーザーがスレッド インスタンスを使用して start() メソッドを呼び出す前、スレッドは誕生状態にあります。ユーザーが start() メソッドを呼び出すと、スレッドは準備完了状態になります。状態 (実行可能状態とも呼ばれます); スレッドがシステム リソースを取得すると、実行状態に入ります。
スレッドが実行可能状態になると、準備完了状態と実行状態の間で遷移し、待機状態、スリープ状態、ブロック状態、または停止状態になる場合もあります。実行状態のスレッドが Thread クラスの wait() メソッドを呼び出すと、スレッドは待機状態になります。待機状態に入ったスレッドは、Thread クラスの notify0 メソッドを呼び出して目覚める必要があり、notifyAl1O) メソッドを呼び出す必要があります。待機状態のスレッドが起動し、スレッドが Thread クラスの sleep( メソッドを呼び出すとスリープ状態になります。実行状態で入出力要求を発行すると、スレッドはスリープ状態になります。ブロック状態になり入力を待ちます。/出力が終了すると、スレッドは準備完了状態になります。ブロックされたスレッドの場合、システム リソースがアイドル状態であっても、スレッドは実行状態に戻ることができません。スレッドの run() メソッドが実行されると、スレッドは実行状態に戻ります。 < a i=2> 説明 スレッドをさまざまな状態に置く方法については、セクション 20.4 で説明します。ここでは、読者が必要とすることだけが必要です。スレッドの複数の状態を理解する


20.4 スレッドの操作方法
4.1 スレッドスリープ
	import java.awt.*;
	import java.util.Random; import javax.swing.*;
	public class SleepMethodTest extends JFrame {
	private static Color[]color={ Color.BLACK, Color.BLUE,Color.CYAN,Color.GREEN,Color.ORANGE, Color.YELLOW, Color.RED,Color.PINK,Color.LIGHT_GRAY};//定义颜色数组
	private static final Random rand=new Random();	//创建随机对象	
	private static Color getC(){	//获取随机颜色值的方法	
	return color[(rand.nextInt(color.length))];
	}
	public SleepMethodTest() {
	Thread t = new Thread(new Runnable() {//创建匿名线程对象	
		int x=30;	Il定义初始坐标	
		int y=50;
		public void run(){
		while (true) {	//无限循环	
		try{	Thread.sleep(100);	//线程休眠 0.1秒	
		} catch (InterruptedException e){
		e.printStackTrace();
		Graphics graphics=getGraphics();	//获取组件绘图上下文对象	
		graphics.setColor(getC());	//设置绘图颜色	
		graphics.drawLine(x,y,100,y++);	//绘制直线并递增垂直坐标	
		if (y >=80) {
		y = 50;
		}
		}
		}
		});
		t.start();	//启动线程	
		}
	
		public static void main(String[]args){
		init(new SleepMethodTest(),100,100);
		}
		public static void init(JFrame frame, int width, int height){//初始化程序界面的方法
		frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); 
		frame.setSize(width, height); frame.setVisible(true);
		
	}
 
}
4.2 スレッドの追加

join() メソッドを使用してスレッドに参加する

import java.awt.BorderLayout;
import javax.swing.*;
public class JoinTest extends JFrame {
private Thread threadA;	//定义两个线程	
private Thread threadB;
private JProgressBar progressBar = new JProgressBar();	//定义两个进度条组件	
private JProgressBar progressBar2=new JProgressBar();
public static void main(String[] args) {
	JoinTest test = new JoinTest(); test.setVisible(true);
	public JoinTest(){
		setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); setBounds(200,200,200,100);
		getContentPane().add(progressBar, BorderLayout.NORTH); //将进度条设置在窗体最北面 getContentPane().add(progressBar2,BorderLayout.SOUTH); //将进度条设置在窗体最南面
		progressBar.setStringPainted(true);	//设置进度条显示数字字符	
		progressBar2.setStringPainted(true);
		threadA = new Thread(new Runnable() {	//使用匿名内部类形式初始化 Thread实例	
	int count = 0;
	public void run() {	//重写 run()方法	
			while (true) {
				progressBar.setValue(++count);	//设置进度条的当前值	
				try {
					Thread.sleep(100);	//使线程A 休眠 100毫秒	
					threadB.join();	//使线程B调用 join()方法	
				}catch (InterruptedException e){
					e.printStackTrace();
				}
			}
	}
			});
	threadA.start();	//启动线程A
	threadB= new Thread(new Runnable(){
		int count = 0;
		public void run() {
		while (true) {
		progressBar2.setValue(++count);	//设置进度条的当前值	
		try{	Thread.sleep(100);	//使线程B休眠 100 毫秒	
		} catch (InterruptedException e) {
		e.printStackTrace();
		if (count ==100)	break;	//跳出循环	
		}
		}
		});	//启动线程B	
		threadB.start();
		}
	}
  4.3 スレッドの中断


run() メソッドで無限ループの形式を使用し、その後、ブール フラグを使用してループの停止を制御することが推奨されます。
sleep() または wait() メソッドを使用したためにスレッドが準備完了状態になった場合は、Thread クラスの中断) メソッドを使用して、スレッドを run() メソッドから離脱させることができます。同時にスレッドを終了しますが、プログラムは InterruptedException をスローし、ユーザーは while ループの終了など、例外を処理するときにスレッドの中断されたビジネス処理を完了できます。
次の例は、interrupted0 メソッドを使用するスレッドを示しています。同時に、プログラムは InterruptedException 例外をスローし、例外処理中に while ループを終了します。プロジェクトでは、データベース接続の終了やソケット接続の終了などの操作がここで実行されることがよくあります。

import java.awt.BorderLayout;
import java.awt.event.*;
import javax.swing.*;
	public class InterruptedSwing extends JFrame {
		
		public InterruptedSwing(){
		JProgressBar progressBar = new JProgressBar();	//创建进度条	
		getContentPane().add(progressBar, BorderLayout.NORTH);//将进度条放置在窗体合适位置
		JButton button=new JButton("停止");
		getContentPane().add(button,BorderLayout.SOUTH);
		progressBar.setStringPainted(true);	//设置进度条上显示数字	
		Thread t = new Thread(new Runnable(){
		int count = 0; public void run() {
		while (true){
			progressBar.setValue(++count);//设置进度条的当前值
		try{ 
			Thread.sleep(100);	//使线程休眠 100毫秒	
		} catch (InterruptedException e){//捕捉InterruptedException 异常
			System.out.println("当前线程序被中断"); 
			break;}
		}
		}
		}
		);
		button.addActionListener(new ActionListener(){
		@Override
		public void actionPerformed(ActionEvent e){
		t.interrupt();	//中断线程	
		}
		});	t.start();	//启动线程	
		}
		public static void init(JFrame frame, int width, int height) {
		frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); frame.setSize(width, height); frame.setVisible(true);
		}
 
		public static void main(String[] args){
		init(new InterruptedSwing(),100,100);
		
	}
 
}
4.4 スレッドの礼儀


Thread クラスは、yield() メソッドで表される丁寧なメソッドを提供します。これは、現在実行中のスレッドに、リソースを使用できることを通知するリマインダーを与えるだけです。提供: 他のスレッド ただし、これは単なるものです。ヒントとして、現在のスレッドがリソースを解放することを保証するメカニズムはありません。 yield() メソッドは、同じ優先度を持つスレッドに実行可能状態に入る機会を与え、現在のスレッドが実行権を放棄すると再び準備完了状態に戻ります。マルチタスクをサポートするオペレーティング システムの場合、オペレーティング システムが実行するスレッドに CPU タイム スライスを自動的に割り当てるため、yield() メソッドを呼び出す必要はありません。


 

public class PT implements Runnable {
	String name;
	public PT(String name) {
	this.name = name; 
	}
	@Override
	public void run() {
	String tmp= "";
	for (int i= 0; i< 50000; i++) {
	tmp +=i;
//完成5万次字符串拼接
	}
	System.out.println(name +"线程完成任务");
	}
	public static void main(String[]args){
	Thread a = new Thread(new PT("A"));
	a.setPriority(1);	//A线程优先级最小	
	Thread b = new Thread(new PT("B"));
	b.setPriority(3);
	Thread c = new Thread(new PT("C"));	
	c.setPriority(7);	
	Thread d = new Thread(new PT("D"));
	d.setPriority(10);	//D线程优先级最大	
	a.start();	
	b.start();	
	c.start();	
	d.start();	
	}
	}

スレッドの優先度はsetPriority()メソッドで調整できますが、このメソッドで設定した優先度が1~10以外の場合はIllegalArgumentException例外が発生します。

20.6 スレッドの同期
シングルスレッド プログラムでは、一度に 1 つのことしか実行できません。後続の処理は、前の処理が完了するまで待つ必要があります。ただし、複数の In スレッド プログラムを使用すると、2 人が同時に会話したり、2 人が同じ単板橋を同時に渡ったりするなど、2 つのスレッドがリソースを占有する問題が発生します。したがって、マルチスレッドプログラミングでは、これらのリソースへのアクセスの競合を防ぐ必要があります。 Java は、リソース アクセスの競合を防ぐためのスレッド同期メカニズムを提供します。


6.1 スレッドの安全性

実際の開発では、マルチスレッド プログラムがよく使用されます。マルチスレッド プログラムを作成するときは、スレッド セーフティの問題を考慮する必要があります。実際、スレッド セーフティの問題は、2 つのスレッドが 1 つのオブジェクトのデータに同時にアクセスすることによって発生します。
 

 
	public class ThreadSafeTest implements Runnable {
		//设置当前总票数
		int num = 10;
		public void run() {
		//设置无限循环
		while (true) {
		if (num > 0){	//判断当前票数是否大于0	
		try {
		Thread.sleep(100);	//使当前线程休眠100毫秒	
		} catch (InterruptedException e) {
			e.printStackTrace();
			}
		System.out.println(Thread.currentThread().getName()+ "----票数"+ num--);//票数减1
		}
	}
	}
		public static void main(String[]args){
		ThreadSafeTest t= new ThreadSafeTest();	//实例化类对象	
		Thread tA = new Thread(t,"线程一");	//以该类对象分别实例化4个线程	
		Thread tB = new Thread(t,"线程二"); Thread tC = new Thread(t,"线程三"); Thread tD= new Thread(t,"线程四");
		tA.start();	//分别启动线程	
		tB.start(); 
		tC.start();
		tD.start();
		}
}

この結果から、出力された残りの投票がマイナスであることがわかり、問題が発生します。これは、4 つのスレッドが同時に作成されるためです。これら 4 つのスレッドは run() メソッドを実行します。num 変数が 1 の場合、スレッド 1、スレッド 2、スレッド 3、およびスレッド 4 はすべて、num 変数の記憶関数を持ちます。スレッド 1 が run() メソッドを実行するとき、デクリメント操作を実行する前に、sleep() メソッドを呼び出して準備完了状態に入るように指定します。この種のマルチスレッド このとき、スレッド 2、スレッド 3、スレッド 4 も run() メソッドに入り、num 変数が 0 より大きい場合、実行量はまだ 0 より大きいことがわかりましたが、この時点ではスレッド 1 のスリープ時間が経過し、num 変数が変更されると、最初のスレッドの値がデクリメントされます。同時に、スレッド 2、スレッド 3、およびスレッド 4 も num 変数をデクリメントし、負の値になります。 。

6.2 スレッド同期メカニズム
1. 同期ブロック
Java は、リソースの競合を効果的に防止できる同期メカニズムを提供します。同期メカニズムは synchronized キーワードを使用します。 
 構文は次のとおりです。 

同期済み (オブジェクト){ }

通常、共有リソースに対する操作は synchronized で定義された領域に配置されるため、他のスレッドがロックを取得するときは、その領域に入る前にロックが解放されるのを待つ必要があります。オブジェクトは任意のオブジェクトであり、各オブジェクトにはフラグ ビットがあり、それぞれ 0 と 1 の 2 つの値があります。スレッドが同期ブロックに到達すると、まずオブジェクトのフラグ ビットをチェックします。フラグ ビットが 0 の場合は、この同期ブロックに他のスレッドが存在することを示します。この時点で、現在のスレッドはスレッドが実行されるまで準備完了状態になります。同期ブロック内のコードの実行は、同期ブロックの実行を完了します。コードが渡された後、オブジェクトの識別ビットは 1 に設定され、現在のスレッドは同期ブロック内のコードの実行を開始でき、オブジェクトの識別ビットはobject は 0 に設定され、他のスレッドが同期ブロック内のコードを実行するのを防ぎます。
 

public class SynchronizedTest implements Runnable { 
	int num = 10;
 
//设置当前总票数
	public void run() {
		while (true){	//设置无限循环	
			synchronized (this){	//设置同步代码块	
				if (num > 0) {	//判断当前票数是否大于0	
					try {
						Thread.sleep(100);	//使当前线程体眠 100
					} catch (InterruptedException e) {
						e.printStackTrace();}
						//票数减 1
						System.out.println(Thread.currentThread().getName()+"--票数" + num--);
						}
				}
			}
		}
						public static void main(String[] args){
						//实例化类对象
						SynchronizedTest t = new SynchronizedTest();//以该类对象分别实例化4个线程 
						Thread tA= new Thread(t,"线程一"); 
						Thread tB = new Thread(t,"线程二");
						Thread tC = new Thread(t,"线程三"); 
						Thread tD = new Thread(t,"线程四");
						tA.start();	//分别启动线程	
						tB.start(); 
						tC.start(); 
						tD.start();
						}
						}

2. 同期方法

synchronized メソッドは、メソッドの前に synchronized キーワードを付けて変更したメソッドで、その構文は次のとおりです。

同期された void f(){}

オブジェクト上で同期メソッドが呼び出された場合、オブジェクト上の他の同期メソッドは、同期メソッドが完了するまで待機してから実行する必要があります。共有リソースにアクセスできるすべてのメソッドは、同期されるように変更する必要があります。そうしないと、エラーが発生します。
例 20.7 のコードを変更し、共有リソース操作を同期メソッドに配置します。コードは次のとおりです。


 

int num = 10;
public synchronized void doit() {    //定义同步方法
if(num>0){
try{
Thread.sleep(10);
}catch(interruptedException e){
e.printStackTrace():
System.out.printin(Thread.currentThread().getName()+"-票数” +num--);
public void run(){
	while(true){
doit():	//在run()方法中调用该同步方法

おすすめ

転載: blog.csdn.net/Leonie1/article/details/134717040