java线程详解

这篇具有很好参考价值的文章主要介绍了java线程详解。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

java线程详解

线程

概念

说到线程,就不得不提进程,为什么呢,因为进程是操作系统进行分配资源和调度的最小单位,比如windows系统安装的应用软件(office、qq、微信等)启动时,由操作系统协调分配资源和调度执行称之为一个进程,进程间是相互独立和隔离的。而线程是进程最小执行单位,一个进程的执行过程中可以有多个线程,这样可以发挥多核CPU的能力,提高执行效率。
java中的线程不是由操作系统直接调度,而且通过java虚拟机与操作系统进行指令交互完成。所以对于java程序员来说,使用线程非常简单,只需要在语言层面编写完代码,交给虚拟机运行,剩下的脏活累活在底层就由java虚拟机完成,使用线程一时爽,一直使用一直爽(哈哈,虽然多线程能充分压榨CPU,但是用不好的话也会产生许多问题,比如并发导致的数据错误、系统负载飙升等)。

基本用法

有两种方式来创建线程
1、一种是实现Runnable接口,然后利用Thread类的构造函数传入Runnable接口创建Thread实例。
2、另外一种是继承Thread

1、实现Runnable

class WorkerThread1 implements Runnable {
    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName() + "执行完成");
    }
}

2、继承Thread

class WorkerThread2 extends Thread {
    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName() + "执行完成");
    }
}

测试用例:

public static void main(String[] args) {
    test1();
}

private static void test1() {
    new Thread(new WorkerThread1(), "t1").start();
    Thread t2 = new WorkerThread2();
    t2.setName("t2");
    t2.start();
}

输出:
注意这里的执行顺序不一定是t1先输出,也可能是t2先输出,因为线程启动后只是准备就绪,最终需要等待操作系统调度执行才能执行。

t1执行完成
t2执行完成

这两种方式都可以创建线程并执行,细心的读者可能看过源码发现其实Thread类也实现了Runnable接口。如果自己的类已经继承了别的类,那么可以实现Runnable接口创建线程,否则可以继承Thread类复写run()方法即可。

注意:要让线程执行需要调用start()方法,这样虚拟机才能创建一个线程等待操作系统调度,直接执行run()方法则是在当前线程直接调用该方法,同步执行,不会再创建线程。

线程状态及流转

线程的生命周期可用状态表示,总共有6种状态,
Thread类的源码里我们可以看到有个枚举类State

public enum State {
    // 新建状态,被new出来后,还未调用start方法
    NEW,
    // 可运行状态,线程已就绪,获取到CPU资源就运行,运行中就是Running状态
    RUNNABLE,
    // 阻塞状态,比如在等待锁对象,或者读取流等待
    BLOCKED,
    // 等待状态,比如在等待锁对象,需要被notify唤醒处于就绪状态,获取到CPU资源就运行
    WAITING,
    // 带有超时时间的等待,时间过后自动返回继续执行,比如sleep或者wait(long time)
    TIMED_WAITING,
    // 终止状态,自然停止或者抛出异常停止
    TERMINATED;
}

状态流转图:
java线程详解

属性及方法

属性

常用并且需要关注的属性如下:

private int priority;// 线程优先级,1<=priority<=10,优先级越大可能最终被操作系统优先调度的几率越大,但不一定会被先调用
private boolean daemon = false;// 是否后台线程,当所有的非后台线程结束时,所有的后台线程才结束,比如垃圾回收线程就是后台线程,负责在程序运行过程中,清理掉不在使用的对象
private Runnable target;// 目标类,要使用线程执行的业务逻辑写在run()方法里
private ThreadGroup group;// 线程组,所有的线程都必须属于某一个线程组,默认的是父线程组,未创建线程组时即main线程组
ThreadLocal.ThreadLocalMap threadLocals = null;// 线程独享本地变量表

ThreadLocal不了解的童鞋可以参考ThreadLocal源码分析

构造方法

java线程详解

其实最终都会调到一个init的方法,这里会初始化一些线程的基本信息。
java线程详解

ThreadGroup g:属于的线程组(每个线程必须属于一个线程组,没有设置线程组的话这里会默认使用父线程组,即main线程组)
Runnable target:要执行的方法
String name:线程名字,可单独设置,默认使用的是`Thread-自增序号`
long stackSize:这只线程占用的栈大小,一般不设置,由java虚拟机决定
AccessControlContext acc:这是一种安全访问机制,一般不用自己设置。

常用方法

