文章详情

短信预约-IT技能 免费直播动态提醒

请输入下面的图形验证码

提交验证

短信预约提醒成功

Java多线程使用方式和实现原理

2023-06-02 16:16

关注

本篇内容介绍了“Java多线程使用方式和实现原理”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!

Java中的线程

Java之父对线程的定义是:

线程是一个独立执行的调用序列,同一个进程的线程在同一时刻共享一些系统资源(比如文件句柄等)也能访问同一个进程所创建的对象资源(内存资源)。java.lang.Thread对象负责统计和控制这种行为。

每个程序都至少拥有一个线程-即作为Java虚拟机(JVM)启动参数运行在主类main方法的线程。在Java虚拟机初始化过程中也可能启动其他的后台线程。这种线程的数目和种类因JVM的实现而异。然而所有用户级线程都是显式被构造并在主线程或者是其他用户线程中被启动。

  本文主要讲了java中多线程的使用方法、线程同步、线程数据传递、线程状态及相应的一些线程函数用法、概述等。在这之前,首先让我们来了解下在操作系统中进程和线程的区别:  进程:每个进程都有独立的代码和数据空间(进程上下文),进程间的切换会有较大的开销,一个进程包含1--n个线程。(进程是资源分配的最小单位)  线程:同一类线程共享代码和数据空间,每个线程有独立的运行栈和程序计数器(PC),线程切换开销小。(线程是cpu调度的最小单位)  线程和进程一样分为五个阶段:创建、就绪、运行、阻塞、终止。  多进程是指操作系统能同时运行多个任务(程序)。  多线程是指在同一程序中有多个顺序流在执行。在java中要想实现多线程,有两种手段,一种是继续Thread类,另外一种是实现Runable接口.(其实准确来讲,应该有三种,还有一种是实现Callable接口,并与Future、线程池结合使用

Java线程状态机

Java 给多线程编程提供了内置的支持。 一条线程指的是进程中一个单一顺序的控制流,一个进程中可以并发多个线程,每条线程并行执行不同的任务。

多线程是多任务的一种特别的形式,但多线程使用了更小的资源开销。

这里定义和线程相关的另一个术语 - 进程:一个进程包括由操作系统分配的内存空间,包含一个或多个线程。一个线程不能独立的存在,它必须是进程的一部分。一个进程一直运行,直到所有的非守护线程都结束运行后才能结束。

多线程能满足程序员编写高效率的程序来达到充分利用 CPU 的目的。


一个线程的生命周期

线程是一个动态执行的过程,它也有一个从产生到死亡的过程。

下图显示了一个线程完整的生命周期。

Java多线程使用方式和实现原理

线程状态转换

public class 线程的状态转换 {//一开始线程是init状态,结束时是terminated状态class t implements Runnable {    private String name;    public t(String name) {        this.name = name;    }    @Override    public void run() {        System.out.println(name + "run");    }}//测试join,父线程在子线程运行时进入waiting状态@Testpublic void test1() throws InterruptedException {    Thread dad = new Thread(new Runnable() {        Thread son = new Thread(new t("son"));        @Override        public void run() {            System.out.println("dad init");            son.start();            try {                //保证子线程运行完再运行父线程                son.join();                System.out.println("dad run");            } catch (InterruptedException e) {                e.printStackTrace();            }        }    });    //调用start,线程进入runnable状态,等待系统调度    dad.start();    //在父线程中对子线程实例使用join,保证子线程在父线程之前执行完}//测试sleep@Testpublic void test2(){    Thread t1 = new Thread(new Runnable() {        @Override        public void run() {            System.out.println("t1 run");            try {                Thread.sleep(3000);            } catch (InterruptedException e) {                e.printStackTrace();            }        }    });    //主线程休眠。进入time waiting状态    try {        Thread.sleep(3000);    } catch (InterruptedException e) {        e.printStackTrace();    }    t1.start();}//线程2进入blocked状态。public static void main(String[] args) {    test4();    Thread.yield();//进入runnable状态}//测试blocked状态public static void test4() {    class A {        //线程1获得实例锁以后线程2无法获得实例锁,所以进入blocked状态        synchronized void run() {            while (true) {                System.out.println("run");            }        }    }    A a = new A();    new Thread(new Runnable() {        @Override        public void run() {            System.out.println("t1 get lock");            a.run();        }    }).start();    new Thread(new Runnable() {        @Override        public void run() {            System.out.println("t2 get lock");            a.run();        }    }).start();}//volatile保证线程可见性volatile static int flag = 1;//object作为锁对象,用于线程使用wait和notify方法volatile static Object o = new Object();//测试wait和notify//wait后进入waiting状态,被notify进入blocked(阻塞等待锁释放)或者runnable状态(获取到锁)public void test5() {    new Thread(new Runnable() {        @Override        public void run() {            //wait和notify只能在同步代码块内使用            synchronized (o) {                while (true) {                    if (flag == 0) {                        try {                            Thread.sleep(2000);                            System.out.println("thread1 wait");                            //释放锁,线程挂起进入object的等待队列,后续代码运行                            o.wait();                        } catch (InterruptedException e) {                            e.printStackTrace();                        }                    }                    System.out.println("thread1 run");                    System.out.println("notify t2");                    flag = 0;                    //通知等待队列的一个线程获取锁                    o.notify();                }            }        }    }).start();    //解释同上    new Thread(new Runnable() {        @Override        public void run() {            while (true) {                synchronized (o) {                    if (flag == 1) {                        try {                            Thread.sleep(2000);                            System.out.println("thread2 wait");                            o.wait();                        } catch (InterruptedException e) {                            e.printStackTrace();                        }                    }                    System.out.println("thread2 run");                    System.out.println("notify t1");                    flag = 1;                    o.notify();                }            }        }    }).start();}//输出结果是//    thread1 run//    notify t2//    thread1 wait//    thread2 run//    notify t1//    thread2 wait//    thread1 run//    notify t2//不断循环}

Java Thread常用方法

Thread#yield():

执行此方法会向系统线程调度器(Schelduler)发出一个暗示,告诉其当前JAVA线程打算放弃对CPU的使用,但该暗示,有可能被调度器忽略。使用该方法,可以防止线程对CPU的过度使用,提高系统性能。

Thread#sleep(time)或Thread.sleep(time, nanos):

使当前线程进入休眠阶段,状态变为:TIME_WAITING

Thread.interrupt():

中断当前线程的执行,允许当前线程对自身进行中断,否则将会校验调用方线程是否有对该线程的权限。

如果当前线程因被调用Object#wait(),Object#wait(long, int), 或者线程本身的join(), join(long),sleep()处于阻塞状态中,此时调用interrupt方法会使抛出InterruptedException,而且线程的阻塞状态将会被清除。

Thread#interrupted(),返回true或者false:

查看当前线程是否处于中断状态,这个方法比较特殊之处在于,如果调用成功,会将当前线程的interrupt status清除。所以如果连续2次调用该方法,第二次将返回false。

Thread.isInterrupted(),返回true或者false:

与上面方法相同的地方在于,该方法返回当前线程的中断状态。不同的地方在于,它不会清除当前线程的interrupt status状态。

Thread#join(),Thread#join(time):

A线程调用B线程的join()方法,将会使A等待B执行,直到B线程终止。如果传入time参数,将会使A等待B执行time的时间,如果time时间到达,将会切换进A线程,继续执行A线程。

构造方法和守护线程

构造方法Thread类中不同的构造方法接受如下参数的不同组合:一个Runnable对象,这种情况下,Thread.start方法将会调用对应Runnable对象的run方法。如果没有提供Runnable对象,那么就会立即得到一个Thread.run的默认实现。一个作为线程标识名的String字符串,该标识在跟踪和调试过程中会非常有用,除此别无它用。线程组(ThreadGroup),用来放置新创建的线程,如果提供的ThreadGroup不允许被访问,那么就会抛出一个SecurityException 。Thread对象拥有一个守护(daemon)标识属性,这个属性无法在构造方法中被赋值,但是可以在线程启动之前设置该属性(通过setDaemon方法)。当程序中所有的非守护线程都已经终止,调用setDaemon方法可能会导致虚拟机粗暴的终止线程并退出。isDaemon方法能够返回该属性的值。守护状态的作用非常有限,即使是后台线程在程序退出的时候也经常需要做一些清理工作。(daemon的发音为”day-mon”,这是系统编程传统的遗留,系统守护进程是一个持续运行的进程,比如打印机队列管理,它总是在系统中运行。)

启动线程的方式和isAlive方法

启动线程
调用start方法会触发Thread实例以一个新的线程启动其run方法。新线程不会持有调用线程的任何同步锁。

当一个线程正常地运行结束或者抛出某种未检测的异常(比如,运行时异常(RuntimeException),错误(ERROR) 或者其子类)线程就会终止。

当线程终止之后,是不能被重新启动的。在同一个Thread上调用多次start方法会抛出InvalidThreadStateException异常。

如果线程已经启动但是还没有终止,那么调用isAlive方法就会返回true.即使线程由于某些原因处于阻塞(Blocked)状态该方法依然返回true。

如果线程已经被取消(cancelled),那么调用其isAlive在什么时候返回false就因各Java虚拟机的实现而异了。没有方法可以得知一个处于非活动状态的线程是否已经被启动过了。

Java多线程优先级

Java的线程实现基本上都是内核级线程的实现,所以Java线程的具体执行还取决于操作系统的特性。

Java虚拟机为了实现跨平台(不同的硬件平台和各种操作系统)的特性,Java语言在线程调度与调度公平性上未作出任何的承诺,甚至都不会严格保证线程会被执行。但是Java线程却支持优先级的方法,这些方法会影响线程的调度:

每个线程都有一个优先级,分布在Thread.MIN_PRIORITY和Thread.MAX_PRIORITY之间(分别为1和10)
默认情况下,新创建的线程都拥有和创建它的线程相同的优先级。main方法所关联的初始化线程拥有一个默认的优先级,这个优先级是Thread.NORM_PRIORITY (5).

线程的当前优先级可以通过getPriority方法获得。
线程的优先级可以通过setPriority方法来动态的修改,一个线程的最高优先级由其所在的线程组限定。

“Java多线程使用方式和实现原理”的内容就介绍到这里了,感谢大家的阅读。如果想了解更多行业相关的知识可以关注编程网网站,小编将为大家输出更多高质量的实用文章!

阅读原文内容投诉

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

软考中级精品资料免费领

  • 历年真题答案解析
  • 备考技巧名师总结
  • 高频考点精准押题
  • 2024年上半年信息系统项目管理师第二批次真题及答案解析(完整版)

    难度     813人已做
    查看
  • 【考后总结】2024年5月26日信息系统项目管理师第2批次考情分析

    难度     354人已做
    查看
  • 【考后总结】2024年5月25日信息系统项目管理师第1批次考情分析

    难度     318人已做
    查看
  • 2024年上半年软考高项第一、二批次真题考点汇总(完整版)

    难度     435人已做
    查看
  • 2024年上半年系统架构设计师考试综合知识真题

    难度     224人已做
    查看

相关文章

发现更多好内容

猜你喜欢

AI推送时光机
位置:首页-资讯-后端开发
咦!没有更多了?去看看其它编程学习网 内容吧
首页课程
资料下载
问答资讯