当前位置: 首页 > 编程笔记 >

Java 多线程实例详解(二)

夏侯鹏
2023-03-14
本文向大家介绍Java 多线程实例详解(二),包括了Java 多线程实例详解(二)的使用技巧和注意事项,需要的朋友参考一下

本文承接上一篇文章《Java多线程实例详解(一)》。

四.Java多线程的阻塞状态与线程控制

上文已经提到Java阻塞的几种具体类型。下面分别看下引起Java线程阻塞的主要方法。

1.join()

join —— 让一个线程等待另一个线程完成才继续执行。如A线程线程执行体中调用B线程的join()方法,则A线程被阻塞,知道B线程执行完为止,A才能得以继续执行。

public class ThreadTest {

 public static void main(String[] args) {

 MyRunnable myRunnable = new MyRunnable();
 Thread thread = new Thread(myRunnable);

 for (int i = 0; i < 100; i++) {
  System.out.println(Thread.currentThread().getName() + " " + i);
  if (i == 30) {
  thread.start();
  try {
   thread.join(); // main线程需要等待thread线程执行完后才能继续执行
  } catch (InterruptedException e) {
   e.printStackTrace();
  }
  }
 }
 }
}

class MyRunnable implements Runnable {

 @Override
 public void run() {
 for (int i = 0; i < 100; i++) {
  System.out.println(Thread.currentThread().getName() + " " + i);
 }
 }
}

2.sleep()

sleep —— 让当前的正在执行的线程暂停指定的时间,并进入阻塞状态。在其睡眠的时间段内,该线程由于不是处于就绪状态,因此不会得到执行的机会。即使此时系统中没有任何其他可执行的线程,出于sleep()中的线程也不会执行。因此sleep()方法常用来暂停线程执行。

前面有讲到,当调用了新建的线程的start()方法后,线程进入到就绪状态,可能会在接下来的某个时间获取CPU时间片得以执行,如果希望这个新线程必然性的立即执行,直接调用原来线程的sleep(1)即可。

public class ThreadTest {

 public static void main(String[] args) {

 MyRunnable myRunnable = new MyRunnable();
 Thread thread = new Thread(myRunnable);

 for (int i = 0; i < 100; i++) {
  System.out.println(Thread.currentThread().getName() + " " + i);
  if (i == 30) {
  thread.start();
  try {
   Thread.sleep(1); // 使得thread必然能够马上得以执行
  } catch (InterruptedException e) {
   e.printStackTrace();
  }
  }
 }
 }
}

class MyRunnable implements Runnable {

 @Override
 public void run() {
 for (int i = 0; i < 100; i++) {
  System.out.println(Thread.currentThread().getName() + " " + i);
 }
 }
}

注:睡一个毫秒级够了,因为CPU不会空闲,会切换到新建的线程。

3.后台线程(Daemon Thread)

概念/目的:后台线程主要是为其他线程(相对可以称之为前台线程)提供服务,或“守护线程”。如JVM中的垃圾回收线程。

生命周期:后台线程的生命周期与前台线程生命周期有一定关联。主要体现在:当所有的前台线程都进入死亡状态时,后台线程会自动死亡(其实这个也很好理解,因为后台线程存在的目的在于为前台线程服务的,既然所有的前台线程都死亡了,那它自己还留着有什么用...伟大啊 ! !)。

设置后台线程:调用Thread对象的setDaemon(true)方法可以将指定的线程设置为后台线程。

public class ThreadTest {

 public static void main(String[] args) {
 Thread myThread = new MyThread();
 for (int i = 0; i < 100; i++) {
  System.out.println("main thread i = " + i);
  if (i == 20) {
  myThread.setDaemon(true);
  myThread.start();
  }
 }
 }

}

class MyThread extends Thread {

 public void run() {
 for (int i = 0; i < 100; i++) {
  System.out.println("i = " + i);
  try {
  Thread.sleep(1);
  } catch (InterruptedException e) {
  // TODO Auto-generated catch block
  e.printStackTrace();
  }
 }
 }
}

判断线程是否是后台线程:调用thread对象的isDeamon()方法。

注:main线程默认是前台线程,前台线程创建中创建的子线程默认是前台线程,后台线程中创建的线程默认是后台线程。调用setDeamon(true)方法将前台线程设置为后台线程时,需要在start()方法调用之前。前天线程都死亡后,JVM通知后台线程死亡,但从接收指令到作出响应,需要一定的时间。

4.改变线程的优先级/setPriority():

每个线程在执行时都具有一定的优先级,优先级高的线程具有较多的执行机会。每个线程默认的优先级都与创建它的线程的优先级相同。main线程默认具有普通优先级。

设置线程优先级:setPriority(int priorityLevel)。参数priorityLevel范围在1-10之间,常用的有如下三个静态常量值:

MAX_PRIORITY:10

MIN_PRIORITY:1

NORM_PRIORITY:5

