首页 > 编程语言 >Java中Thread类的常用API以及使用示例

Java中Thread类的常用API以及使用示例

时间:2022-08-30 09:34:22浏览次数:84  
标签:Java String Thread 示例 System 线程 sleep public

场景

Java语言是支持多线程的,一个正在运行的Java程序可以称之为一个进程(process),在每个进程里面包含多个线程,线程是进程中单一的顺序控制流,CPU在执行计算机指令的时候都是按顺序执行,但是由于其执行速度很快,可以把时间分成很细小的时间片,交替执行,线程和进程的区别在于:

创建进程的开销大于创建线程的开销,进程之间的通信比线程间要难
线程不能独立存在,依托于进程而存在,线程也可以看作轻量级的进程
多进程的稳定性高于多线程,一个进程的运行不会影响其他进程,但线程崩溃往往会引起程序的崩溃
Thread类位于java.lang包,JDK1.0引入。在HotSpot虚拟机中,线程使用的是基于操作系统的1 : 1的内核实现模型来创建线程,线程的创建、调度、执行、销毁等由内核进行控制,调度过程通过抢占式策略进行调度。

下面记录Thread类的常用API。

注:

博客:
https://blog.csdn.net/badao_liumang_qizhi
关注公众号
霸道的程序猿
获取编程相关电子书、教程推送与免费下载。

实现

1、sleep线程休眠

sleep方法会使当前线程进入指定毫秒数的休眠,暂停执行,虽然给定一个休眠时间,但是最终要以系统的定时器和调度器的精度为准。

public class ApiDemo {

    public static void main(String[] args) throws InterruptedException {
        //1、线程sleep
        //sleep方法会使当前线程进入指定毫秒数的休眠,暂停执行,虽然给定一个休眠时间,但是最终要以系统的定时器和调度器的精度为准
          new Thread(()->{
               long startTime = System.currentTimeMillis();
               sleep(2000L);
               long endTime = System.currentTimeMillis();
               System.out.println(String.format("总共花费时间:%d ms",(endTime-startTime)));
           }).start();

           long startTime = System.currentTimeMillis();
           sleep(3000l);
           long endTime = System.currentTimeMillis();
           System.out.println(String.format("主线程总共花费时间:%d ms",(endTime-startTime)));

        //输出结果
       //总共花费时间:2012 ms
       // 主线程总共花费时间:3006 ms
       //分别在自定义的线程和主线程中进行了休眠,每个线程的休眠互不影响。

    }

2、使用TimeUnit代替Thread.sleep

在JDK1.5以后,引入了枚举TimeUnit,其对sleep进行了很好的封装,使用它可以省去时间单位的换算步骤

        //休眠3000毫秒
        //TimeUnit.MILLISECONDS.sleep(3000);
        //休眠4秒
        //TimeUnit.SECONDS.sleep(4);
        //休眠1分钟
        //TimeUnit.MINUTES.sleep(1);
        //休眠1小时
        //TimeUnit.HOURS.sleep(1);

3、yield方法

会提醒调度器自愿放弃当前的CPU资源,如果CPU资源不紧张,则会忽略这种提醒。

package com.ruoyi.demo.thread;

import java.util.concurrent.TimeUnit;
import java.util.stream.IntStream;

import static jodd.util.ThreadUtil.sleep;

public class ApiDemo {

    public static void main(String[] args) throws InterruptedException {

        IntStream.range(0,2).mapToObj(ApiDemo::create).forEach(Thread::start);

    }

    private static Thread create(int index){
        return new Thread(()->{
            //如果不加index为0调用yield方法,输出结果有时候0在前,有时候1在前
            //当调用了yield方法,顺序始终是0,1,因为index为0的线程如果最先获取了CPU资源会高速CPU调度器放弃了原本属于自己的资源
            //但是yield只是一个提示,不能保证每次都能满足
            if(index == 0){
                Thread.yield();
            }
            System.out.println(index);
        });
    }

}

4、setPriority设置线程优先级

理论上优先级较高的线程会获取优先被CPU调度的机会,但是也只是一个提示作用,不能保证

每次都是这样。

 

       Thread t1 = new Thread(()->{
           while(true)
           {
               System.out.println("t1");
           }
        });
        t1.setPriority(3);

        Thread t2 = new Thread(()->{
            while(true)
            {
                System.out.println("t2");
            }
        });
        t2.setPriority(1);

        t1.start();
        t2.start();

通过设置t1的优先级高,所以t1的输出频率要高于t2。

除了设置优先级,还有getPriority()获取线程的优先级。

5、获取当前线程currentThread以及获取线程ID

获取当前线程currentThread,getId()获取线程的唯一ID。