// jvm方法,获取当前正在执行的线程
public static native Thread currentThread();
// 调用该方法所在的线程通知让出CPU资源,由操作系统重新分配线程执行,有可能还是该线程继续执行
public static native void yield();
// 线程休眠millis毫秒,让出CPU资源,但是不会释放持有的对象锁,当被中断时抛出异常
public static native void sleep(long millis) throws InterruptedException;
// 启动线程,更合理的说法是线程准备就绪,等待最终操作系统调度执行
public synchronized void start()
// 中断线程,其实只是设置中断标志为true,如果线程已经终止,调用该方法不会有任何作用,线程自身和安全验证通过的线程才有权限调该线程的这个方法,否则会返回异常SecurityException,还有以下几种特殊场景。
// 1、如果当前线程处理wait()、join()、sleep(long)方法时中断标志将被清除,并且抛出异常InterruptedException。(清除中断标志是因为抛出了异常后可由程序控制执行,中断标志没啥意义了)
// 2、继承了InterruptibleChannel如果IO阻塞状态,则channel会关闭并且中断标志设置为true,抛出异常ClosedByInterruptException
// 3、如果该线程阻塞在Selector,则中断标志设置为true并且select会立即返回一个非0的值表示select方法执行了
public void interrupt()
// 返回当前线程是否处于中断状态,并清除当前运行的线程的中断标志。
public static boolean interrupted() {
    return currentThread().isInterrupted(true);
}
// 返回当前线程是否处于中断状态,不会清除当前运行的线程的中断标志。线程终止后该方法始终返回false
public boolean isInterrupted() {
   return isInterrupted(false);
}

常用场景

重点看下interrupt()interrupted()isInterrupted()这三个方法的使用。
1、不使用变量终止线程
下面示例调用了interrupt()方法

public class ThreadTest {
    public static void main(String[] args) throws InterruptedException {
        Thread t = new Thread(new Runnable() {
            @Override
            public void run() {
                while (true) {
                    System.out.println(Thread.currentThread().getName() + ":" + System.currentTimeMillis());
                }
            }
        }, "t1");
        t.start();
        //主线程休2秒
        Thread.sleep(2000);
        t.interrupt();
        System.out.println(Thread.currentThread().getName() + "运行结束");
    }
}

程序运行部分结果

t1:1584607075440
t1:1584607075440
t1:1584607075440
t1:1584607075440
main运行结束
t1:1584607077740
t1:1584607077740
t1:1584607077740
t1:1584607077740
t1:1584607077741
t1:1584607077741
t1:1584607077741

这里我们看到调用了interrupt()方法,但是t线程并没有终止,因为这个方法只是设置中断标志为true,线程是否结束跟这个标志没有关系,需要其他逻辑判断。
如果我们把程序的while循环的条件调整如下这样,那么t线程调用isInterrupted()方法获取到中断标志为true,!true条件结果为false所以while循环结束,线程自然终止。

while (true) 
改成
while (!Thread.currentThread().isInterrupted())

2、线程组的操作
线程组主要是可以管理线程或线程组,一个大型的线程组看起来像是一棵树,可方便的对线程和子线程组进行监控和操作,比如统计活跃线程和线程组,或者中断和销毁线程组操作等。

public static void main(String[] args) {
    ThreadGroup tg1 = new ThreadGroup("tg1");
    ThreadGroup tg2 = new ThreadGroup("tg2");
    ThreadGroup tg3 = new ThreadGroup(tg2, "tg3");
    Thread t1 = new WorkerThread(tg1, "t1");
    Thread t2 = new WorkerThread(tg2, "t2");
    Thread t3 = new WorkerThread(tg1, "t3");
    Thread t4 = new WorkerThread(tg3, "t4");
    t1.start();
    t2.start();
    t3.start();
    t4.start();
    System.out.println("tgName:" + tg1.getName() + "\tactiveCount:" + tg1.activeCount());
    System.out.println("tgName:" + tg2.getName() + "\tactiveGroupCount:" + tg2.activeGroupCount());
    System.out.println("tgName:" + tg3.getParent().getName() + "\tactiveGroupCount:" + tg3.activeGroupCount());
}
    
class WorkerThread extends Thread {
    public WorkerThread(ThreadGroup tg, String name) {
        super(tg, name);
    }
    @Override
    public void run() {
        while (true) {
        }
    }
}

输出结果如下:

tgName:tg1	activeCount:2
tgName:tg2	activeGroupCount:1
tgName:tg2	activeGroupCount:0

总结

线程的使用可以提高系统的处理能力,比较复杂的业务可以使用多线程协作缩短执行时间,线程的结束不能仅仅依靠中断,需要根据业务实现自己的逻辑。另外引入多线程会增加系统的复杂度,线程的管理问题也会比较麻烦,同时线程数过多会频繁的让CPU切换,增加系统负载,这种时候可以选择使用线程池来解决问题,后续会再详细介绍。文章来源地址https://www.toymoban.com/news/detail-605156.html

