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

运行结果:

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

相关文章
|
2天前
|
Java 数据库
【Java多线程】对线程池的理解并模拟实现线程池
【Java多线程】对线程池的理解并模拟实现线程池
11 1
|
1天前
|
安全 Java
【JAVA进阶篇教学】第十篇:Java中线程安全、锁讲解
【JAVA进阶篇教学】第十篇:Java中线程安全、锁讲解
|
1天前
|
安全 Java
【JAVA进阶篇教学】第六篇:Java线程中状态
【JAVA进阶篇教学】第六篇:Java线程中状态
|
1天前
|
缓存 Java
【JAVA进阶篇教学】第五篇:Java多线程编程
【JAVA进阶篇教学】第五篇:Java多线程编程
|
1天前
|
Java
【JAVA基础篇教学】第十二篇:Java中多线程编程
【JAVA基础篇教学】第十二篇:Java中多线程编程
|
1天前
|
安全 Java
java-多线程学习记录
java-多线程学习记录
|
2天前
|
Java
【Java多线程】面试常考 —— JUC(java.util.concurrent) 的常见类
【Java多线程】面试常考 —— JUC(java.util.concurrent) 的常见类
12 0
|
2天前
|
设计模式 消息中间件 安全
【Java多线程】关于多线程的一些案例 —— 单例模式中的饿汉模式和懒汉模式以及阻塞队列
【Java多线程】关于多线程的一些案例 —— 单例模式中的饿汉模式和懒汉模式以及阻塞队列
9 0
|
2天前
|
安全 Java 程序员
【Java多线程】面试常考——锁策略、synchronized的锁升级优化过程以及CAS(Compare and swap)
【Java多线程】面试常考——锁策略、synchronized的锁升级优化过程以及CAS(Compare and swap)
6 0
|
2天前
|
Java
【Java多线程】分析线程加锁导致的死锁问题以及解决方案
【Java多线程】分析线程加锁导致的死锁问题以及解决方案
11 1