首页 > 编程语言 >并发编程[2]_线程的常用方法

并发编程[2]_线程的常用方法

时间:2024-08-23 10:38:59浏览次数:12  
标签:log Thread 编程 并发 线程 thread1 main public


介绍一下线程常用的一些方法

1. run() 和 start()

start() 方法让线程进入就绪状态
run() 方法 是Runnable 中的一个抽象方法,线程启动时就会调用run() 方法

(1) 如果直接调用run()方法,是不会启动新线程的

public class Test1 {
    private static final Logger log = LoggerFactory.getLogger(Test1.class);

    public static void main(String[] args) {
        Thread thread = new Thread("thread1") {
            @Override
            public void run() {
                log.debug("running...");
            }
        };

        log.debug("main...");
        thread.run();
    }

}

运行结果:

2021-04-14 21:02:55  [main] - main...
2021-04-14 21:02:55  [main] - running...

可以看出log输出内容都是在 [main]线程中输出的, 并不是在thread1线程中执行的。
(2) start() 方法调用多次会报IllegalThreadStateException异常

public class Test1 {
    private static final Logger log = LoggerFactory.getLogger(Test1.class);

    public static void main(String[] args) {
        Thread thread = new Thread("thread1") {
            @Override
            public void run() {
                log.debug("running...");
            }
        };

        log.debug("main...");
        thread.start();
        thread.start();
    }
}

运行结果:

2021-04-14 21:24:34  [main] - main...
Exception in thread "main" 2021-04-14 21:24:34  [thread1] - running...
java.lang.IllegalThreadStateException
    at java.lang.Thread.start(Thread.java:708)
    at com.yt.concurrency.p1.Test1.main(Test1.java:24)

2. getState()

getState()方法是获取线程当前状态,返回值是State类型(Thread类中的enum类型), 6个值分别是 NEW,RUNNABLE, BLOCKED, WAITING,TIMED_WAITING, TERMINATED

    public static void main(String[] args) {
        Thread thread = new Thread("thread1") {
            @Override
            public void run() {
                log.debug("running...");
            }
        };
        log.debug("before:{}", thread.getState().toString());
        thread.start();
        log.debug("after:{}", thread.getState().toString());
    }

运行结果:

2021-04-14 21:31:34  [main] - before:NEW
2021-04-14 21:31:34  [main] - after:RUNNABLE
2021-04-14 21:31:34  [thread1] - running...

3. sleep()

sleep() 能让线程暂时休眠指定毫秒数,并让线程从Running(RUNNABLE)状态进入Timed Waiting(TIMED_WAITING)状态
调用sleep() 方法进入等待状态时,能够被其他线程使用interrupt()方法打断,然后抛出InterruptedException异常

public class Test1 {
    private static final Logger log = LoggerFactory.getLogger(Test1.class);

    public static void main(String[] args) {
        Thread thread = new Thread("thread1") {
            @Override
            public void run() {
                try {
                    // 休眠4秒
                    Thread.sleep(4000);
                } catch (InterruptedException e) {
                    log.debug("4.线程被打断");
                    e.printStackTrace();
                }
            }
        };
        log.debug("1.线程未启动:{}", thread.getState().toString());
        thread.start();
        log.debug("2.线程刚启动:{}", thread.getState().toString());
        try {
            // 主线程休眠2秒,便于观察
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        log.debug("3.线程休眠中:{}", thread.getState().toString());
        thread.interrupt();
    }
}

运行结果:

2021-04-14 22:23:40.608  [main] - 1.线程未启动:NEW
2021-04-14 22:23:40.608  [main] - 2.线程刚启动:RUNNABLE
2021-04-14 22:23:42.608  [main] - 3.线程休眠中:TIMED_WAITING
2021-04-14 22:23:42.608  [thread1] - 4.线程被打断

4. yield()

yield() 方法会让线程从running状态进入runnable状态,把自己cpu执行的时间让掉,让其他线程或者还是自己的这个线程来运行,具体还是得由操作系统的调度器做决定

5. join()

join() 方法会让调用线程同步等待被调用的线程,可以加参数等待毫秒数 join(long millis) 。

测试:新建一个线程,sleep一秒后为静态变量a赋值,然后主线程正常读取变量a。

public class Test1 {
    private static final Logger log = LoggerFactory.getLogger(Test1.class);
    private static int a = 0;

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

