精华内容
下载资源
问答
  • java项目启动后即运行一个线程的两种方法
    千次阅读
    2018-12-14 18:03:15
    更多相关内容
  • java程序自动重新启动

    热门讨论 2012-05-18 10:14:12
    自己写的一小段Java程序,让程序按条件自动重新启动,3个文件都是.java文件,程序不包含包信息,可以放到C盘直接进行编译运行,其中Test是运行主程序,watch是监视检测线程
  • Java创建线程

    千次阅读 2022-04-05 22:49:44
    是操作系统能够进行运算调度的最小单位。 它被包含在进程之中,是进程中的实际运作单位。 一个线程指的是进程中一个单一顺序的控制流。 其实:进程在我们计算机中我们随时都在使用:按住 Ctrl + shift + esc ...

    目录

    既然说到线程,在这里就给大家普及一下线程。

    线程(Thread):

    那么,进程是什么呢?

    接下来,就让我们步入主题:

    在 Java 中,是这样说线程的:

    创建一个新的执行线程有两种方法。 

    第一种:创建一个类实现 Thread 类

     创建线程的第二种方式:使用 Thread(Runnable target) 构造方法,创建对象


    既然说到线程,在这里就给大家普及一下线程。

    🎇🎇🎇🎇🎇🎇🎇🎇

    线程(Thread):

    • 是操作系统能够进行运算调度的最小单位。
    • 它被包含在进程之中,是进程中的实际运作单位。
    • 一个线程指的是进程中一个单一顺序的控制流。

    🥂🥂🥂

    其实:进程在我们计算机中我们随时都在使用:按住 Ctrl + shift + esc

    watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA546L5L2z5Lyf4pmq,size_16,color_FFFFFF,t_70,g_se,x_16

    这个就是一个进程,在计算机中,线程就是进程中的一个执行单元。

    那么,进程是什么呢?

    进程:简简单单,我们日常中使用的QQ就是一个进程进程可以理解为就是一个软件,线程则就是这个软件中的一些功能,多个线程同时工作,才能使得进程工作。🤔🤔🤔。

    使用官方的话来描述进程:

    是计算机中的程序关于某数据集合上的一次运行活动,是操作系统进行资源分配与调度的基本单位。

    接下来,就让我们步入主题:

    ⏰⏰⏰⏰⏰⏰⏰⏰⏰⏰⏰⏰⏰⏰⏰⏰⏰⏰⏰⏰

    在 Java 中,是这样说线程的:

    Java中,Thread类定义为:继承 了祖先类 Object ,实现了 Runable 接口。

    public class Thread extends Object implements Runnable

    线程是程序(进程)中执行的线程。

    Java虚拟机允许应用程序同时执行多个执行线程。 


    每个线程都有优先权。 具有较高优先级的线程优先于优先级较低的线程执行。 每个线程可能也可能不会被标记为守护程序。 当在某个线程中运行的代码创建一个新的Thread对象时,新线程的优先级最初设置为等于创建线程的优先级,并且当且仅当创建线程是守护进程时才是守护线程。 

    当Java虚拟机启动时,通常有一个非守护进程线程(通常调用某些指定类的名为main的方法)。 Java虚拟机将继续执行线程,直到发生以下任一情况: 

    已经调用了Runtime类的exit方法,并且安全管理器已经允许进行退出操作。 
    所有不是守护进程线程的线程都已经死亡,无论是从调用返回到run方法还是抛出超出run方法的run 。 


    创建一个新的执行线程有两种方法。 

    第一种:创建一个类实现 Thread 类

    package stu.my.cdn.creadthread.p1;
    
    /**
     * 创建线程的第一种方式:创建类,实现 Thread 方法
     */
    public class MyThread extends Thread {
    
        /**
         * run () 方法就是子线程要执行的代码
         */
        @Override
        public void run() {
            System.out.println("这是子线程打印的东西");
        }
    
        /**
         * 主线程 main
         */
        public static void main(String [] args) {
            System.out.println("JVM 启动 main线程,main线程执行了main方法");
            // 创建子线程对象
            MyThread thread = new MyThread();
            // 启动线程----启动线程后,会自动执行自定义类中的 run 方法
            thread.start();
            System.out.println("mian 主线程执行结束");
        }
    }
    

     以下是程序的输出结果:

    watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA546L5L2z5Lyf4pmq,size_20,color_FFFFFF,t_70,g_se,x_16

    这种程序执行分为三种方式:

    串行:特殊的并行,程序排队执行,比较耗时,但安全。 

    并发:一个程序执行,在等待阶段,下一个开始。

    并行:简单粗暴,程序一起开始执行,一般效率高。

    从硬件角度来说:如果是单核 CPU ,一个处理器一次只能执行一个线程的情况下,处理器可以使用时间片轮转技术,可以让 CPU 快速的在各个线程之间进行切换,对用户来说,感觉是三个线程在同时执行,如果是多核 CPU ,可以为不同的线程分配不同的 CPU 内核。

    package stu.my.cdn.creadthread.p2;
    
    public class MyThread2 extends Thread{
    
        @Override
        public void run() {
            // 处理异常快捷键 ,选中代码: Ctrl + Alt + T
            try {
                for(int i = 1; i <= 5; i++){
                    System.out.println("子线程==>" + i);
                    int time = (int) (Math.random() * 1000);
                    Thread.sleep(time);     // 线程睡觉,单位是 毫秒, 1s = 1000ms
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    
        public static void main(String[] args) {
            MyThread2 myThread2 = new MyThread2();
            myThread2.start();      // 开启子线程
    
            try {
                for(int i = 1; i <= 5; i++){
                    System.out.println("主线程main==>" + i);
                    int time = (int) (Math.random() * 1000);
                    Thread.sleep(time);     // 线程睡觉,单位是 毫秒, 1s = 1000ms
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
    

     第一次执行顺序:

     watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA546L5L2z5Lyf4pmq,size_20,color_FFFFFF,t_70,g_se,x_16

    第二次执行顺序 :

     watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA546L5L2z5Lyf4pmq,size_20,color_FFFFFF,t_70,g_se,x_16

    fb528211efe249a6887c1003042963df.gif

     创建线程的第二种方式:使用 Thread(Runnable target) 构造方法,创建对象

    如果已经类已经有了继承的父类,则第一种方式便不再使用这种情况,(因为在JAVA中一个类只能有一个父类):🤔🤔🤔🤔

     那我们便可以使用这种方式:实现接口,使用构造方法,创建线程:

     watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA546L5L2z5Lyf4pmq,size_17,color_FFFFFF,t_70,g_se,x_16

    package stu.my.cdn.creadthread.p3;
    
    /**
     * 当线程已经有父类了,就不能继承 Thread 类的形式创建线程了,可以使用 Runnable 接口的形式
     */
    public class MyRunnable implements  Runnable{
        /**
        * 这里的 run 方法 就相当于我们日常中使用的 main 方法
        * 一旦 线程 start() 就会执行这个方法 
        */
        @Override
        public void run() {
            for(int i = 0; i <= 5; i++){
                System.out.println("MyRunnable implements Runnable 's run method:" + i);
            }
        }
    
        public static void main(String[] args) {
            // 创建 Runnable 接口的实现类
            MyRunnable myRunnable = new MyRunnable();
            // 使用 Runnable 的 Thread(Runnable target) 方法,创建线程对象
            Thread thread = new Thread(myRunnable);
            // 开启线程
            thread.start();
    
            // main 线程
            for (int i = 1; i <= 5; i++){
                System.out.println("Runnable is main:" +i);;
            }
    
            // 使用匿名内部内实现
            Thread thread1 = new Thread(new Runnable() {
                @Override
                public void run() {
                    for(int i = 0; i < 5; i++){
                        System.out.println("use anonymous interior class finished thread create :" + i);
                    }
                }
            });
            // 开启 thread1
            thread1.start();
        }
    }
    

     一样,线程每次并行执行,根据 CPU 核数的不同和允许执行线程的数量,利用对应的技术,在各个线程之间切换,提高执行的效率。

     watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA546L5L2z5Lyf4pmq,size_20,color_FFFFFF,t_70,g_se,x_16

     本期就到这里~~~

    感谢大家阅读~~~68739b582cce4a7289d1d9dede0f0f48.gif

    展开全文
  • Java线程和操作系统线程的关系

    千次阅读 2020-06-30 13:55:51
    fork一个子进程的消耗是很大的,fork是一个昂贵的系统调用,即使使用现代的写时复制(copy-on-write)技术。 各个进程拥有自己独立的地址空间,进程间的协作需要复杂的IPC技术,如消息传递和共享内存等。 多线程的优...

    传统进程的缺点

    • fork一个子进程的消耗是很大的,fork是一个昂贵的系统调用,即使使用现代的写时复制(copy-on-write)技术。
    • 各个进程拥有自己独立的地址空间,进程间的协作需要复杂的IPC技术,如消息传递和共享内存等。

    多线程的优缺点
    线程:其实可以先简单理解成cpu的一个执行流,指令序列。多支持多线程的程序(进程)可以取得真正的并行(parallelism),且由于共享进程的代码和全局数据,故线程间的通信是方便的。它的缺点也是由于线程共享进程的地址空间,因此可能会导致竞争,因此对某一块有多个线程要访问的数据需要一些同步技术。
    轻量级进程LWP
    既然称作轻量级进程,可见其本质仍然是进程,与普通进程相比,LWP与其它进程共享所有(或大部分)逻辑地址空间和系统资源,一个进程可以创建多个LWP,这样它们共享大部分资源;LWP有它自己的进程标识符,并和其他进程有着父子关系,父进程);这是和类Unix操作系统的系统调用vfork()生成的进程一样的。
    LWP由内核管理并像普通进程一样被调度。Linux内核是支持LWP的典型例子。Linux内核在 2.0.x版本就已经实现了轻量进程,应用程序可以通过一个统一的clone()系统调用接口,用不同的参数指定创建轻量进程还是普通进程,通过参数决定子进程和父进程共享的资源种类和数量,这样就有了轻重之分。在内核中, clone()调用经过参数传递和解释后会调用do_fork(),这个核内函数同时也是fork()、vfork()系统调用的最终实现。

    在大多数系统中,LWP与普通进程的区别也在于它只有一个最小的执行上下文和调度程序所需的统计信息,而这也是它之所以被称为轻量级的原因。
    因为LWP之间共享它们的大部分资源,所以它在某些应用程序就不适用了;这个时候就要使用多个普通的进程了

    用户线程
    用户线程指的是完全建立在用户空间的线程库,用户线程的建立,同步,销毁,调度完全在用户空间完成,不需要内核的帮助。因此这种线程的操作是极其快速的且低消耗的。

    操作系统线程模型

    线程实现在用户空间下

    当线程在用户空间下实现时,操作系统对线程的存在一无所知,操作系统只能看到进程,而不能看到线程。所有的线程都是在用户空间实现。在操作系统看来,每一个进程只有一个线程。过去的操作系统大部分是这种实现方式,这种方式的好处之一就是即使操作系统不支持线程,也可以通过库函数来支持线程。

    我们换一种通俗的方式来讲解这段话,首先就是在这在模型下,程序员需要自己实现线程的数据结构、创建销毁和调度维护。也就相当于需要实现一个自己的线程调度内核,而同时这些线程运行在操作系统的一个进程内,最后操作系统直接对进程进行调度。

    image.png

    这样做有一些优点,就是线程的调度只是在用户态,减少了操作系统从内核态到用户态的切换开销。
    当然缺点也很明显:这种模式最致命的缺点也是由于操作系统不知道线程的存在,因此当一个进程中的某一个线程进行系统调用时,比如缺页中断而导致线程阻塞,此时操作系统会阻塞整个进程,即使这个进程中其它线程还在工作

    线程实现在操作系统内核中

    内核线程就是直接由操作系统内核(Kernel)支持的线程,这种线程由内核来完成线程切换,内核通过操纵调度器(Scheduler)对线程进行调度,并负责将线程的任务映射到各个处理器上。每个内核线程可以视为内核的一个分身,这样操作系统就有能力同时处理多件事情,支持多线程的内核就叫做多线程内核(Multi-Threads Kernel)。

    通俗的讲就是,程序员直接使用操作系统中已经实现的线程,而线程的创建、销毁、调度和维护,都是靠操作系统(准确的说是内核)来实现,程序员只需要使用系统调用,而不需要自己设计线程的调度算法和线程对CPU资源的抢占使用

    目前的Linux已经基于NPTL实现了更符合POSIX标准的线程,之前的LinuxThreads早已经被替代。Pthread库目前也是基于NPTL实现,已经是一种符合POSIX标准的线程模型了,Linux摆脱了原来LWP的阴影。

    使用用户线程加轻量级进程混合实现

    在这种混合实现下,即存在用户线程,也存在轻量级进程。用户线程还是完全建立在用户空间中,因此用户线程的创建、切换、析构等操作依然廉价,并且可以支持大规模的用户线程并发。而操作系统提供支持的轻量级进程则作为用户线程和内核线程之间的桥梁,这样可以使用内核提供的线程调度功能及处理器映射,并且用户线程的系统调用要通过轻量级进程来完成,大大降低了整个进程被完全阻塞的风险。在这种混合模式中,用户线程与轻量级进程的数量比是不定的,即为N:M的关系:

    Linux线程的发展

    一直以来, linux内核并没有线程的概念. 每一个执行实体都是一个task_struct结构, 通常称之为进程. Linux内核在 2.0.x版本就已经实现了轻量进程,应用程序可以通过一个统一的clone()系统调用接口,用不同的参数指定创建轻量进程还是普通进程。在内核中, clone()调用经过参数传递和解释后会调用do_fork(),这个核内函数同时也是fork()、vfork()系统调用的最终实现。后来为了引入多线程,Linux2.0~2.4实现的是俗称LinuxThreads的多线程方式,到了2.6,基本上都是NPTL的方式了

    模型一 :LinuxThreads

    linux 2.6以前, pthread线程库对应的实现是一个名叫linuxthreads的lib.这种实现本质上是一种LWP的实现方式,即通过轻量级进程来模拟线程,内核并不知道有线程这个概念,在内核看来,都是进程。

    Linux采用的“一对一”的线程模型,即一个LWP对应一个线程。这个模型最大的好处是线程调度由内核完成了,而其他线程操作(同步、取消)等都是核外的线程库函数完成的。

    linux上的线程就是基于轻量级进程, 由用户态的pthread库实现的.使用pthread以后, 在用户看来, 每一个task_struct就对应一个线程, 而一组线程以及它们所共同引用的一组资源就是一个进程.但是, 一组线程并不仅仅是引用同一组资源就够了, 它们还必须被视为一个整体.

    对此, POSIX(portable operation system interface)标准提出了如下要求:

    1, 查看进程列表的时候, 相关的一组task_struct应当被展现为列表中的一个节点;
    2, 发送给这个"进程"的信号(对应kill系统调用), 将被对应的这一组task_struct所共享, 并且被其中的任意一个"线程"处理;
    3, 发送给某个"线程"的信号(对应pthread_kill), 将只被对应的一个task_struct接收, 并且由它自己来处理;
    4, 当"进程"被停止或继续时(对应SIGSTOP/SIGCONT信号), 对应的这一组task_struct状态将改变;
    5, 当"进程"收到一个致命信号(比如由于段错误收到SIGSEGV信号), 对应的这一组task_struct将全部退出;
    6, 等等(以上可能不够全);

    在LinuxThreads中,专门为每一个进程构造了一个管理线程,负责处理线程相关的管理工作。当进程第一次调用pthread_create()创建一个线程的时候就会创建并启动管理线程。然后管理线程再来创建用户请求的线程。也就是说,用户在调用pthread_create后,先是创建了管理线程,再由管理线程创建了用户的线程。

    linuxthreads利用前面提到的轻量级进程来实现线程, 但是对于POSIX提出的那些要求,linuxthreads除了第5点以外, 都没有实现(实际上是无能为力):

    1, 如果运行了A程序, A程序创建了10个线程, 那么在shell下执行ps命令时将看到11个A进程, 而不是1个(注意, 也不是10个, 下面会解释);
    
    2, 不管是kill还是pthread_kill, 信号只能被一个对应的线程所接收;
    
    3, SIGSTOP/SIGCONT信号只对一个线程起作用;
    

    还好linuxthreads实现了第5点, 我认为这一点是最重要的. 如果某个线程"挂"了, 整个进程还在若无其事地运行着, 可能会出现很多的不一致状态. 进程将不是一个整体, 而线程也不能称为线程. 或许这也是为什么linuxthreads虽然与POSIX的要求差距甚远, 却能够存在, 并且还被使用了好几年的原因吧~

    但是, linuxthreads为了实现这个"第5点", 还是付出了很多代价, 并且创造了linuxthreads本身的一大性能瓶颈.

    为什么A程序创建了10个线程, 但是ps时却会出现11个A进程了. 因为linuxthreads自动创建了一个管理线程. 上面提到的"第5点"就是靠管理线程来实现的.

    当程序开始运行时, 并没有管理线程存在(因为尽管程序已经链接了pthread库, 但是未必会使用多线程).

    程序第一次调用pthread_create时, linuxthreads发现管理线程不存在, 于是创建这个管理线程. 这个管理线程是进程中的第一个线程(主线程)的儿子.
    然后在pthread_create中, 会通过pipe向管理线程发送一个命令, 告诉它创建线程. 即是说, 除主线程外, 所有的线程都是由管理线程来创建的, 管理线程是它们的父亲.
    于是, 当任何一个子线程退出时, 管理线程将收到SIGUSER1信号(这是在通过clone创建子线程时指定的). 管理线程在对应的sig_handler中会判断子线程是否正常退出, 如果不是, 则杀死所有线程, 然后自杀.

    那么, 主线程怎么办呢? 主线程是管理线程的父亲, 其退出时并不会给管理线程发信号. 于是, 在管理线程的主循环中通过getppid检查父进程的ID号, 如果ID号是1, 说明父亲已经退出, 并把自己托管给了init进程(1号进程). 这时候, 管理线程也会杀掉所有子线程, 然后自杀.

    可见, 线程的创建与销毁都是通过管理线程来完成的, 于是管理线程就成了linuxthreads的一个性能瓶颈.
    创建与销毁需要一次进程间通信, 一次上下文切换之后才能被管理线程执行, 并且多个请求会被管理线程串行地执行.

    这种通过LWP的方式来模拟线程的实现看起来还是比较巧妙的,但也存在一些比较严重的问题:

    1)线程ID和进程ID的问题
    按照POSIX的定义,同一进程的所有的线程应该共享同一个进程和父进程ID,而Linux的这种LWP方式显然不能满足这一点。

    2)信号处理问题

    异步信号是以进程为单位分发的,而Linux的线程本质上每个都是一个进程,且没有进程组的概念,所以某些缺省信号难以做到对所有线程有效,例如SIGSTOP和SIGCONT,就无法将整个进程挂起,而只能将某个线程挂起。

    3)线程总数问题
    LinuxThreads将每个进程的线程最大数目定义为1024,但实际上这个数值还受到整个系统的总进程数限制,这又是由于线程其实是核心进程。

    4)管理线程问题

    管理线程容易成为瓶颈,这是这种结构的通病;同时,管理线程又负责用户线程的清理工作,因此,尽管管理线程已经屏蔽了大部分的信号,但一旦管理线程死亡,用户线程就不得不手工清理了,而且用户线程并不知道管理线程的状态,之后的线程创建等请求将无人处理。

    5)同步问题
    LinuxThreads中的线程同步很大程度上是建立在信号基础上的,这种通过内核复杂的信号处理机制的同步方式,效率一直是个问题。

    模型二:NPTL

    到了linux 2.6, glibc中有了一种新的pthread线程库--NPTL(Native POSIX Threading Library).

    本质上来说,NPTL还是一个LWP的实现机制,但相对原有LinuxThreads来说,做了很多的改进。下面我们看一下NPTL如何解决原有LinuxThreads实现机制的缺陷

    NPTL实现了前面提到的POSIX的全部5点要求. 但是, 实际上, 与其说是NPTL实现了, 不如说是linux内核实现了.

    在linux 2.6中, 内核有了线程组的概念, task_struct结构中增加了一个tgid(thread group id)字段.

    如果这个task是一个"主线程", 则它的tgid等于pid, 否则tgid等于进程的pid(即主线程的pid).

    在clone系统调用中, 传递CLONE_THREAD参数就可以把新进程的tgid设置为父进程的tgid(否则新进程的tgid会设为其自身的pid).

    类似的XXid在task_struct中还有两 个:task->signal->pgid保存进程组的打头进程的pid、task->signal->session保存会话 打头进程的pid。通过这两个id来关联进程组和会话。

    有了tgid, 内核或相关的shell程序就知道某个tast_struct是代表一个进程还是代表一个线程, 也就知道在什么时候该展现它们, 什么时候不该展现(比如在ps的时候, 线程就不要展现了).
    而getpid(获取进程ID)系统调用返回的也是tast_struct中的tgid, 而tast_struct中的pid则由gettid系统调用来返回.

    在执行ps命令的时候不展现子线程,也是有一些问题的。比如程序a.out运行时,创建 了一个线程。假设主线程的pid是10001、子线程是10002(它们的tgid都是10001)。这时如果你kill 10002,是可以把10001和10002这两个线程一起杀死的,尽管执行ps命令的时候根本看不到10002这个进程。如果你不知道linux线程背 后的故事,肯定会觉得遇到灵异事件了

    为了应付"发送给进程的信号"和"发送给线程的信号", task_struct里面维护了两套signal_pending, 一套是线程组共享的, 一套是线程独有的.

    通过kill发送的信号被放在线程组共享的signal_pending中, 可以由任意一个线程来处理; 通过pthread_kill发送的信号(pthread_kill是pthread库的接口, 对应的系统调用中tkill)被放在线程独有的signal_pending中, 只能由本线程来处理.

    当线程停止/继续, 或者是收到一个致命信号时, 内核会将处理动作施加到整个线程组中

    Java线程

    Java线程在操作系统上本质

    Java线程在JDK1.2之前,是基于称为“绿色线程”(Green Threads)的用户线程实现的,而在JDK1.2中,线程模型替换为基于操作系统原生线程模型来实现。因此,在目前的JDK版本中,操作系统支持怎样的线程模型,在很大程度上决定了Java虚拟机的线程是怎样映射的,这点在不同的平台上没有办法达成一致,虚拟机规范中也并未限定Java线程需要使用哪种线程模型来实现。线程模型只对线程的并发规模和操作成本产生影响,对Java程序的编码和运行过程来说,这些差异都是透明的。
    也就说JDK1.2之前,程序员们为JVM开发了自己的一个线程调度内核,而到操作系统层面就是用户空间内的线程实现。而到了JDK1.2及以后,JVM选择了更加稳健且方便使用的操作系统原生的线程模型,通过系统调用,将程序的线程交给了操作系统内核进行调度。现在的Java中线程的本质,其实就是操作系统中的线程**

    image.png

     

    特别注意:这些线程的状态时JVM中的线程状态!不是操作系统中的线程状态。

    操作系统中的进程(线程)状态**(区分和JVM中的线程状态)

    image.png

    这里需要着重解释一点,在现在的操作系统中,因为线程依旧被视为轻量级进程,所以操作系统中线程的状态实际上和进程状态是一致的模型。

    操作系统中线程和Java线程状态的关系:

    从实际意义上来讲,操作系统中的线程除去new和terminated状态,一个线程真实存在的状态,只有:

    • ready:表示线程已经被创建,正在等待系统调度分配CPU使用权。
    • running:表示线程获得了CPU使用权,正在进行运算
    • waiting:表示线程等待(或者说挂起),让出CPU资源给其他线程使用
      为什么除去new和terminated状态?是因为这两种状态实际上并不存在于线程运行中,所以也没什么实际讨论的意义

    对于Java中的线程状态
    无论是Timed Waiting ,Waiting还是Blocked,对应的都是操作系统线程的waiting(等待)状态。而Runnable状态,则对应了操作系统中的ready和running状态。
    状态转换

    image.png

     

    Java语言定义了5种线程状态,在任意一个时间点,一个线程只能有且只有其中的一种状态,这5种状态分别如下:

    1)新建(New):创建后尚未启动的线程处于这种状态。

    2)运行(Runable):Runable包括了操作系统线程状态中的Running和Ready,也就是处于此状态的线程有可能正在执行,也有可能正在等待着CPU为它分配执行时间。

    3)无限期等待(Waiting):处于这种状态的线程不会被分配CPU执行时间,它们要等待被其它线程显示地唤醒。以下方法会让线程陷入无限期的等待状态:

    • 没有设置Timeout参数的Object.wait()方法
    • 没有设置Timeout参数的Thread.join()方法
    • LockSupport.park()方法
    1. 限期等待(Timed Waiting):处于这种状态的线程也不会被分配CPU执行时间,不过无须等待被其它线程显示地唤醒,在一定时间之后它们会由系统自动唤醒。以下方法会让线程进入限期等待状态:
      Thread.sleep()方法
      设置了Timeout参数的Object.wait()方法
      设置了Timeout参数的Thread.join()方法
      LockSupport.parkNanos()方法
      LockSupport.parkUntil()方法
    

    而对不同的操作系统,由于本身设计思路不一样,对于线程的设计也存在种种差异,所以JVM在设计上,就已经声明:

    虚拟机中的线程状态,不反应任何操作系统线程状态

    java主线程结束和子线程结束之间的关系

    Main线程是个非守护线程,不能设置成守护线程。

    这是因为,main线程是由java虚拟机在启动的时候创建的。main方法开始执行的时候,主线程已经创建好并在运行了。对于运行中的线程,调用Thread.setDaemon()会抛出异常Exception in thread "main" java.lang.IllegalThreadStateException。测试代码如下:

    public class MainTest
    {
        public static void main(String[] args)
        {
            System.out.println(" parent thread begin ");
            Thread.currentThread().setDaemon(true);
        }
    }
    

    Main线程结束,其他线程一样可以正常运行。

    主线程,只是个普通的非守护线程,用来启动应用程序,不能设置成守护线程;除此之外,它跟其他非守护线程没有什么不同。主线程执行结束,其他线程一样可以正常执行。代码如下:

    public class ParentTest
    {
     
        public static void main(String[] args)
        {
            System.out.println("parent thread begin ");
            
            ChildThread t1 = new ChildThread("thread1");
            ChildThread t2 = new ChildThread("thread2");
            t1.start();
            t2.start();
     
            System.out.println("parent thread over ");
        }
    }
     
    class ChildThread extends Thread
    {
        private String name = null;
     
        public ChildThread(String name)
        {
            this.name = name;
        }
     
        @Override
        public void run()
        {
            System.out.println(this.name + "--child thead begin");
     
            try
            {
                Thread.sleep(500);
            }
            catch (InterruptedException e)
            {
                System.out.println(e);
            }
     
            System.out.println(this.name + "--child thead over");
        }
    }
    

    这样其实是很合理的,按照操作系统的理论,进程是资源分配的基本单位,线程是CPU调度的基本单位。对于CPU来说,其实并不存在java的主线程和子线程之分,都只是个普通的线程。进程的资源是线程共享的,只要进程还在,线程就可以正常执行,换句话说线程是强依赖于进程的。也就是说,线程其实并不存在互相依赖的关系,一个线程的死亡从理论上来说,不会对其他线程有什么影响。

    Main线程结束,其他线程也可以立刻结束,当且仅当这些子线程都是守护线程。

    java虚拟机(相当于进程)退出的时机是:虚拟机中所有存活的线程都是守护线程。只要还有存活的非守护线程虚拟机就不会退出,而是等待非守护线程执行完毕;反之,如果虚拟机中的线程都是守护线程,那么不管这些线程的死活java虚拟机都会退出。测试代码如下:

    public class ParentTest
    {
     
        public static void main(String[] args)
        {
            System.out.println("parent thread begin ");
            
            ChildThread t1 = new ChildThread("thread1");
            ChildThread t2 = new ChildThread("thread2");
            t1.setDaemon(true);
            t2.setDaemon(true);
            
            t1.start();
            t2.start();
     
            System.out.println("parent thread over ");
        }
    }
    class ChildThread extends Thread
    {
        private String name = null;
        public ChildThread(String name)
        {
            this.name = name;
        }
        @Override
        public void run()
        {
            System.out.println(this.name + "--child thead begin");
            try
            {
                Thread.sleep(500);
            }
            catch (InterruptedException e)
            {
                System.out.println(e);
            }
            System.out.println(this.name + "--child thead over");
        }
    }
     
     执行结果如下:
    parent thread begin
    parent thread over
    thread1--child thead begin
    thread2--child thead begin
    

    在这种情况下,的确主线程退出,子线程就立刻结束了,但是这是属于JVM的底层实现机制,并不是说主线程和子线程之间存在依赖关系。

    linux中用top、ps命令查看进程中的线程

    在ps命令中,“-T”选项可以开启线程查看。下面的命令列出了由进程号为<pid>的进程创建的所有线程。

    1.$ ps -T -p <pid>

    1.$ ps -T -p <pid>

    top命令可以实时显示各个线程情况。要在top输出中开启线程查看,请调用top命令的“-H”选项,该选项会列出所有Linux线程。在top运行时,你也可以通过按“H”键将线程查看模式切换为开或关。
    1.$ top -H

    要让top输出某个特定进程<pid>并检查该进程内运行的线程状况:

    2.$ top -H -p <pid>

    展开全文
  • 功能: 在tomcat启动时,就自动执行一servlet,此servlet隔段时间处理某一操作。
  • 需求的话,具体是需要在项目启动时 ,开启一个异步线程,并且需要线程一直执行。 用的springboot。 本来想的是在需要执行的异步方法加上@Order(value = 1) ,ps:”该注解可以指定在项目启动后,就开始执行哪些方法...

    需求的话,具体是需要在项目启动时 ,开启一个异步线程,并且需要线程一直执行。
    用的springboot。
    本来想的是在需要执行的异步方法加上@Order(value = 1) ,ps:”该注解可以指定在项目启动后,就开始执行哪些方法,value值表示执行顺序 ,越小越靠前,不能为0”,但是发现启动后,异步方法一直执行,但主线程并没有继续执行,程序也就没跑起来。 后来经过打印发现, 此时异步方法并没有开启异步线程,依然在用主线程跑,所以项目也就没启动,因为她需要异步方法执行完成后才能继续,而异步方法这边设定的是一个 死循环。
    解决方法呢,就是另外写一个方法 ,加上@Order(value = 1)注解,再在方法内部调用需要执行的异步方法,就开启异步线程了。
    其实很简单的一个问题,但当时头昏了,一下没想出来该咋办。。还耽误我一些时间 ,哈哈哈哈

    展开全文
  • 线程的创建及启动方式 import java.util.concurrent.*; public class T02_HowToCreateThread { /** * 继承 Thread, 重写 run() */ static class MyThread extends Thread { @Override public void run() { //...
  • Java线程超详解

    万次阅读 多人点赞 2019-06-11 01:00:30
    引言 随着计算机的配置越来越高,我们需要将进程进一步优化,细分为线程,充分提高图形化界面的多线程的开发。这就要求对线程的掌握很...启动当前线程2.调用线程中的run方法 2.run():通常需要重写Thread类中的此...
  • Java线程面试题(面试必备)

    万次阅读 多人点赞 2020-05-26 01:15:38
    2. 线程与进程2.1 什么是线程与进程2.2 线程与进程的区别2.3 用户线程与守护线程2.4 什么是线程死锁2.5 形成死锁的四个必要条件2.6 如何避免死锁3. 创建线程的四种方式4. 线程状态和基本操作 一、多线程基础基础...
  • Java项目main方法启动的两种方式

    千次阅读 2021-02-12 15:45:48
    1.打包时指定了主类,可以直接用java -jar xxx.jar。maven-assembly-pluginfalsejar-with-dependenciescom.TestApplicationmake-assemblypackageassembly2.打包时没有指定主类,可以用java -cp xxx.jar 主类名称...
  • java线程启动,停止线程

    千次阅读 2016-10-19 22:34:33
    启动线程的两种方式 暂停线程 停止线程 线程的优先级 守护线程 线程的启动 启动线程的两种方式 继承Thread类 线程代码: package com.sun.thread; public class MyThread extends Thread { @...
  • 详解Java线程-守护线程与用户线程

    千次阅读 2021-02-26 13:48:02
    守护进程这个概念最早还是在linux系统中接触的,直到近期使用java开发心跳检测功能时,使用Timer时才发现原来java也有守护线程的概念。1. Java 线程1.1 守护线程与用户线程Java 线程分为守护线程(DaemonThread) 和 ...
  • Java JVM启动参数

    万次阅读 2018-08-22 22:51:46
    已经使用了一段时间的Java,期间也了解和用过一些JVM(Java Virtual Machine)参数,但是没有仔细去整理过每个参数代表的意义,为此整理成文。 Java使用方式 java [options] classname [args] java [options] -jar ...
  • Java线程学习(吐血超详细总结)

    万次阅读 多人点赞 2015-03-14 13:13:17
    本文主要讲了java中多线程的使用方法、线程同步、线程数据传递、线程状态及相应的一些线程函数用法、概述等。
  • Java命令行启动设置JVM参数

    千次阅读 2021-05-26 17:53:02
    使用Java命令启动应用的时候适当的配置你的参数能有效提高系统的性能。 Xms:是指程序启动时初始内存大小(此值可以设置成与-Xmx相同,以避免每次GC完成后 JVM 内存重新分配)。 Xmx:指程序运行时最大可用内存...
  • java线程面试题及答案

    千次阅读 2021-09-18 09:20:33
    并行和并发有什么区别? 并行是指两个或者多个事件在同一时刻发生;而并发是指两个或多个事件在同一时间间隔发生。并行没有对 CPU 资源的抢占;...2、进程有自己的独立地址空间,每启动一个进程,系统就.
  • 启动线程的三种方式

    千次阅读 2021-01-01 07:41:29
    文章目录前言一、启动线程的第一种方法:继承类二、启动线程的第二种方式:实现接口三、启动线程的第三种方式:匿名内部类 前言 启动线程一共有三种方法:继承类,实现接口,匿名内部类。 一、启动线程的第一种...
  • 万字图解Java线程

    万次阅读 多人点赞 2020-09-06 14:45:07
    java线程我个人觉得是javaSe中最难的一部分,我以前也是感觉学会了,但是真正有多线程的需求却不知道怎么下手,实际上还是对多线程这块知识了解不深刻,不知道多线程api的应用场景,不知道多线程的运行流程等等,...
  • java线程-学习总结(完整版)

    千次阅读 多人点赞 2020-12-04 00:02:16
    // runnable } } 就绪&运行 Runable&Running 当我们新建线程完后执行start才进入就绪状态Runnable,线程内部调用了run方法时进入运行阶段Running,但是直接执行run方法不是启动线程,具体如下验证。 public class ...
  • Java线程编程基础(详细)

    万次阅读 多人点赞 2020-11-03 17:36:30
    Java线程编程基础进程与线程线程实现Thread类实现多线程Runnable接口实现多线程Callable接口实现多线程线程运行状态多线程常用操作方法线程的命名和获取线程休眠线程中断线程强制执行线程让步线程优先级设定...
  • java 线程知识点总结

    千次阅读 2022-04-22 12:00:09
    程序、进程、线程: 程序:是指含有指令和数据的文件,被存储在磁盘或其他...进程:是程序的一次执行过程,是代码在数据集合上的一次运行活动,是系统资源分配和调度的基本单位; 线程:是进程的一个执行路径, ...
  • (通俗易懂)Java线程基础知识系统化详解+代码例子

    千次阅读 多人点赞 2019-12-14 20:26:45
    进程是操作系统结构的基础;是一次程序的执行;是一个程序及其数据在处理机上顺序执行时所发生的活动。操作系统中,几乎所有运行中的任务对应一条进程(Process)。一个程序进入内存运行,即变成一个进程。进程是...
  • java线程 面试题整理(更新......)

    千次阅读 2021-11-30 16:23:06
    3、什么是同步执行和异步执行4、Java中实现多线程有几种方法?(较难)(1)继承Thread类(2)实现runable接口(3)实现Callable接口(创建FutureTask(Callable)对象)5、Future接口,Callable接口,FutureTask实现类的...
  • jMeter 线程启动时间

    千次阅读 2019-06-09 11:16:00
    jMeter里设置线程数量为5: ![clipboard1]... 其实这是因为我设置的Ramp up period参数在起作用: ...1. 决定多长时间启动所有线程。如果使用10个线程,r...
  • 该文档写于 2016年在银联基于数据驱动的方式落地了新一代清分系统,当时使用的IDEA工具还是Eclipse.放出来吧...也纯当是纪念了。
  • 分析java 线程占用内存 本文将为您提供一个教程,使您可以确定活动应用程序Java线程中保留了多少Java堆空间 。 将提供来自Oracle Weblogic 10.0生产环境的真实案例研究,以使您更好地理解分析过程。 我们还将尝试...
  • 主要是 Java 并发, 停止线程的方法分析
  • Java线程(超详细!)

    万次阅读 多人点赞 2021-05-12 17:00:59
    注意:一个进程可以启动多个线程。 eg.对于java程序来说,当在DOS命令窗口中输入: java HelloWorld 回车之后。 会先启动JVM,而JVM就是一个进程。 JVM再启动一个主线程调用main方法。 同时再启动一个垃圾回收线程...
  • JAVA线程是什么

    千次阅读 2020-08-31 23:53:23
    一、 什么是多线程: ...多线程:线程是进程内部比进程更小的执行单元(执行流|程序片段),每个线程完成一个任务,每个进程内部包含了多个线程每个线程做自己的事情,在进程中的所有线程共享该进程的资源; 主
  • java--守护线程

    千次阅读 2022-02-20 19:21:37
    守护线程 gc线程线程不定时回收垃圾)属于守护线程 当所有的非守护线程结束时,程序也就终止了,同时会杀死进程中的所有守护线程 和主线程一起销毁 setDaemon(true)必须在调用线程的start()方法之前设置,否则...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 282,274
精华内容 112,909
关键字:

java系统启动自动启动线程

java 订阅