一篇搞懂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);
    }
  }
}

运行结果:

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

相关文章
|
1天前
|
监控 Java
java异步判断线程池所有任务是否执行完
通过上述步骤,您可以在Java中实现异步判断线程池所有任务是否执行完毕。这种方法使用了 `CompletionService`来监控任务的完成情况,并通过一个独立线程异步检查所有任务的执行状态。这种设计不仅简洁高效,还能确保在大量任务处理时程序的稳定性和可维护性。希望本文能为您的开发工作提供实用的指导和帮助。
30 17
|
11天前
|
Java
Java—多线程实现生产消费者
本文介绍了多线程实现生产消费者模式的三个版本。Version1包含四个类:`Producer`(生产者)、`Consumer`(消费者)、`Resource`(公共资源)和`TestMain`(测试类)。通过`synchronized`和`wait/notify`机制控制线程同步,但存在多个生产者或消费者时可能出现多次生产和消费的问题。 Version2将`if`改为`while`,解决了多次生产和消费的问题,但仍可能因`notify()`随机唤醒线程而导致死锁。因此,引入了`notifyAll()`来唤醒所有等待线程,但这会带来性能问题。
Java—多线程实现生产消费者
|
13天前
|
安全 Java Kotlin
Java多线程——synchronized、volatile 保障可见性
Java多线程中,`synchronized` 和 `volatile` 关键字用于保障可见性。`synchronized` 保证原子性、可见性和有序性,通过锁机制确保线程安全;`volatile` 仅保证可见性和有序性,不保证原子性。代码示例展示了如何使用 `synchronized` 和 `volatile` 解决主线程无法感知子线程修改共享变量的问题。总结:`volatile` 确保不同线程对共享变量操作的可见性,使一个线程修改后,其他线程能立即看到最新值。
|
13天前
|
消息中间件 缓存 安全
Java多线程是什么
Java多线程简介:本文介绍了Java中常见的线程池类型,包括`newCachedThreadPool`(适用于短期异步任务)、`newFixedThreadPool`(适用于固定数量的长期任务)、`newScheduledThreadPool`(支持定时和周期性任务)以及`newSingleThreadExecutor`(保证任务顺序执行)。同时,文章还讲解了Java中的锁机制,如`synchronized`关键字、CAS操作及其实现方式,并详细描述了可重入锁`ReentrantLock`和读写锁`ReadWriteLock`的工作原理与应用场景。
|
14天前
|
安全 Java 编译器
深入理解Java中synchronized三种使用方式:助您写出线程安全的代码
`synchronized` 是 Java 中的关键字,用于实现线程同步,确保多个线程互斥访问共享资源。它通过内置的监视器锁机制,防止多个线程同时执行被 `synchronized` 修饰的方法或代码块。`synchronized` 可以修饰非静态方法、静态方法和代码块,分别锁定实例对象、类对象或指定的对象。其底层原理基于 JVM 的指令和对象的监视器,JDK 1.6 后引入了偏向锁、轻量级锁等优化措施,提高了性能。
37 3
|
14天前
|
存储 安全 Java
Java多线程编程秘籍:各种方案一网打尽,不要错过!
Java 中实现多线程的方式主要有四种:继承 Thread 类、实现 Runnable 接口、实现 Callable 接口和使用线程池。每种方式各有优缺点,适用于不同的场景。继承 Thread 类最简单,实现 Runnable 接口更灵活,Callable 接口支持返回结果,线程池则便于管理和复用线程。实际应用中可根据需求选择合适的方式。此外,还介绍了多线程相关的常见面试问题及答案,涵盖线程概念、线程安全、线程池等知识点。
96 2
|
22天前
|
安全 Java API
java如何请求接口然后终止某个线程
通过本文的介绍,您应该能够理解如何在Java中请求接口并根据返回结果终止某个线程。合理使用标志位或 `interrupt`方法可以确保线程的安全终止,而处理好网络请求中的各种异常情况,可以提高程序的稳定性和可靠性。
46 6
|
1月前
|
存储 监控 小程序
Java中的线程池优化实践####
本文深入探讨了Java中线程池的工作原理,分析了常见的线程池类型及其适用场景,并通过实际案例展示了如何根据应用需求进行线程池的优化配置。文章首先介绍了线程池的基本概念和核心参数,随后详细阐述了几种常见的线程池实现(如FixedThreadPool、CachedThreadPool、ScheduledThreadPool等)的特点及使用场景。接着,通过一个电商系统订单处理的实际案例,分析了线程池参数设置不当导致的性能问题,并提出了相应的优化策略。最终,总结了线程池优化的最佳实践,旨在帮助开发者更好地利用Java线程池提升应用性能和稳定性。 ####
|
30天前
|
安全 算法 Java
Java多线程编程中的陷阱与最佳实践####
本文探讨了Java多线程编程中常见的陷阱,并介绍了如何通过最佳实践来避免这些问题。我们将从基础概念入手,逐步深入到具体的代码示例,帮助开发者更好地理解和应用多线程技术。无论是初学者还是有经验的开发者,都能从中获得有价值的见解和建议。 ####
|
30天前
|
Java 调度
Java中的多线程编程与并发控制
本文深入探讨了Java编程语言中多线程编程的基础知识和并发控制机制。文章首先介绍了多线程的基本概念,包括线程的定义、生命周期以及在Java中创建和管理线程的方法。接着,详细讲解了Java提供的同步机制,如synchronized关键字、wait()和notify()方法等,以及如何通过这些机制实现线程间的协调与通信。最后,本文还讨论了一些常见的并发问题,例如死锁、竞态条件等,并提供了相应的解决策略。
51 3