        //4、获取线程ID
        new Thread(() -> {
            System.out.println(Thread.currentThread().getId());
        }).start();
        //5、获取当前线程以及名字
        new Thread(() -> {
            System.out.println(Thread.currentThread().getName());
        }).start();

6、interrupt打断堵塞

调用wait、sleep、join等方法时会使线程进入堵塞状态,而调用interrupt方法就可以打断堵塞。

一旦在堵塞的情况下被打断,都会抛出一个InterruptedException的异常。

        Thread thread = new Thread(() -> {
            try {
                TimeUnit.MINUTES.sleep(2);
            } catch (InterruptedException e) {
                System.out.println("被interrupted");
            }
        });
        thread.start();
        TimeUnit.SECONDS.sleep(2);
        thread.interrupt();

新建一个线程并企图休眠2分钟,但是主线程在2秒后调用interrupt将其打断。

7、join方法

join某个线程A,会使当前线程进入等待,直到线程A结束生命周期,或者到达给定的时间

下面创建两个线程,分别启动,并且调用了每个线程的join方法,join方法是被主线程调用的,会发现线程1和线程2交替地输出直到他们结束生命周期,main线程的循环才会开始运行。

public class ApiDemo {

    public static void main(String[] args) throws InterruptedException {
        List<Thread> threads = IntStream.range(1, 3).mapToObj(ApiDemo::create).collect(Collectors.toList());
        //启动线程
        threads.forEach(Thread::start);
        //执行这两个线程的join方法
        for(Thread thread : threads){
            thread.join();
        }
        //main线程循环输出
        for (int i = 0; i < 10; i++) {
            System.out.println(Thread.currentThread().getName()+"#"+i);
            shortSleep();
        }

    }


    //创建线程,每个线程只做循环输出
    private static Thread create(int index){
        return new Thread(()->{
            for (int i = 0; i < 10; i++) {
                System.out.println(Thread.currentThread().getName() +"#"+i);
                shortSleep();
            }
        });
    }