        Thread thread1 = new Thread("thread1") {
            @Override
            public void run() {
                try {
                    sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                a = 10;
            }
        };
        thread1.start();
//      暂时注释掉join方法
//      thread1.join();
        log.debug("a:{}", a);
    }
}

此时的输出结果为:

2021-04-14 23:27:59.210  [main] - a:0

因为main方法输出a的时候,线程thread1还未给a赋值,所以读取到的是0。
这时将代码中加上 thread1.join();
此时的输出结果为:

2021-04-14 23:31:04.955  [main] - a:10

main方法会同步等待thread1线程结束,也可使用 thread1.join(2000) 来为join方法指定等待时间为2秒。

6. interrupt()

interrupt() 方法可以打断线程,有一个打断标志位可以标志是否被打断,使用isInterrupted() 查看。值得一提的是,打断阻塞的线程时,例如sleep()被打断,会报出InterruptedException()异常,且标志位会自动清除,重新置为false,而打断正常运行的线程时,会把标志位置为true,但是线程仍会正常执行。

与isInterrupted() 方法相同作用的 interrupted()方法也是查看打断标志位,区别是,调用isInterrupted()方法后,不会清除标志位,而调用interrupted()方法则会。

打断sleep()线程:

public class Test1 {
    private static final Logger log = LoggerFactory.getLogger(Test1.class);

    public static void main(String[] args) throws InterruptedException {
        Thread thread1 = new Thread("thread1") {
            @Override
            public void run() {
                try {
                    log.debug("打断标志1: {}", Thread.currentThread().isInterrupted());
                    log.debug("sleep");
                    sleep(5000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                    log.debug("打断标志2: {}", Thread.currentThread().isInterrupted());
                }
            }
        };
        thread1.start();
        // 主线程睡眠两秒,保证打断时thread1是处在sleep状态
        Thread.sleep(2000);
        thread1.interrupt();
    }
}

运行结果:

2021-04-15 20:51:55.322  [thread1] - 打断标志1: false
2021-04-15 20:51:55.322  [thread1] - sleep
java.lang.InterruptedException: sleep interrupted
    at java.lang.Thread.sleep(Native Method)
    at com.yt.concurrency.p1.Test1$1.run(Test1.java:21)
2021-04-15 20:51:57.321  [thread1] - 打断标志2: false

打断正常线程:

public class Test1 {
    private static final Logger log = LoggerFactory.getLogger(Test1.class);

    public static void main(String[] args) throws InterruptedException {
        Thread thread1 = new Thread("thread1") {
            @Override
            public void run() {
                log.debug("打断标志1:{}",Thread.currentThread().isInterrupted());
                while(true){
                    if(Thread.currentThread().isInterrupted()){
                        log.debug("打断标志2:{}",Thread.currentThread().isInterrupted());
                        log.debug("被打断,退出循环...");
                        break;
                    }
                }
            }
        };
        thread1.start();
        Thread.sleep(1000);
        thread1.interrupt();
    }
}

运行结果:

2021-04-15 20:54:08.953  [thread1] - 打断标志1:false
2021-04-15 20:54:09.952  [thread1] - 打断标志2:true
2021-04-15 20:54:09.952  [thread1] - 被打断,退出循环...

例子中,因为打断正常线程时,线程不会终止或报异常,所以得判断打断标志位是否为true,来退出循环

