一篇搞懂Java多线程(全网最细)(二)

简介: 一篇搞懂Java多线程(全网最细)(二)

5. 采用匿名内部类创建

第三种方式:采用匿名内部类创建线程

package com.thread;
/**
 * 匿名内部类创建线程
 * @author 云村小威
 *
 * @2023年7月21日 下午12:37:53
 */
public class Text03 {
  public static void main(String[] args) {
    Thread t = new Thread(new Runnable() {
      @Override
      public void run() {
        for (int i = 0; i < 5; i++) {
          System.out.println("t线程"+i);
        }
      }
    });
    //启动线程
    t.start();
    for (int i = 0; i < 5; i++) {
      System.out.println("main线程"+i);
    }
  }
}

运行:

说明 :

  1. Thread2类通过Runnable接口,使得该类有了多线程类的特征。Run()方法是多线程程序的一个约定。所有的多线程代码都在 run()方法里面。Thread类实际上也是实现了Runnable接口的类。
  2. 在启动的多线程的时候,需要先通过Thread类的构造方法Thread(Runnable target)构造出对象,然后调用Thread对象的start()方法来运行多线程代码。
  3. 实际上所有的多线程代码都是通过运行Thread的start()方法来运行的。因此,不管是扩展Thread类还是实现Runnable接口来实现多线程,最终还是通过Thread的对象的API来控制线程的,熟悉Thread类的API是进行多线程编程的基础。

6. Thread和Runnable的区别

      如果一个类继承Thread,则不适合资源共享。但是如果实现了Runnable接口的话,则很容易的实现资源贡共享。

总结 : 实现Runnable接口比继承Thread类具有的有优势

  • 适合多个相同的程序代码的线程去处理统一资源
  • 可以避免java中的单继承的限制
  • 增加程序的健壮性,代码可以被多个线程共享,代码和数据独立
  • 线程池只能放入实现Runnable或callable类线程,不能直接放入继承Therad的类

       main方法其也是一个线程。在java中所有的线程都是同时启动的,至于什么时候、那哪个先执行,完全看谁先得到CPU的资源。

       在java中,每次程序运行至少启动2个线程。一个是main线程,一个是垃圾收集线程。因为每当java命令执行一个类的时候,实际上都会启动一个JVM,每一个JVM实习就是在操作系统中启动了一个进程。

三、线程的生命周期

1、新建状态(new):新建一个线程对象

2、就绪状态(Runnable):线程对象创建后,其他线程调用了该对象的start()方法。该状态的线程位于可运行的线程池中,变得可运行,等待获取CPU的使用权

3、运行状态(Running):就绪状态的线程获取了CPU,执行程序代码

4、阻塞状态(Blocked):阻塞状态是线程因为某种原因放弃CPU使用权,暂时停止运行。直到线程进入就绪状态,才有机会转到运行状态

阻塞的情况分为三种 :

  • 等待阻塞:运行的线程执行wait()方法,JVM会把该线程放入等待池中。(wait方法会释放持有的锁)
  • 同步阻塞:运行的线程在获取对象的同步锁时,若该同步锁被别的线程占用,则JVM会把该线程放入锁池中。
  • 其他阻塞:运行的线程执行sleep()或join()方法,或者发出了I/O请求时,JVM会把该线程程置为阻塞状态。当sleep()状态超时、join()等待线程终止或者超时、或者I/O处理完毕时,线程重新转入就绪状。(sleep是不会释放持有的锁)

死亡状态(Dead):线程执行完了或者因异常退出了run()方法,该线程结束生命周期。

四、线程调度

1. 调整线程优先级 :

Java线程有优先级,优先级高的线程会获得较多的运行机会。

Java线程的优先级用整数表示,取值范围是1~10,Thread类有以下三个静态常量:

static int MAX_PRIORITY
        线程可以具有的最高优先级,取值为10。
static int MIN_PRIORITY
        线程可以具有最低优先级,取值为1。
static int NORM_PRIORITY
        分配给线程的默认优先级,取值为5。

Thread类的setPriority和getPriority()方法分别用来设置和获取线程的优先级。

每个线程都有默认的优先级,主线程的默认优先级为Thread.NORM_PRIORITY。

用法:

Thread t1 = new Thread("t1");
Thread t2 = new Thread("t2");
t1.setPriority(Thread.MAX_PRIORITY);
t2.setPriority(Thread.MIN_PRIORIRY);

注意:

      并不是设置了优先级就一定先执行,根据电脑性能等一些有影响还有一点随机性的,只是优先概率大一点。

2. 线程睡眠

Thread.sleep(long millis(毫秒)):让线程进入休眠(阻塞状态)放弃占有CPU时间片,让给其他线程使用。

sleep方法可做到间隔特定的时间,去执行一段特定的代码,每隔多久就执行一次。

package com.thread;
/**
 * 线程睡眠
 * @author 云村小威
 *
 * @2023年7月21日 下午12:37:53
 */
public class Text04 {
  public static void main(String[] args) {
    for (int i = 0; i < 10; i++) {
      System.out.println(Thread.currentThread().getName()+"-->"+i);
      //睡眠0.8秒
      try {
        Thread.sleep(800);
      } catch (InterruptedException e) {
        e.printStackTrace();
      }
    }
  }
}

运行:

3. 线程让步

Thread.yield()方法:暂停当前正在执行的线程对象,把执行机会让給相同或者更高优先级的线程。

