【JAVA秒会技术之玩转多线程】多线程那些事儿(一)

    xiaoxiao2023-08-14  170

    多线程那些事儿(一)

        现在只要出去面试,关于“Java多线程”的问题,几乎没有一家单位不问的,可见其重要性。于是博主抽空研究了一下,确实很有意思!以下是我综合整理了网上的各种资料,和个人的一些理解,写的一篇总结博文,仅供学习、交流。

    (一)多线程的概念

            多线程的概念,简单理解:一个进程运行时产生了不止一个线程

            进程的概念,简单理解:正在运行的程序的实例

    两者之间的关系:进程就是线程的容器

    (二)多线程的好处

            使用多线程可以提高CPU的利用率。在多线程程序中,一个线程必须等待的时候,CPU可以运行其它的线程而不是等待,这样就大大提高了程序的效率。

    (三)线程状态转换

     

    1新建状态(New):新创建了一个线程对象。

    2就绪状态(Runnable):线程对象创建后,其他线程调用了该对象的start()方法。该状态的线程位于可运行线程池中,变得可运行,等待获取CPU的使用权。

    3运行状态(Running):就绪状态的线程获取了CPU,执行程序代码。

    4阻塞状态(Blocked):阻塞状态是线程因为某种原因放弃CPU使用权,暂时停止运行。直到线程进入就绪状态,才有机会转到运行状态。阻塞的情况分三种:

            ①等待阻塞:运行的线程执行wait()方法,JVM会把该线程放入等待池中。

            ②同步阻塞:运行的线程在获取对象的同步锁时,若该同步锁被别的线程占用,则JVM会把该线程放入锁池中。

            ③其他阻塞:运行的线程执行sleep()join()方法,或者发出了I/O请求时,JVM会把该线程置为阻塞状态。当sleep()状态超时、join()等待线程终止或者超时、或者I/O处理完毕时,线程重新转入就绪状态。

    5死亡状态(Dead):线程执行完了或者因异常退出了run()方法,该线程结束生命周期。

    (五)创建线程的方式

           创建线程的方式主要有两种:

      (1)一种是继承Tread

    package com.liyan.Test; public class MyThread extends Thread { //继承Tread类 @Override public void run(){ //重写run方法 try{ ....... }catch(Exception e){ e.printStackTrace(); } } public static void main(String[] args) { MyThread myThread = new MyThread(); //产生新的线程对象 myThread.start(); //开启线程 } }

      (2)一种是实现Runnable接口

    package com.liyan.gcTest; public class MyThread implements Runnable { //实现Runnable接口 @Override public void run(){ //重写run方法 try{ ....... }catch(Exception e){ e.printStackTrace(); } } public static void main(String[] args) { MyThread myThread = new MyThread(); //产生新的线程对象 Thread thread = new Thread(myThread); //将创建的线程作为参数传入 thread.start(); //开启线程 } }

    (六)终止线程的方式

       1. 使用退出标志终止线程

           run方法执行完后,线程就会退出。但有时run方法是永远不会结束的。如在服务端程序中使用线程进行监听客户端请求,或是其他的需要循环处理的任务。在这种情况下,一般是将这些任务放在一个循环中,如while循环。如果想让循环永远运行下去,可以使用whiletrue{……}来处理。但要想使while循环在某一特定条件下退出,最直接的方法就是设一个boolean类型的标志,并通过设置这个标志为truefalse来控制while循环是否退出。下面给出了一个利用退出标志终止线程的例子。

    package com.liyan.thread; public class ThreadDemo extends Thread { public volatile boolean exit = false; public void run() { while (!exit); } public static void main(String[] args) throws Exception { ThreadDemo thread = new ThreadDemo(); thread.start(); sleep(5000); // 主线程延迟5秒 thread.exit = true; // 终止线程thread thread.join(); System.out.println("线程退出!"); } }

    2. 使用stop方法终止线程   在上面代码中定义了一个退出标志exit,当exittrue时,while循环退出,exit的默认值为false.在定义exit时,使用了一个Java关键字volatile,这个关键字的目的是使exit同步,也就是说在同一时刻只能由一个线程来修改exit的值

        使用stop方法可以强行终止正在运行或挂起的线程。我们可以使用如下的代码来终止线程:

    thread.stop();

    3. 使用interrupt方法终止线程    虽然使用上面的代码可以终止线程,但使用stop方法是很危险的,就象突然关闭计算机电源,而不是按正常程序关机一样,可能会产生不可预料的结果,因此,并不推荐使用stop方法来终止线程。

        使用interrupt方法来终端线程可分为两种情况:

       (1)线程处于阻塞状态,如使用了sleep方法。

       (2)使用while(!isInterrupted()){……}来判断线程是否被中断。

        在第一种情况下使用interrupt方法,sleep方法将抛出一个InterruptedException例外,在第二种情况下线程将直接退出。下面的代码演示了在第一种情况下使用interrupt方法。

    package com.liyan.thread; public class ThreadDemo extends Thread { public void run() { try { sleep(50000); // 延迟50秒 } catch (InterruptedException e) { System.out.println(e.getMessage()); } } public static void main(String[] args) throws Exception { Thread thread = new ThreadDemo(); thread.start(); System.out.println("在50秒之内按任意键中断线程!"); System.in.read(); thread.interrupt(); thread.join(); System.out.println("线程已经退出!"); } }

        输出结果:

    在50秒之内按任意键中断线程! liyan sleep interrupted 线程已经退出!

        在调用interrupt方法后,sleep方法抛出异常,然后输出错误信息:sleep interrupted.

        注意:在Thread类中有两个方法可以判断线程是否通过interrupt方法被终止。一个是静态的方法interrupted(),一个是非静态的方法isInterrupted(),这两个方法的区别是interrupted用来判断当前线是否被中断,而isInterrupted可以用来判断其他线程是否被中断。因此,while(!isInterrupted())也可以换成while(!Thread.interrupted())。

       【参考:http://www.bitscn.com/pdb/java/200904/161228_3.html

    (七)Daemon线程

        在Java中有两类线程:User Thread(用户线程)Daemon Thread(守护线程) 

        用个比较通俗的比如,任何一个守护线程都是整个JVM中所有非守护线程的保姆:

        只要当前JVM实例中尚存在任何一个非守护线程没有结束,守护线程就全部工作;只有当最后一个非守护线程结束时,守护线程随着JVM一同结束工作。

            Daemon的作用是为其他线程的运行提供便利服务,它的优先级比较低,用于为系统中的其它对象和线程提供服务。守护线程不依赖于终端,但是依赖于系统,与系统“同生共死”

        守护线程最典型的应用就是GC (垃圾回收器),当我们的程序中不再有任何运行的hread,程序就不会再产生垃圾,垃圾回收器也就无事可做,所以当垃圾回收线程是JVM上仅剩的线程时,垃圾回收线程会自动离开。它始终在低级别的状态中运行,用于实时监控和管理系统中的可回收资源。

             UserDaemon两者几乎没有区别,唯一的不同之处就在于虚拟机的离开:如果User Thread已经全部退出运行了,只剩下Daemon Thread存在了,虚拟机也就退出了。 因为没有了被守护者,Daemon也就没有工作可做了,也就没有继续运行程序的必要了。

    // 设定 daemonThread 为 守护线程,default false(非守护线程) daemonThread.setDaemon(true); // 验证当前线程是否为守护线程,返回 true 则为守护线程 daemonThread.isDaemon();

       (1thread.setDaemon(true)必须在thread.start()之前设置,否则会跑出一个IllegalThreadStateException异常。你不能把正在运行的常规线程设置为守护线程。      这里有几点需要注意

       (2Daemon线程中产生的新线程也是Daemon

       (3不要认为所有的应用都可以分配给Daemon来进行服务,比如读写操作或者计算逻辑因为你不可能知道在所有的User完成之前,Daemon是否已经完成了预期的服务任务。一旦User退出了,可能大量数据还没有来得及读入或写出,计算任务也可能多次运行结果不一样。这对程序是毁灭性的。造成这个结果理由已经说过了:一旦所有User Thread离开了,虚拟机也就退出运行了。

       【参考:http://blog.csdn.net/shimiso/article/details/8964414

    相关资源:秒杀多线程
    最新回复(0)