    //休眠1秒
    private static void shortSleep(){
        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

输出结果

Thread-1#0
Thread-0#0
Thread-0#1
Thread-1#1
Thread-1#2
Thread-0#2
Thread-1#3
Thread-0#3
Thread-0#4
Thread-1#4
Thread-0#5
Thread-1#5
Thread-0#6
Thread-1#6
Thread-1#7
Thread-0#7
Thread-1#8
Thread-0#8
Thread-1#9
Thread-0#9
main#0
main#1
main#2
main#3
main#4
main#5
main#6
main#7
main#8
main#9

Process finished with exit code 0

应用场景:

app调用后台服务查询航班信息,后台需要到各大航空公司的接口获取信息,最后统一整理加工返回

到app端。除了使用CountDownLatch等,也可以用join方法。将每一个航空公司的查询交给一个线程去工作,

然后在他们结束之后统一对数据进行整理。

8、join方法结合实战(查询多个航空公司api返回航班列表汇总)

每个航空公司的接口不一样,查询速度也存在差异,如果跟航空公司进行串行化交互(逐个查询),

客户端需要等待很长的时间,如果将每一个航空公司的查询都交给一个线程去工作,然后在他们结束工作之后

统一对数据进行整理,这样就可以节省时间。

定义查询接口FightQuery,并提供一个返回方法,不管是Thread的run方法还是Runable接口,都是void返回类型,如果

想通过某个线程的运行得到结果,就需要自己定义一个返回的接口。

package com.ruoyi.demo.thread;

import java.util.List;

public interface FightQuery {
    //FightQuery提供了一个返回方法,不管是Thread的run方法,还是Runable方法,都是void返回类型,如果想通过某个线程的运行得到结果,就需要自己定义一个返回的结果
    List<String> get();
}

查询Fight的task就是一个线程的子类,主要用于到各大航空公司获取数据

package com.ruoyi.demo.thread;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ThreadLocalRandom;
import java.util.concurrent.TimeUnit;

public class FightQueryTask extends Thread implements FightQuery{

    //起点
    private final String origin;
    //终点
    private final String destination;
    //航班列表
    private final List<String> flightList = new ArrayList<>();

    public FightQueryTask(String airline,String origin,String destination){
        //调用父类Thread的构造方法,传递航班名参数作为线程name
        super("["+airline+"]");
        this.origin  = origin;
        this.destination = destination;
    }

    @Override
    public void run() {
        //getName()是调用父类Thread的getName()方法
        System.out.printf("%s-query from %s to %s \n",getName(),origin,destination);
        //ThreadLocalRandom 线程安全随机数获取
        int randomVal = ThreadLocalRandom.current().nextInt(10);
        try {
            TimeUnit.SECONDS.sleep(randomVal);
            this.flightList.add(getName()+"-"+randomVal);
            System.out.printf("The Fight:%s list query successful \n",getName());
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    @Override
    public List<String> get() {
        return this.flightList;
    }
}

注意上面的构造方法中调用了super,这代表着调用了父类Thread的构造方法,传递航班名参数作为线程name

 

 

以及getName也是调用了父类Thread的方法。

然后实现app模拟发起航班查询

package com.ruoyi.demo.thread;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;


public class FightQueryDemo {

    //定义合作的各大航空公司
    private static List<String> fightCompany = Arrays.asList("Company1","Company2","Company3");

    public static void main(String[] args) {
        //模拟发出搜索机票请求,传递参数起点和终点
        List<String> results = search("QD","BJ");
        //遍历输出查询结果
        results.forEach(System.out::println);
    }

    private static List<String> search(String original,String dest)
    {
        final List<String> result = new ArrayList<>();
        //创建查询航班信息的线程列表
        //这里有三家航工公司,遍历这三家航空公司,调用创建线程的方法传递航空公司的名字和起点以及终点
        List<FightQueryTask> tasks = fightCompany.stream().map(f->createSearchTask(f,original,dest)).collect(Collectors.toList());
        //遍历启动这几个线程
        tasks.forEach(Thread::start);
        //分别调用每一个线程的join方法,获取每个查询线程的结果,并且将其加入到result中
        tasks.forEach(t->{
            try {
                t.join();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        });
        //在此之前,当前线程会堵塞住,获取每个查询线程的结果,并且加入到result中
        //调用接口的get方法获取每个公司的航班列表,并将每个公司的航班列表addAll到result中
        tasks.stream().map(FightQuery::get).forEach(result::addAll);
        return result;
    }
    private static FightQueryTask createSearchTask(String fight,String original,String dest){
        //通过传参的构造方法传递参数
        return  new FightQueryTask(fight,original,dest);
    }
}

总结:

主线程收到了seach请求之后,交给了若干个查询线程分别进行工作,最后将每一个线程获取的航班进行统一的汇总。

由于每个航空公司的查询时间不一样,所以使用随机值来模拟不同的查询速度。

运行结果

 

 

以上代码和示例参考《Java高并发编程详解》,建议阅读原书。

标签:Java,String,Thread,示例,System,线程,sleep,public
From: https://www.cnblogs.com/badaoliumangqizhi/p/16638152.html

相关文章

  • B/S端界面控件DevExtreme JavaScript—全新的UI模板库 (v22.2)
    DevExtreme拥有高性能的HTML5/JavaScript小部件集合,使您可以利用现代Web开发堆栈(包括React,Angular,ASP.NETCore,jQuery,Knockout等)构建交互式的Web应用程序,该套件附带功能......
  • java计算代码段执行时间
    java里计算代码段执行时间可以有两种方法,一种是毫秒级别的计算,另一种是更精确的纳秒级别的计算。一)毫秒级别计算时间longstartTime=System.currentTimeMillis();/*......
  • Java 学生管理系统
    今天简单练习了一下数组和循环的一些知识,写了一个小游戏,大致如下/***一个小功能:学生管理系统*1、添加学生信息*2、显示学生信息*3、删除学生信息*4、修......
  • 你对 JavaScript 中的变量了解多少?
    你知道多少JavaScript中的变量?昨天,当我搞砸了变量时,我正在研究我的开尔文天气项目。该项目需要声明许多变量以将温度程度从一个更改为另一个。因此对于像我这样的Roo......
  • Javascript 中的内存引用
    Javascript中的内存引用Photoby哈里森布罗德本特on不飞溅在本文中,我将尝试通过一个示例练习来解释Javascript中的内存引用是如何处理的,我认为这可以更好地展示......
  • JAVA SE
    注释//adf单行​/*aadf多行adsfadf*/ 关键字&标识符关键字像voidmainpublic这种固定的单词有特殊含义的编写时会变色不能用作类名,变量名方法名标识符......
  • Java基本语法
    Java基本语法注释、标识符、关键字注释单行注释多行注释文档注释数据类型变量、常量运算符包机制、JavaDoc......
  • java 线程池 学习记录
    线程池构造函数参数有哪些核心线程池最大线程数空闲非核心线程存活时长空闲非核心线程存活时长单位阻塞队列线程生产工厂拒绝执行处理类execute和s......
  • Java开发工具IDEA
    Java开发工具IDEA下载网址连接安装下载略使用IDEA创建第一个程序File--->New--->Project--->Java(选择JDK的版本)--->NextProjectname:项目的名称Projectlocat......
  • java并发 学习记录
    哪些方法会抛出InterruptedException异常?Thread类怎么处理异常可以在Thread中设置异常处理类(实例方法)--setUncaughtExceptionHandlerThread.interupt()方法可以......