Maison > Java > javaDidacticiel > Analyse complète du multithreading Java Thread

Analyse complète du multithreading Java Thread

高洛峰
Libérer: 2017-01-05 15:27:44
original
1534 Les gens l'ont consulté

Le multi-threading est un point de connaissance très important en Java. Ici, l'éditeur résumera pour vous le multi-threading Java. J'espère que vous pourrez le maîtriser.

1. Cycle de vie des threads et cinq états de base

Concernant le cycle de vie des threads en Java, jetez d'abord un œil à l'image plus classique ci-dessous :

Java Thread多线程全面解析

L'image ci-dessus couvre essentiellement les points de connaissances importants du multi-threading en Java. Après avoir maîtrisé chaque point de connaissance de la figure ci-dessus, vous pouvez essentiellement maîtriser le multithreading en Java. Incluent principalement :

Le thread Java a cinq états de base

Nouvel état (Nouveau) : lorsque la paire d'objets thread est créée, elle entre dans le nouvel état, tel que : Thread t = new MyThread();

État prêt (Runnable) : lorsque la méthode start() (t.start();) de l'objet thread est appelée, le thread entre dans l'état prêt . Le thread à l'état prêt signifie uniquement que le thread est prêt et attend l'exécution de la planification du processeur à tout moment. Cela ne signifie pas que le thread sera exécuté immédiatement après l'exécution de t.start(); >Statut d'exécution (Running) : lorsque le processeur commence à planifier le thread à l'état prêt, le thread peut réellement être exécuté à ce moment-là, c'est-à-dire qu'il entre dans l'état d'exécution. Remarque : L'état prêt est la seule entrée vers l'état d'exécution. C'est-à-dire que si un thread veut entrer dans l'état d'exécution pour être exécuté, il doit d'abord être dans l'état prêt

Bloqué ; : en état d'exécution Pour une raison quelconque, le thread dans le thread abandonne temporairement le droit d'utiliser le CPU et arrête l'exécution. À ce moment-là, il entre dans l'état de blocage jusqu'à ce qu'il entre dans l'état prêt, il a la possibilité de l'être. appelé à nouveau par le CPU pour entrer dans l'état d'exécution.

Selon les différentes raisons de blocage, l'état de blocage peut être divisé en trois types :

1 En attente de blocage : le thread en cours d'exécution exécute la méthode wait(). pour que ce fil entre dans l'état de blocage en attente ;

