IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> Java知识库 -> java之多线程(一) -> 正文阅读

[Java知识库]java之多线程(一)

1.线程使用

1.1线程的休眠打印字幕

1.线程休眠演示打印电影字幕
在这里插入图片描述

1.2多线程 VS 单线程性能

1.单线程:

public class ThreadDemo2 {
    //总任务次数
    private final static int COUNT=10;

    public static void main(String[] args) {
        //记录开始执行的时间
        long stime=System.currentTimeMillis();
        //使用单线程进行执行
        singleThread();
        //记录结束时间
        long etime=System.currentTimeMillis();
        System.out.println("执行时间:"+(etime-stime));
    }


    /**
     * 单线程任务执行
     */
    private static void singleThread() {
        for (int i = 0; i <COUNT ; i++) {
            try{
                //每次执行需要1s
                Thread.sleep(1000);
            }catch(InterruptedException e){
                e.printStackTrace();
            }
        }
    }
}

在这里插入图片描述
2.多线程:

import java.time.LocalTime;
public class ThreadDemo2 {
    //总任务次数
    private final static int COUNT=10;

    public static void main(String[] args) {
        //记录开始执行的时间
        long stime=System.currentTimeMillis();//获取时间戳
        //使用单线程进行执行
       //  singleThread();

        //多线程
        multiThread();
        //记录结束时间
        long etime=System.currentTimeMillis();
        System.out.println("执行时间:"+(etime-stime));
    }