  • 两阶段终止模式

两阶段终止模式可以优雅地终止线程,如果用stop()方法终止线程(不推荐使用),会直接将线程结束,如果线程持有锁,便来不及释放,有可能会导致程序发生错误,所以用interrupt()方法来设置打断标志位(阻塞状态时会抛 InterruptedException 异常),是比较好的方式。
举个例子:

public class Test1 {
    private static final Logger log = LoggerFactory.getLogger(Test1.class);

    public static void main(String[] args) throws InterruptedException {
        Thread thread1 = new Thread("thread1") {
            @Override
            public void run() {
                while(true){
                    // 1. 如果此时被打断,则会进行收尾操作,然后退出循环
                    if(Thread.currentThread().isInterrupted()){
                        log.debug("线程终止前的一些收尾操作");
                        break;
                    }
                    log.debug("线程运行中...");
                    try {
                        Thread.sleep(3000);
                        log.debug("业务逻辑");
                    } catch (InterruptedException e) {
                      // 2. 如果是在阻塞状态中被打断,则需要重新设置状态位,下次循环判断的时候才会收尾
                        e.printStackTrace();
                        log.debug("睡眠时被打断,会把打断标志位设置为false,所以需要重新设置打断标志位。");
                        Thread.currentThread().interrupt();
                    }
                }
            }
        };
        thread1.start();
        Thread.sleep(1000);
        log.error("准备打断thread1线程");
        thread1.interrupt();
    }
}

运行结果:

2021-04-15 22:23:51.828  [thread1] - 线程运行中...
2021-04-15 22:23:52.828  [main] - 准备打断thread1线程
java.lang.InterruptedException: sleep interrupted
    at java.lang.Thread.sleep(Native Method)
    at com.yt.concurrency.p1.Test1$1.run(Test1.java:26)
2021-04-15 22:23:52.829  [thread1] - 睡眠时被打断,会把打断标志位设置为false,所以需要重新设置打断标志位。
2021-04-15 22:23:52.829  [thread1] - 线程终止前的一些收尾操作

7. setDaemon()

可以使用setDaemon(true)设置线程为守护线程。非守护线程都结束之后,守护线程会随着JVM一同结束工作。 垃圾回收器就是一个常见的守护线程。

普通线程,若一个线程未结束,则程序不会结束

public class Test1 {
    private static final Logger log = LoggerFactory.getLogger(Test1.class);

    public static void main(String[] args) throws InterruptedException {
        Thread thread1 = new Thread("thread1") {
            @Override
            public void run() {
                while(true){
                    log.debug("thread1线程工作中...");
                }
            }
        };
        thread1.start();
        log.debug("主线程结束...");
    }
}

输出为

2021-04-15 22:39:42.220  [main] - 主线程结束...
2021-04-15 22:39:42.220  [thread1] - thread1线程工作中...
2021-04-15 22:39:42.221  [thread1] - thread1线程工作中...
......
......

主线程结束后,thread1线程并未结束,程序也没有退出,正无限执行thread1中的代码。
将thread1线程设置为守护线程,加上thread1.setDaemon(true)

public class Test1 {
    private static final Logger log = LoggerFactory.getLogger(Test1.class);

    public static void main(String[] args) throws InterruptedException {
        Thread thread1 = new Thread("thread1") {
            @Override
            public void run() {
                while (true) {
                    log.debug("thread1线程工作中...");
                }
            }
        };
        // 在start前设置,不然未生效
        thread1.setDaemon(true);
        thread1.start();
        log.debug("主线程结束...");
    }
}

输出为

2021-04-15 22:43:55.032  [main] - 主线程结束...
2021-04-15 22:43:55.032  [thread1] - thread1线程工作中...

输出就两行,程序自动退出。(具体输出几行是看当时的执行情况,总之程序会在主线程结束之后结束。)

标签:log,Thread,编程,并发,线程,thread1,main,public
From: https://www.cnblogs.com/Aeons/p/18375494

相关文章