package com.thread;
/**
 * 线程让步
 * @author 云村小威
 *
 * @2023年7月21日 下午12:37:53
 */
public class Text05 {
  public static void main(String[] args) {
    Thread t = new Thread(new Runnable() {
      @Override
      public void run() {
        for (int i = 0; i < 15; i++) {
          Thread.yield();
          System.out.println(Thread.currentThread().getName()+"-->"+i);
        }
      }
    });
    t.setName("t");
    t.start();
    //主线程
    for (int i = 0; i < 15; i++) {
      System.out.println(Thread.currentThread().getName()+"-->"+i);
    }
  }
}

运行结果:让主线程快走完了才运行

4. sleep()和yield()的区别

  1. sleep()使当前线程进入停滞状态,所以执行sleep()的线程在指定的时间内肯定不会被执行;yield()只是使当前线程重新回到可执行状态,所以执行yield()的线程有可能在进入到可执行状态后马上又被执行。
  2. sleep 方法使当前运行中的线程睡眼一段时间,进入不可运行状态,这段时间的长短是由程序设定的,yield 方法使当前线程让出 CPU 占有权,但让出的时间是不可设定的。
  3. 实际上,yield()方法对应了如下操作:先检测当前是否有相同优先级的线程处于同可运行状态,如有,则把CPU 的占有权交给此线程,否则,继续运行原来的线程。所以yield()方法称为“退让”,它把运行机会让给了同等优先级的其他线程
  4. 另外,sleep 方法允许较低优先级的线程获得运行机会,但 yield() 方法执行时,当前线程仍处在可运行状态,所以,不可能让出较低优先级的线程些时获得 CPU 占有权。在一个运行系统中,如果较高优先级的线程没有调用 sleep 方法,又没有受到 I\O阻塞,那么,较低优先级线程只能等待所有较高优先级的线程运行结束,才有机会运行。

5. 线程加入

Thread.join() 方法:用于等待其他线程终止

       如果线程A中调用了线程B的join方法,那么线程A阻塞,直到线程B执行完后,线程A从阻塞状态转为就绪状态,等待获取CPU的使用权。join方法要在start方法调用后调用才有效,线程必须启动,再加入。

package com.thread;
/**
 * 线程加入
 * 
 * @author 云村小威
 *
 * @2023年7月21日 下午12:37:53
 */
public class Text06 {
  public static void main(String[] args) throws InterruptedException {
    Thread t1 = new Thread(new JoinThread("t1"));
    Thread t2 = new Thread(new JoinThread("t2"));
    t1.start();
    t1.join();
    t2.start();
  }
}
class JoinThread implements Runnable {
  private String name;
  public JoinThread(String name) {
    this.name = name;
  }
  @Override
  public void run() {
    for (int i = 1; i <= 3; i++) {
      System.out.println(name + "-->" + i);
    }
  }
}

运行结果:

主线程一定会等子线程都结束了才结束!

相关文章
|
JSON 网络协议 安全
【Java】(10)进程与线程的关系、Tread类;讲解基本线程安全、网络编程内容;JSON序列化与反序列化
几乎所有的操作系统都支持进程的概念,进程是处于运行过程中的程序,并且具有一定的独立功能,进程是系统进行资源分配和调度的一个独立单位一般而言,进程包含如下三个特征。独立性动态性并发性。
636 1
|
JSON 网络协议 安全
【Java基础】(1)进程与线程的关系、Tread类;讲解基本线程安全、网络编程内容;JSON序列化与反序列化
几乎所有的操作系统都支持进程的概念,进程是处于运行过程中的程序,并且具有一定的独立功能,进程是系统进行资源分配和调度的一个独立单位一般而言,进程包含如下三个特征。独立性动态性并发性。
565 1
|
数据采集 存储 弹性计算
高并发Java爬虫的瓶颈分析与动态线程优化方案
高并发Java爬虫的瓶颈分析与动态线程优化方案
Java 数据库 Spring
533 0
|
算法 Java
Java多线程编程:实现线程间数据共享机制
以上就是Java中几种主要处理多线程序列化资源以及协调各自独立运行但需相互配合以完成任务threads 的技术手段与策略。正确应用上述技术将大大增强你程序稳定性与效率同时也降低bug出现率因此深刻理解每项技术背后理论至关重要.
729 16
|
缓存 并行计算 安全
关于Java多线程详解
本文深入讲解Java多线程编程,涵盖基础概念、线程创建与管理、同步机制、并发工具类、线程池、线程安全集合、实战案例及常见问题解决方案,助你掌握高性能并发编程技巧,应对多线程开发中的挑战。
|
数据采集 存储 前端开发
Java爬虫性能优化:多线程抓取JSP动态数据实践
Java爬虫性能优化:多线程抓取JSP动态数据实践
|
Java API 调度
从阻塞到畅通:Java虚拟线程开启并发新纪元
从阻塞到畅通:Java虚拟线程开启并发新纪元
602 83
|
安全 算法 Java
Java 多线程:线程安全与同步控制的深度解析
本文介绍了 Java 多线程开发的关键技术,涵盖线程的创建与启动、线程安全问题及其解决方案,包括 synchronized 关键字、原子类和线程间通信机制。通过示例代码讲解了多线程编程中的常见问题与优化方法,帮助开发者提升程序性能与稳定性。
585 0
|
存储 Java 调度
Java虚拟线程:轻量级并发的革命性突破
Java虚拟线程:轻量级并发的革命性突破
635 83