到了这里,关于java线程详解的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 关于进程、线程、协程的概念以及Java中的应用

    本文将从“操作系统”、“Java应用”上两个角度来探究这三者的区别。 在我本人的疑惑中,我有以下3个问题。 在“多道程序环境下”,允许多个程序并发执行,此时它们将失去封闭性,并具有间断性以及不可再现性的特征,因此需要引入进程的概念。 进程是程序执行的过

    2024年02月08日
    浏览(50)
  • 【Java练习题汇总】《第一行代码JAVA》多线程篇,汇总Java练习题——线程及多线程概念、Thread 类及 Runnable 接口、线程状态、synchronized同步操作...

    一、填空题 Java 多线程可以依靠________ 、________ 和________ 三种方式实现。 多个线程操作同一资源的时候需要注意________,依靠________ 实现,实现手段是:________ 和________,过多的使用,则会出现________ 问题。 Java 程序运行时,至少启动________ 个线程,分别是________ 和_

    2024年02月16日
    浏览(41)
  • 【Java系列】详解多线程(三)—— 线程安全(下篇)

    个人主页:兜里有颗棉花糖 欢迎 点赞👍 收藏✨ 留言✉ 加关注💓本文由 兜里有颗棉花糖 原创 收录于专栏【Java系列专栏】【JaveEE学习专栏】 本专栏旨在分享学习Java的一点学习心得,欢迎大家在评论区交流讨论💌 我们先来看一下什么是内存可见性问题,通过一段代码来进

    2024年02月04日
    浏览(36)
  • JAVA NIO概念详解

    Java NIO(New I/O)是Java平台提供的一组用于高效处理I/O操作的API。相较于传统的Java I/O(java.io)API,Java NIO提供了更加灵活、高效的非阻塞I/O操作方式。主要一些概念如下。 Java NIO中的I/O操作是基于缓冲区的。缓冲区实质上是一个固定大小的内存块,用于存储数据。它可以作为

    2024年02月09日
    浏览(30)
  • 【Java 基础篇】Java多线程编程详解:线程创建、同步、线程池与性能优化

    Java是一门强大的编程语言,其中最引人注目的特性之一是多线程支持。多线程允许我们在同一程序中同时执行多个任务,这大大提高了应用程序的性能和响应能力。本文将深入介绍Java线程的基础知识,无论您是初学者还是有一些经验的开发人员,都将从中获益。 在计算机科

    2024年02月07日
    浏览(44)
  • 【Java|多线程与高并发】线程池详解

    Java线程池是一种用于管理和重用线程的机制,它可以在需要执行任务时,从线程池中获取线程,执行任务,然后将线程放回池中,以便后续使用。线程池可以有效地管理线程的数量,提高程序的性能和资源利用率。 为什么从线程池里面取线程比直接创建线程快呢? 创建线程是

    2024年02月11日
    浏览(35)
  • Java 线程状态详解

    当一个线程对象被创建,但还没有调用其start()方法时,它处于新建状态。(内核里还没创建对应PCB) 当线程的run()方法执行完毕或线程被强制终止时,线程进入终止状态。(表示内核中的PCB已经执行完毕,但是Thread对象还在) 当线程对象调用了start()方法后,线程进入就绪状态。有

    2024年02月12日
    浏览(34)
  • java线程详解

    说到线程,就不得不提进程,为什么呢,因为进程是操作系统进行分配资源和调度的最小单位,比如windows系统安装的应用软件(office、qq、微信等)启动时,由操作系统协调分配资源和调度执行称之为一个进程,进程间是相互独立和隔离的。而线程是进程最小执行单位,一个

    2024年02月16日
    浏览(20)
  • java线程-synchronized详解

    解决线程原子性问题,最常见的手段就是加锁,Java提供了两种加锁的方式,一个synchronized隐式锁,另外一个是通过J.U.C框架提供的Lock显式加锁。本文主要介绍一个Synchronized的实现方式。 synchronized解决的是多个线程之间访问资源的同步性,synchronized 翻译为中文的意思是

    2024年02月10日
    浏览(38)
  • java---线程安全详解

    目录 前言 一、线程不安全产生的原因 1.多个线程同时修改一个变量 2.非原子性操作 3.内存可见性问题 4.指令重排序问题  二、线程安全的解决 1.加锁排队执行 1. 同步锁synchronized 2.可重入锁ReentrantLock 2.原子类AtomicInteger 总结 线程安全是指某个方法或某段代码,在多线程中能够

    2024年02月08日
    浏览(22)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包