    /**
     * 多线程运行
     */
    private static void multiThread() {
        Thread t=new Thread(()->{
            for (int i = 0; i <COUNT/2 ; i++) {
                try{
                    //每次方法执行需要1s
                    Thread.sleep(1000);
                    System.out.println("新线程:执行了for循环:"+ LocalTime.now());
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });
        //开始执行线程
        t.start();

        //主线程也进行任务执行
        for (int i = 0; i <COUNT/2 ; i++) {
            try {
                Thread.sleep(1000);
                System.out.println("主线程:执行了for循环:"+LocalTime.now());
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        //等待新线程执行完之后
        try {
            t.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }


    /**
     * 单线程任务执行
     */
    private static void singleThread() {
        for (int i = 0; i <COUNT ; i++) {
            try{
                //每次执行需要1s
                Thread.sleep(1000);
            }catch(InterruptedException e){
                e.printStackTrace();
            }
        }
    }
}

在这里插入图片描述

1.3线程创建方式

1.3.1继承Thread(一种)

1.创建MyThread线程继承Thread来创建线程:

/**
 * 继承Thread创建线程
 */
public class ThreadDemo3 {
    public static void main(String[] args) {
        //获得当前线程
        Thread mainThread=Thread.currentThread();
        System.out.println("线程名称:"+mainThread.getName());
        Thread thread=new MyThread();
        //开启线程
        thread.start();
    }
}
class MyThread extends Thread{
    @Override
    public void run(){
        //具体的业务执行代码
        System.out.println("你好,编程");
        Thread thread=Thread.currentThread();
        System.out.println("线程的名称:"+thread.getName());
    }
}

在这里插入图片描述

2.使用jconsole观察线程
在这里插入图片描述
因为java是单继承,继承了Thread就不能继承其他的类了,然而java可以实现多接口。
3启动线程-start方法
上面我们看到了如何通过覆写run方法创建一个线程对象,但线程对象被创建出来并不意味着线程就开始运行了.
(1)覆写run方法是提供给线程要做的失去的指令清单。
(2)线程对象可以认为是把李四,小明叫过来了。
(3)而调用start()方法,就是喊一声:“行动起来!”,线程才真正独立去执行了。
在这里插入图片描述
调用start方法,才真的在操作系统的底层创建出了一个线程。

4.start方法和run方法区别?(重点)
(1)启动线程用的是start方法,执行start方法才会新创建线程执行。

/**
 * 继承Thread创建线程
 */
public class ThreadDemo3 {
    public static void main(String[] args) {
        //获得当前线程
        Thread mainThread=Thread.currentThread();
        System.out.println("线程名称:"+mainThread.getName());
        Thread thread=new MyThread();
        //开启线程
        thread.start();
    }
}
class MyThread extends Thread{
    @Override
    public void run(){
        //具体的业务执行代码
        System.out.println("你好,编程");
        Thread thread=Thread.currentThread();
        System.out.println("线程的名称:"+thread.getName());
    }
}

在这里插入图片描述

(2)执行run方法就是使用main线程调用一个普通方法而已:
在这里插入图片描述
(3)综上run方法和start方法有三个区别
区别一
run方法和start方法的第一个区别是:调用start方法时真正开启一个线程来执行任务,调用run方法相当于执行普通方法run,并不会开启新线程。
区别二
run方法也叫做线程体,它里面包含了具体要执行的业务代码,当调用run方法时,会立即执行run方法中的代码(如果当前线程时间片未用完);而调用start方法时,是启动一个线程并将线程的状态设置就绪状态。也就是说调用start方法,并不会立即执行。
区别三
因为run方法时普通方法,而普通方法可以被多次调用,所以run方法可以被多次调用,而start方法是创建新线程来执行任务,因为线程只能被创建一次,线程的状态是不可逆的,所以它们的第三个区别是:run方法可以被多次调用,而start方法只能被调用一次。
在这里插入图片描述
在这里插入图片描述

为什么start不能被重复调用?
要找到这个问题的答案,我们就要查看start方法的实现源码:
在这里插入图片描述
从start源码实现的第一行,我们就可以得到这个问题的答案,因为start方法在执行时,会先判断当前线程的状态是不是等于0,也就是是否为新建状态NEW,如果不等于新建状态,那么就会抛出"IIIegalThreadStateException"非法线程状态异常,这就是线程的start方法不能被重复调用的原因。它的执行过程为:当线程调用了第一个start方法之后,线程的状态就会从新建状态NEW变为就绪状态RUNNABLE,此时再次调用start方法,JVM就会判断出当前的线程已经不等于新建状态,从而抛出IIIegalThreadStateException非法线程状态异常。

总结:
在这里插入图片描述

5.继承Thread新建线程的缺点:
java语言是单继承,如果继承了Thread之后,就不能继承其他类。

1.3.2实现Runnable接口(四种)

1.方式一:实现Runnable接口

/**
 * 实现Runnable接口新建线程
 */
public class ThreadDemo4 {
    public static  void main(String[] args) {
        //创建Runnable
        MyThread2 myThread2=new MyThread2();
        //创建一个线程
        Thread thread=new Thread(myThread2);
        //启动线程
        thread.start();
    }
}

class MyThread2 implements Runnable{
    @Override
    public void run(){
        //具体业务代码
        Thread thread=Thread.currentThread();//得到当前线程
        System.out.println("线程执行:"+thread.getName());
    }
}

在这里插入图片描述

2.方式二:匿名内部类(Runnable)方式

/**
 * Runnable匿名内部类来创建线程
 */
public class ThreadDemo5 {
    public static void main(String[] args) {
        //匿名内部类
        Thread thread=new Thread(new Runnable(){
            @Override
            public void run(){
                //业务代码
                Thread t=Thread.currentThread();
                System.out.println("执行线程:"+t.getName());
            }
        });
        //启动线程
        thread.start();
    }
}

在这里插入图片描述

3.方式三:使用Lambda表达式来创建Runnable

如果是JDK8以上的版本创建线程,推荐使用此方式。

/** 
 * 使用Lambda表达式来创建Runnable
 */
public class ThreadDemo6 {
    public static void main(String[] args) {
        //创建线程
        Thread thread=new Thread(()->{
            //具体业务
            Thread t=Thread.currentThread();
            System.out.println("任务执行:"+t.getName());
        });
        //启动线程
        thread.start();
    }
}

在这里插入图片描述

4.方式四:匿名方式创建子对象

/**
 *匿名方式创建子对象
 */
public class ThreadDemo7 {
    public static void main(String[] args) {
        //创建线程并初始化
        Thread thread=new Thread(){
            @Override
            public void run(){
                Thread t=Thread.currentThread();
                System.out.println("任务执行:"+t.getName());
            }
        };
        //执行线程
        thread.start();
    }
}

在这里插入图片描述
注意:以上四种变种支持JDK1.8+版本。
上面创建线程的方式存在一个共同的问题就是没有返回值,也就是当线程执行完成之后,主线程没有办法拿到新线程的执行结果的。

1.3.3带返回值的Callable(2种)

方式一:实现Callable接口

import java.util.Random;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;

/**
 * 实现Callable新建线程
 */
public class ThreadDemo8 {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        //创建Callable实例
        MyCallable myCallable=new MyCallable();
        //用于接收Callable结果的对象
        FutureTask<Integer> futureTask=new FutureTask<>(myCallable);
        //创建新线程
        Thread thread =new Thread(futureTask);
        //启动线程
        thread.start();
        //接收新线程的执行结果
        int result=futureTask.get();
        System.out.println(Thread.currentThread().getName()+"--新线程返回的结果为:"+result);
    }
}

/**
 * Callable<V> 泛型里面可以是任意数据类型
 */
class MyCallable implements Callable<Integer> {

    @Override
    public Integer call() throws Exception {
        //随机数0-9
        int randomNum=new Random().nextInt(10);
        System.out.println(Thread.currentThread().getName()+"--随机数"+randomNum);
        return randomNum;
    }
}

在这里插入图片描述

方式二:匿名Callable

import java.util.Random;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;

public class ThreadDemo9 {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        FutureTask<String> futureTask=new FutureTask<>(new Callable<String>() {
            @Override
            public String call() throws Exception {
                String[] arrs=new String[]{"java","MySQL","Thread"};
                String result=arrs[new Random().nextInt(3)];
                System.out.println(Thread.currentThread().getName()+"--字符传:"+result);
                return result;
            }
        });
        //创建新线程
        Thread thread=new Thread(futureTask);
        //线程
        thread.start();
        String result=futureTask.get();
        System.out.println(Thread.currentThread().getName()+" 新线程的返回值"+result);

    }
}

在这里插入图片描述

1.4小结

创建线程有3大类实现方式,7种实现方法,如果是JDK1.8以上版本,在不需要获得线程执行结果情况下,推荐使用Lambda方式来创建线程,因为它的写法足够简洁;如果想要获取线程执行结果,可使用FutureTask+Callable的方式来实现。

2.线程常见构造方法

在这里插入图片描述
具体使用:

Thread t1 = new Thread();
Thread t2 = new Thread(new MyRunnable());
Thread t3 = new Thread("这是我的名字");
Thread t4 = new Thread(new MyRunnable(), "这是我的名字");

1.创建线程对象并命名:
在这里插入图片描述

/**
 * 创建线程并设置线程名
 */
public class ThreadDemo10 {
    public static void main(String[] args) {
        //创建线程并设置线程名
        Thread t=new Thread("线程1"){
            @Override
            public void run(){
                try {
                    Thread.sleep(60*60*1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        };
        //启动线程
        t.start();
    }
}

在这里插入图片描述
在这里插入图片描述
2.使用Runnable对象创建线程并命名
在这里插入图片描述

/**
 * 使用Runnable创建线程并命名
 */
public class ThreadDemo11 {
    public static void main(String[] args) {
        Thread thread=new Thread(new Runnable(){
            @Override
            public void run(){
                try {
                    Thread.sleep(60*60*100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        },"Runnable-Thread");
        //启动线程
        thread.start();
    }
}

在这里插入图片描述
3.线程分组使用
在这里插入图片描述

import java.util.Random;

/**
 * 线程分组使用
 */
public class ThreadDemo12 {
    public static void main(String[] args) {
        //1.创建一个线程分组(女子100米比赛)
        ThreadGroup group=new ThreadGroup("thread-group");
        //2.定义一个公共的任务(线程任务)
        Runnable runTask= new Runnable() {
            @Override
            public void run(){//业务
                //生成一个1-3秒的随机数
                int num=(1+new Random().nextInt(3));
                try {
                    //跑了n秒之后到达终点
                    Thread.sleep(num*1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                //得到执行此方法的线程
                Thread t=Thread.currentThread();
                System.out.println(t.getName()+"--选手到达终点:"+num+"s");
            }
        };
        //3.线程(运动员)
        Thread t1=new Thread(group,runTask);
        Thread t2=new Thread(group,runTask);
        Thread t3=new Thread(group,runTask);

        //起跑
        t1.start();
        t2.start();
        t3.start();

        //所有人全部到达终点之后宣布成绩
        while(group.activeGroupCount() == 0){

        }
        System.out.println("宣布比赛成绩");
    }
}

在这里插入图片描述
在这里插入图片描述

线程执行顺序?在这里插入图片描述
在这里插入图片描述

3.线程常用属性

在这里插入图片描述
(1)ID是线程的唯一标识,不同线程不会重复
(2)名称是各种调试工具用到
(3)状态表示线程当前所处的一个情况
(4)优先级高的线程理论上说更容易被调度到
(5)关于守护线程(后台线程):JVM会在一个进程的所有非后台线程结束后,才会结束执行。
(6)是否存活,即简单的理解,为run方法是否允许结束了。

3.1 ID和Name

/**
 * 线程id,name
 */
public class ThreadDemo13 {
    public static void main(String[] args) throws InterruptedException {
        Runnable runnable=new Runnable(){
            @Override
            public void run() {
                //得到执行当前任务线程
                Thread t=Thread.currentThread();
                //打印线程的id
                System.out.println("线程ID:"+t.getId());
                //打印线程的名称
                System.out.println("线程名称:"+t.getName());
            }
        };
        Thread thread=new Thread(runnable,"线程1");
        thread.start();

        Thread.sleep(500);
        System.out.println();
        Thread thread2=new Thread(runnable,"线程2");
        thread2.start();

    }
}

在这里插入图片描述
1.线程ID:
每个线程的id一定是不同的,是动态分配的。
在这里插入图片描述
2.线程名称
线程名称是可以手动指定的,并且线程名称是可能会存在重复的情况。
在这里插入图片描述

3.2线程的状态

/**
 * 状态的流转
 */
public class ThreadDemoByState {
    public static void main(String[] args) throws InterruptedException {
        Thread t=new Thread(new Runnable() {
            @Override
            public void run() {
                //拿到当前线程,并打印当前线程的状态
                Thread thread=Thread.currentThread();
                System.out.println("线程状态2:"+thread.getState());
            }
        });

        //打印线程的状态
        System.out.println("线程状态1:"+t.getState());
        t.start();

        //再次打印线程状态
        Thread.sleep(500);
        System.out.println("线程状态3:"+t.getState());
    }
}

在这里插入图片描述

3.3线程优先级

1.获取线程优先级:

/**
 * 获取线程优先级
 */
public class ThreadDemoByPriority {
    public static void main(String[] args) {
        Thread thread=new Thread(new Runnable() {
            @Override
            public void run() {
                //得到当前线程,并打印线程的优先级
                Thread t=Thread.currentThread();
                System.out.println("线程优先级1:"+t.getPriority());
            }
        });
        System.out.println("线程优先级2:"+thread.getPriority());
        thread.start();
        System.out.println("线程优先级3:"+thread.getPriority());
    }
}

在这里插入图片描述

thread.getPriority();

(1)获取线程优先级,线程创建之后优先级就存在了。
(2)优先级是int类型值,默认值是5。线程的优先级1-10,最小的优先级是1,最高的优先级是10,默认的优先级是10.
在这里插入图片描述
在这里插入图片描述

2.设置线程优先级:

/**
 * 设置线程优先级
 */
public class ThreadDemoByPriority2 {
    private final static int MAXCOUNT=1000;
    public static void main(String[] args) {
        Thread t1=new Thread(new Runnable() {
            @Override
            public void run() {
                Thread t=Thread.currentThread();
                int priority=t.getPriority();
                for (int i = 0; i <MAXCOUNT ; i++) {
                    System.out.println(t.getName()+"--优先级:"+priority);
                }
            }
        },"线程1");
        //t1.setPriority(10);
        //设置线程优先级
        t1.setPriority(Thread.MAX_PRIORITY);

        Thread t2=new Thread(new Runnable() {
            @Override
            public void run() {
                Thread t=Thread.currentThread();
                int priority=t.getPriority();
                for (int i = 0; i <MAXCOUNT ; i++) {
                    System.out.println(t.getName()+"--优先级:"+priority);
                }
            }
        },"线程2");
        t2.setPriority(Thread.MIN_PRIORITY);

        Thread t3=new Thread(new Runnable() {
            @Override
            public void run() {
                Thread t=Thread.currentThread();
                int priority=t.getPriority();
                for (int i = 0; i <MAXCOUNT ; i++) {
                    System.out.println(t.getName()+"--优先级:"+priority);
                }
            }
        },"线程3");
        t3.setPriority(Thread.NORM_PRIORITY);

        //同时启动线程
        t2.start();
        t1.start();
        t3.start();
    }
}

在这里插入图片描述
在这里插入图片描述

注意事项:
同时启动多个线程,多个线程设置了不同的优先级,并不是优先级最高的就一定先执行完之后再执行低优先级的线程,而是高优先级的线程获取到CPU时间片的概率更多,整个的执行大致符合高优先级的线程最先执行完成。

3.4线程分类

用户线程

用户线程(main线程是默认用户线程)。

守护线程(后台线程)

1.守护线程是为用户线程服务的,当一个程序中的所有用户线程都结束之后,那么守护线程也会结束。
2.JVM中的垃圾回收器就是典型的守护线程,程序运行的时候它也运行,当满足条件是进行垃圾回收,当所有线程执行完任务终止时它也会随着退出。

守护线程使用

1.获取当前线程是否为守护线程:

//判断守护线程
thread.isDaemon();

结果为true表示是守护线程,false表示是用户线程
在这里插入图片描述
2.观察下面代码:

/**
 * 守护线程示例
 */
public class ThreadDemoByDaemon {
    public static void main(String[] args) {
        //得到当前的线程(main主线程)
        Thread thread=Thread.currentThread();
        System.out.println("是否是守护线程:"+thread.isDaemon());
        Thread t1=new Thread(()->{
            //得到当前线程
            Thread cThread=Thread.currentThread();
            System.out.println(cThread.getName()+"--是否是守护线程"+cThread.isDaemon());
        },"子线程1");
        //启动线程
        t1.start();
    }
}

在这里插入图片描述

/**
 * 守护线程示例
 */
public class ThreadDemoByDaemon {
    public static void main(String[] args) throws InterruptedException {
        //得到当前的线程(main主线程)
        Thread thread=Thread.currentThread();
        System.out.println("是否是守护线程:"+thread.isDaemon());
        Thread t1=new Thread(()->{
            //得到当前线程
            Thread cThread=Thread.currentThread();
            System.out.println(cThread.getName()+"--是否是守护线程"+cThread.isDaemon());

            Thread tt1=new Thread(()->{
                Thread cThread2=Thread.currentThread();
                System.out.println(cThread2.getName()+"--是否是守护线程"+cThread.isDaemon());

            },"子线程的子线程");
            tt1.start();

        },"子线程1");

        //手动指定线程为守护线程
        t1.setDaemon(true);
        //启动线程
        t1.start();

        //将主线程休眠1s
        Thread.sleep(1000);
    }
}

在这里插入图片描述

结论
(1)main线程(主线程)默认是非守护线程(用户线程)
(2)在用户线程中创建的子类也是用户线程(默认情况下)。
(3)在守护线程中创建的子线程默认情况下也是守护线程。

设置守护线程

//手动设置线程为守护线程
thread.setDaemon(true);

在这里插入图片描述

守护线程注意事项

1.线程的类型(用户线程或守护线程)不能在线程运行期间,也就是调用了start()方法之后设置,如果设置那么JVM会报错
在这里插入图片描述

用户线程和守护线程的区别

1.用户线程
在这里插入图片描述
结论:JVM会等待所有的用户线程全部执行完成之后才退出。
2.用户线程

/**
 * 对比用户线程和守护线程的区别
 */
public class ThreadDemoByDaemon2 {
    public static void main(String[] args) throws InterruptedException {
      // userThread();
        daemonThread();
    }

    //守护线程
    private static void daemonThread() {
        Thread thread=new Thread(()->{
            for (int i = 0; i <10 ; i++) {
                System.out.println("执行:"+i);
                try {
                    Thread.sleep(500);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });
        //设置线程为守护线程
        thread.setDaemon(true);
        //启动线程
        thread.start();
    }

    //用户线程
    private static void userThread() {
        Thread thread=new Thread(()->{
            for (int i = 0; i <10 ; i++) {
                System.out.println("执行:"+i);
                try {
                    Thread.sleep(500);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });
        thread.start();
    }
}

在这里插入图片描述
结论 :JVM会等待所有的用户线程执行完之后,再退出,但JVM不会等待守护线程执行完再退出。
3.区别:

public class ThreadDemoByDaemon2 {
    public static void main(String[] args) throws InterruptedException {
        userThread();
        daemonThread();
        //Thread.sleep(1000);
    }

    //守护线程
    private static void daemonThread() {
        Thread thread=new Thread(()->{
            for (int i = 0; i <10 ; i++) {
               System.out.println("守护线程执行:"+i);
                try {
                    Thread.sleep(850);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });
        //设置线程为守护线程
        thread.setDaemon(true);
        //启动线程
        thread.start();
    }

    //用户线程
    private static void userThread() {
        Thread thread=new Thread(()->{
            for (int i = 0; i <10 ; i++) {
                System.out.println("用户线程执行:"+i);
                try {
                    Thread.sleep(500);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });
        thread.start();
    }
}

在这里插入图片描述

守护线程 VS用户线程:
用户线程在java程序中非常重要,JVM一定要等所有的用户线程执行完之后才能自然结束,而守护线程就不一样了,守护线程是为用户线程服务的,所以当所有的用户线程都执行完之后,不管守护线程是否在执行,JVM都会退出执行。

3.5是否存活

1.判断线程是否存活,返回值是布尔值:

thread.isAlive();
/**
 * 判断线程是否存活
 */
public class ThreadDemoByAlive {
    public static void main(String[] args) throws InterruptedException {
        Thread t=new Thread(()->{
            //得到线程
            Thread thread=Thread.currentThread();
            System.out.println("线程是否存活2:"+thread.isAlive());
        });
        System.out.println("线程是否存活1:"+t.isAlive());
        //启动线程
        t.start();
        System.out.println("线程是否存活3:"+t.isAlive());
        Thread.sleep(1000);
        System.out.println("线程是否存活4:"+t.isAlive());
    }
}

在这里插入图片描述
2.isAlive()方法使用场景:

public class ThreadDemoByAlive {
    public static void main(String[] args) throws InterruptedException {
        Thread t=new Thread(()->{
            for (int i = 0; i <10 ; i++) {
                try {
                    Thread.sleep(500);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            System.out.println("1.线程执行完了。");
        });
       t.start();
       while(t.isAlive()){
       }
       System.out.println("2.确定线程执行完了!");
    }
}

在这里插入图片描述

  Java知识库 最新文章
计算距离春节还有多长时间
系统开发系列 之WebService(spring框架+ma
springBoot+Cache(自定义有效时间配置)
SpringBoot整合mybatis实现增删改查、分页查
spring教程
SpringBoot+Vue实现美食交流网站的设计与实
虚拟机内存结构以及虚拟机中销毁和新建对象
SpringMVC---原理
小李同学: Java如何按多个字段分组
打印票据--java
上一篇文章      下一篇文章      查看所有文章
加:2022-03-24 00:21:20  更:2022-03-24 00:23:11 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/24 6:26:11-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码