2. Blocage synchrone - si le fil ne parvient pas à acquérir le verrou de synchronisation synchronisé (parce que le verrou est occupé par d'autres fils), il entrera dans le état de blocage synchronisé ;

3. Autre blocage - Lorsque sleep() ou join() du thread est appelé ou qu'une requête d'E/S est émise, le thread entre dans l'état de blocage. Lorsque l'état sleep() expire, join() attend que le thread se termine ou expire, ou que le traitement des E/S est terminé, le thread entre à nouveau dans l'état prêt.

État mort (Dead) : le thread a terminé l'exécution ou a quitté la méthode run() en raison d'une exception, et le thread termine son cycle de vie.

2. Création et démarrage de multi-threads Java

La création de threads en Java est courante sous trois formes de base

1 . Classe Inheritance Thread, remplacez la méthode run() de cette classe.

Comme indiqué ci-dessus, héritez de la classe Thread et définissez une nouvelle classe de thread MyThread en remplaçant la méthode run(). Le corps de la méthode run() représente les tâches que le thread doit accomplir. , appelé C'est le corps d'exécution du thread. Lorsque cet objet de classe de thread est créé, un nouveau thread est créé et entre dans le nouvel état de thread. En appelant la méthode start() référencée par l'objet thread, le thread entre dans l'état prêt. À ce stade, le thread peut ne pas être exécuté immédiatement, en fonction du timing de planification du processeur.

class MyThread extends Thread {
private int i = ;
@Override
public void run() {
for (i = ; i < ; i++) {
System.out.println(Thread.currentThread().getName() + " " + i);
}
}
} 
public class ThreadTest {
public static void main(String[] args) {
for (int i = ; i < ; i++) {
System.out.println(Thread.currentThread().getName() + " " + i);
if (i == ) {
Thread myThread = new MyThread(); // 创建一个新的线程 myThread 此线程进入新建状态
Thread myThread = new MyThread(); // 创建一个新的线程 myThread 此线程进入新建状态
myThread.start(); // 调用start()方法使得线程进入就绪状态
myThread.start(); // 调用start()方法使得线程进入就绪状态
}
}
}
}
Copier après la connexion

2. Implémentez l'interface Runnable et réécrivez la méthode run() de l'interface. La méthode run() est également un corps d'exécution de thread. Créez une instance de la classe d'implémentation Runnable et utilisez cette instance. en tant que Thread La cible de la classe est utilisée pour créer un objet Thread, qui est le véritable objet thread.

Je pense que tout le monde connaît les deux façons ci-dessus de créer de nouveaux fils de discussion, alors quelle est la relation entre Thread et Runnable ? Regardons d'abord l'exemple suivant.

class MyRunnable implements Runnable {
private int i = ;
@Override
public void run() {
for (i = ; i < ; i++) {
System.out.println(Thread.currentThread().getName() + " " + i);
}
}
} 
public class ThreadTest {
public static void main(String[] args) {
for (int i = ; i < ; i++) {
System.out.println(Thread.currentThread().getName() + " " + i);
if (i == ) {
Runnable myRunnable = new MyRunnable(); // 创建一个Runnable实现类的对象
Thread thread = new Thread(myRunnable); // 将myRunnable作为Thread target创建新的线程
Thread thread = new Thread(myRunnable);
thread.start(); // 调用start()方法使得线程进入就绪状态
thread.start();
}
}
}
}
Copier après la connexion

De même, la méthode de création d'un fil de discussion en implémentant l'interface Runnable est similaire, mais la différence est

public class ThreadTest {
public static void main(String[] args) {
for (int i = ; i < ; i++) {
System.out.println(Thread.currentThread().getName() + " " + i);
if (i == ) {
Runnable myRunnable = new MyRunnable();
Thread thread = new MyThread(myRunnable);
thread.start();
}
}
}
}
class MyRunnable implements Runnable {
private int i = ;
@Override
public void run() {
System.out.println("in MyRunnable run");
for (i = ; i < ; i++) {
System.out.println(Thread.currentThread().getName() + " " + i);
}
}
}
class MyThread extends Thread {
private int i = ;
public MyThread(Runnable runnable){
super(runnable);
}
@Override
public void run() {
System.out.println("in MyThread run");
for (i = ; i < ; i++) {
System.out.println(Thread.currentThread().getName() + " " + i);
}
}
}
Copier après la connexion

Cette méthode peut-elle créer un nouveau fil de discussion en douceur ? La réponse est oui. Quant au corps d'exécution du thread à ce stade, s'agit-il de la méthode run() dans l'interface MyRunnable ou de la méthode run() dans la classe MyThread ? Grâce à la sortie, nous savons que le corps d'exécution du thread est la méthode run() de la classe MyThread. En fait, la raison est très simple, car la classe Thread elle-même implémente également l'interface Runnable et la méthode run() a été définie pour la première fois dans l'interface Runnable.

Thread thread = new MyThread(myRunnable);
Copier après la connexion

Regardons l'implémentation de la méthode run() dans l'interface Runnable de la classe Thread :

public interface Runnable {
public abstract void run();
}
Copier après la connexion

En d'autres termes, lorsque la méthode run() dans la classe Thread est exécuté Lorsque la cible existe, il sera d'abord jugé si la cible existe, et si elle existe, la méthode run() dans la cible sera exécutée, c'est-à-dire la méthode run() dans la classe qui implémente l'interface Runnable et remplace la méthode run(). Cependant, dans l'exemple donné ci-dessus, en raison de l'existence d'un polymorphisme, la méthode run() de la classe Thread n'est pas exécutée du tout, mais la méthode run() du type d'exécution, c'est-à-dire la classe MyThread, est directement exécuté en premier.

  @Override
public void run() {
if (target != null) {
target.run();
}
}
Copier après la connexion

3. Utilisez les interfaces Callable et Future pour créer des fils de discussion. Plus précisément, créez une classe d’implémentation de l’interface Callable et implémentez la méthode clam(). Et utilisez la classe FutureTask pour envelopper l'objet de la classe d'implémentation Callable et utilisez cet objet FutureTask comme cible de l'objet Thread pour créer un thread.

Cela semble un peu compliqué, mais cela deviendra clair si vous regardez simplement un exemple.

public class ThreadTest {
public static void main(String[] args) {
Callable<Integer> myCallable = new MyCallable(); // 创建MyCallable对象
FutureTask<Integer> ft = new FutureTask<Integer>(myCallable); //使用FutureTask来包装MyCallable对象
for (int i = ; i < ; i++) {
System.out.println(Thread.currentThread().getName() + " " + i);
if (i == ) {
Thread thread = new Thread(ft); //FutureTask对象作为Thread对象的target创建新的线程
thread.start(); //线程进入到就绪状态
}
}
System.out.println("主线程for循环执行完毕..");
try {
int sum = ft.get(); //取得新创建的新线程中的call()方法返回的结果
System.out.println("sum = " + sum);
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
}
}
class MyCallable implements Callable<Integer> {
private int i = ;
// 与run()方法不同的是,call()方法具有返回值
@Override
public Integer call() {
int sum = ;
for (; i < ; i++) {
System.out.println(Thread.currentThread().getName() + " " + i);
sum += i;
}
return sum;
}
}
Copier après la connexion

首先,我们发现,在实现Callable接口中,此时不再是run()方法了,而是call()方法,此call()方法作为线程执行体,同时还具有返回值!在创建新的线程时,是通过FutureTask来包装MyCallable对象,同时作为了Thread对象的target。那么看下FutureTask类的定义:

public class FutureTask<V> implements RunnableFuture<V> {
//....
} 
public interface RunnableFuture<V> extends Runnable, Future<V> {
void run();
}
Copier après la connexion

于是,我们发现FutureTask类实际上是同时实现了Runnable和Future接口,由此才使得其具有Future和Runnable双重特性。通过Runnable特性,可以作为Thread对象的target,而Future特性,使得其可以取得新创建线程中的call()方法的返回值。

执行下此程序,我们发现sum = 4950永远都是最后输出的。而“主线程for循环执行完毕..”则很可能是在子线程循环中间输出。由CPU的线程调度机制,我们知道,“主线程for循环执行完毕..”的输出时机是没有任何问题的,那么为什么sum =4950会永远最后输出呢?

原因在于通过ft.get()方法获取子线程call()方法的返回值时,当子线程此方法还未执行完毕,ft.get()方法会一直阻塞,直到call()方法执行完毕才能取到返回值。

上述主要讲解了三种常见的线程创建方式,对于线程的启动而言,都是调用线程对象的start()方法,需要特别注意的是:不能对同一线程对象两次调用start()方法。

三. Java多线程的就绪、运行和死亡状态

就绪状态转换为运行状态:当此线程得到处理器资源;

运行状态转换为就绪状态:当此线程主动调用yield()方法或在运行过程中失去处理器资源。

运行状态转换为死亡状态:当此线程线程执行体执行完毕或发生了异常。

此处需要特别注意的是:当调用线程的yield()方法时,线程从运行状态转换为就绪状态,但接下来CPU调度就绪状态中的哪个线程具有一定的随机性,因此,可能会出现A线程调用了yield()方法后,接下来CPU仍然调度了A线程的情况。

由于实际的业务需要,常常会遇到需要在特定时机终止某一线程的运行,使其进入到死亡状态。目前最通用的做法是设置一boolean型的变量,当条件满足时,使线程执行体快速执行完毕。如:

public class ThreadTest {
public static void main(String[] args) {
MyRunnable myRunnable = new MyRunnable();
Thread thread = new Thread(myRunnable);
for (int i = ; i < ; i++) {
System.out.println(Thread.currentThread().getName() + " " + i);
if (i == ) {
thread.start();
}
if(i == ){
myRunnable.stopThread();
}
}
}
}
class MyRunnable implements Runnable {
private boolean stop;
@Override
public void run() {
for (int i = ; i < && !stop; i++) {
System.out.println(Thread.currentThread().getName() + " " + i);
}
}
public void stopThread() {
this.stop = true;
}
}
Copier après la connexion

以上所述是小编给大家介绍的Java Thread多线程全面解析,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对PHP中文网的支持!

更多Java Thread多线程全面解析相关文章请关注PHP中文网!


Étiquettes associées:
source:php.cn
Déclaration de ce site Web
Le contenu de cet article est volontairement contribué par les internautes et les droits d'auteur appartiennent à l'auteur original. Ce site n'assume aucune responsabilité légale correspondante. Si vous trouvez un contenu suspecté de plagiat ou de contrefaçon, veuillez contacter admin@php.cn
Tutoriels populaires
Plus>
Derniers téléchargements
Plus>
effets Web
Code source du site Web
Matériel du site Web
Modèle frontal