获取线程优先级:getPriority()。

注:具有较高线程优先级的线程对象仅表示此线程具有较多的执行机会,而非优先执行。

public class ThreadTest {

 public static void main(String[] args) {
 Thread myThread = new MyThread();
 for (int i = 0; i < 100; i++) {
  System.out.println("main thread i = " + i);
  if (i == 20) {
  myThread.setPriority(Thread.MAX_PRIORITY);
  myThread.start();
  }
 }
 }

}

class MyThread extends Thread {

 public void run() {
 for (int i = 0; i < 100; i++) {
  System.out.println("i = " + i);
 }
 }
}

5.线程让步:yield()

上一篇博文中已经讲到了yield()的基本作用,同时,yield()方法还与线程优先级有关,当某个线程调用yiled()方法从运行状态转换到就绪状态后,CPU从就绪状态线程队列中只会选择与该线程优先级相同或优先级更高的线程去执行。

public class ThreadTest {

 public static void main(String[] args) {
 Thread myThread1 = new MyThread1();
 Thread myThread2 = new MyThread2();
 myThread1.setPriority(Thread.MAX_PRIORITY);
 myThread2.setPriority(Thread.MIN_PRIORITY);
 for (int i = 0; i < 100; i++) {
  System.out.println("main thread i = " + i);
  if (i == 20) {
  myThread1.start();
  myThread2.start();
  Thread.yield();
  }
 }
 }

}

class MyThread1 extends Thread {

 public void run() {
 for (int i = 0; i < 100; i++) {
  System.out.println("myThread 1 -- i = " + i);
 }
 }
}

class MyThread2 extends Thread {

 public void run() {
 for (int i = 0; i < 100; i++) {
  System.out.println("myThread 2 -- i = " + i);
 }
 }
}

 系列文章:

java 多线程实例讲解 (一)
Java 多线程实例详解(二)
Java 多线程实例详解(三)

 类似资料:
  • 本文向大家介绍Java 多线程实例详解(三),包括了Java 多线程实例详解(三)的使用技巧和注意事项,需要的朋友参考一下 本文主要接着前面多线程的两篇文章总结Java多线程中的线程安全问题。 一.一个典型的Java线程安全例子 上面例子很容易理解,有一张银行卡,里面有1000的余额,程序模拟你和你老婆同时在取款机进行取钱操作的场景。多次运行此程序,可能具有多个不同组合的输出结果。其中一种可能的输

  • 本文向大家介绍JAVA 多线程爬虫实例详解,包括了JAVA 多线程爬虫实例详解的使用技巧和注意事项,需要的朋友参考一下 JAVA 多线程爬虫实例详解 前言 以前喜欢Python的爬虫是出于他的简洁,但到了后期需要更快,更大规模的爬虫的时候,我才渐渐意识到Java的强大。Java有一个很好的机制,就是多线程。而且Java的代码效率执行起来要比python快很多。这份博客主要用于记录我对多线程爬虫的实

  • 本文向大家介绍java多线程下载实例详解,包括了java多线程下载实例详解的使用技巧和注意事项,需要的朋友参考一下 本文实例讲述了java多线程下载。分享给大家供大家参考,具体如下: 使用多线程下载文件可以更快完成文件的下载,多线程下载文件之所以快,是因为其抢占的服务器资源多。如:假设服务器同时最多服务100个用户,在服务器中一条线程对应一个用户,100条线程在计算机中并非并发执行,而是由CPU划

  • 本文向大家介绍Java多线程用法的实例详解,包括了Java多线程用法的实例详解的使用技巧和注意事项,需要的朋友参考一下 Java多线程用法的实例详解 前言: 最全面的java多线程用法解析,如果你对Java的多线程机制并没有深入的研究,那么本文可以帮助你更透彻地理解Java多线程的原理以及使用方法。 1.创建线程 在Java中创建线程有两种方法:使用Thread类和使用Runnable接口。在使用

  • 本文向大家介绍Java 多线程优先级实例详解,包括了Java 多线程优先级实例详解的使用技巧和注意事项,需要的朋友参考一下 Java 多线程优先级实例详解 线程的优先级将该线程的重要性传递给调度器。尽管CPU处理现有线程集的顺序是不确定的,但是调度器将倾向于让优先权最高的线程先执行。 你可以用getPriority()来读取现有线程的优先级,并且在任何时刻都可以通过setPriority()来修改

  • 本文向大家介绍Java多线程 线程组原理及实例详解,包括了Java多线程 线程组原理及实例详解的使用技巧和注意事项,需要的朋友参考一下 线程组 线程组可以批量管理线程和线程组对象。 一级关联 例子如下,建立一级关联。 输出结果如下 每隔三秒输出两个线程名称,符合预期。 线程组自动归组属性 输出结果如下 没有指定线程组,则归属到当前线程所属的组。 根线程组 运行结果 当前线程的线程组的父线程组是系统