  • 并发编程[1]_线程的创建
    介绍线程创建的两种基本的方法:继承Thread类和实现Runnable接口1.继承Thread类自定义类继承Thread类,重写run()方法importorg.slf4j.LoggerFactory;/***@author:yt*@date:2021/4/1222:09*@description:创建Thread类继承Thread*/publicclassMyTh......
  • 并发编程[3]_java线程的六种状态
    java线程状态1.操作系统进程的五种状态网上找了一张图:2.java线程的六种状态Thread类中getState()方法可以获取线程的状态,返回值是Thread类中的enum类型,取值有NEW,RUNNABLE,BLOCKED,WAITING,TIMED_WAITING,TERMINATED六种状态。java的线程状态将阻塞状态细分为BLOCKED,WAITING......
  • 大模型重塑软件架构·开启智能编程新纪元 |大模型书籍推荐
    在大模型时代洪流中,架构设计师就像时代舵手,不仅精通传统架构设计精髓,更要拥抱数据洪流与AI智能的浪潮。他们需具备前瞻视野,深入理解大模型技术如何重塑业务逻辑与系统架构,灵活运用云计算、微服务、自动化运维等现代技术栈,构建高可用、可扩展、智能化的系统架构。今天,小编......
  • 编程创建一个Cale计算类,在其中定义2个变量表示两个操作数,定义四个方法实现求和、差、
    1publicclassHomework06{2//编写一个main方法3publicstaticvoidmain(String[]args){45Calecale=newCale(2,10);6System.out.println("和="+cale.sum());7System.out.println("差="+cale.minus());......
  • 更懂你的文心快码 Inline Chat 全新上线,带来更加简化交互式的编程体验!
    更懂你的文心快码 InlineChat 全新上线,带来更加简化交互式的编程体验,充分适应程序员的编程和使用习惯。代码行内集成、智能生成、智能问答,无需跳出编辑区,AI就在你手边看,开发过程更流畅。那么Inlinechat如何助力实际开发场景?CoCo为大家展开讲讲。更懂你的文心快码......
  • bat编程
    .bat文件(批处理文件)是Windows系统中用于自动化执行一系列命令的脚本文件。下面是一些.bat文件的基本语法和常用命令:1.注释使用REM命令或@echooff后面的行(在@echooff生效的情况下)来添加注释。注释不会被执行,仅用于说明。REM这是一个注释@echooff::这也是一......
  • 干货-并发编程提高——线程池(十二)
    提到线程池就不得不说池化技术,那么什么是池化技术呢?池化技术能够减少资源对象的创建次数,提高程序的性能。特别是高并发下这种提高更加明显。使用池化技术缓存的资源有如下特点:对象的创建时间长对象创建需要大量资源对象创建后可被重复使用有没有点儿像共享单车?一个资......
  • 多线程入门Demo
    packagerun;importjava.util.ArrayList;importjava.util.List;importjava.util.concurrent.CompletionService;importjava.util.concurrent.ExecutionException;importjava.util.concurrent.ExecutorCompletionService;importjava.util.concurrent.Future;impo......
  • Scratch编程环境的暗色模式:探索可访问性的边界
    标题:Scratch编程环境的暗色模式:探索可访问性的边界Scratch,这个广受欢迎的图形化编程平台,由麻省理工学院媒体实验室开发,一直致力于为用户提供友好且易于访问的编程体验。随着用户对编程环境个性化需求的增长,Scratch的编程环境是否支持暗模式或可访问性选项,成为了编程教育领......
  • Scratch的诞生:开启编程世界的大门
    标题:Scratch的诞生:开启编程世界的大门Scratch,这款全球数百万儿童和青少年的编程启蒙工具,自诞生之日起就以其独特的图形化编程界面和强大的社区支持,引领着编程教育的潮流。本文将详细探讨Scratch的起源,它如何从一个教育项目成长为全球性的编程学习平台,并提供一些基础的Scrat......