• 线程生命周期和多线程的实现


    线程的生命周期

    新建(New)、就绪(Runnable)、执行(Running)、阻塞(Blocked)、死亡(Dead)

    新建状态
    当程序使用 new 关键字创建了一个线程之后,该线程就处于新建状态,此时仅由 JVM 为其分配内存,并初始化其成员变量的值。
    就绪状态
    当线程对象调用了 start()方法之后,该线程处于就绪状态。 Java 虚拟机会为其创建方法调用栈和程序计数器,等待调度运行。
    运行状态
    如果处于就绪状态的线程获得了 CPU,开始执行 run()方法的线程执行体,则该线程处于运行状态。

    阻塞状态
    阻塞状态是指线程因为某种原因放弃了 cpu 使用权,也即让出了 cpu timeslice,暂时停止运行。直到线程进入就绪状态,才有机会再次获得 cpu timeslice 转到运行(running)状态。阻塞的情况分三种:

    1.等待阻塞(o.wait->等待队列)
    运行(running)的线程执行 o.wait()方法, JVM 会把该线程放入等待队列中。
    2.同步阻塞(lock->锁池)
    线程在获取对象的同步锁时,若该同步锁被别的线程占用,JVM会把该线程放入锁池中。
    3.其他阻塞(sleep/join)
    线程执行 Thread.sleep(long ms)或 t.join()方法,或者发出了 I/O 请求时,JVM 会把该线程置为阻塞状态。
    当 sleep()状态超时、 join()等待线程终止或者超时、或者 I/O处理完毕时,线程重新转入就绪状态。
    sleep 与 wait 区别
    1. 调用Thread::sleep()方法线程不会释放对象锁,让出 cpu 该其他线程,但是他的监控状态依然保持者,当指定的时间到了又会自动恢复运行状态。
    2. 调用 Object::wait()方法,线程会放弃对象锁,进入等待锁定池,只有针对此对象调用 notify()方法后,本线程才进入对象锁定池,准备获取对象锁进入运行状态。

    线程死亡
    1.正常结束, run()或 call()方法执行完成,线程正常结束。(包扩使用退出标志可控地退出线程和调用interrupt()方法)

    interrupt()
    1. 线程处于阻塞状态: 如使用了 sleep,同步锁的 wait,socket 中的 receiver,accept 等方法时,
    会使线程处于阻塞状态。当调用线程的 interrupt()方法时,会抛出 InterruptException 异常。
    阻塞中的那个方法抛出这个异常,通过代码捕获该异常,然后 break 跳出循环状态,从而让
    我们有机会结束这个线程的执行。 通常很多人认为只要调用 interrupt 方法线程就会结束,实
    际上是错的, 一定要先捕获 InterruptedException 异常之后通过 break 来跳出循环,才能正
    常结束 run 方法。
    2. 线程未处于阻塞状态: 使用 isInterrupted()判断线程的中断标志来退出循环。当使用
    interrupt()方法时,中断标志就会置 true,和使用自定义的标志来控制循环是一样的道理。
    异常结束,线程抛出一个未捕获的 Exception 或 Error。

    2.异常结束,线程抛出一个未捕获的 Exception 或 Error。

    3.调用 stop(线程不安全,不推荐使用)。

    类似突然关闭计算机电源,而不是按正常程序关机一样。thread.stop()调用之后,创建子线程的线程就会抛出 ThreadDeatherror 的错误,并且会释放子线程所持有的所有锁,可能会造成数据不一致。

    JAVA实现多线程的方法

    一. 继承Thread类

    public class MyThread extends Thread { 
        private String name; 
        public MyThread(String name){
           this.name = name;
        }
      public void run() {  
       System.out.println(name+":is running!");  
      }  
    }  
     
     public class test{
     
        public static void main(String[] args){
        
            MyThread myThread1 = new MyThread("线程1");  
            MyThread myThread2 = new MyThread("线程2");  
            myThread1.start();  //一个线程只能启动一次,不能多次启动。
            myThread2.start();
        }
     }


    二. 实现Runnable接口
    如果自己的类已经extends另一个类,就无法直接extends Thread,此时,可以实现一个Runnable接口。

    public class MyThread extends OtherClass implements Runnable {  
        private String name; 
        public MyThread(String name){
           this.name = name;
        }
      public void run() {  
         System.out.println(name+":is running!");    
       }  
    }
    
    
     public class test{
     
        public static void main(String[] args){
            //为了启动MyThread,需要首先实例化一个Thread,并传入自己的MyThread实例:
            MyThread myThread = new MyThread("我的线程");  
            Thread thread = new Thread(myThread);  
            thread.start();  
        }
     }

    三. 使用 FutureTask 实现有返回结果的线程

    使用 FutureTask 实现有返回结果的线程
    FutureTask 是一个可取消的异步计算任务,是一个独立的类,实现了 Future、Runnable接口。FutureTask 的出现是为了弥补Thread 的不足而设计的,可以让程序员跟踪、获取任务的执行情况、计算结果 。
    因为 FutureTask实现了 Runnable,所以 FutureTask 可以作为参数来创建一个新的线程来执行,也可以提交给 Executor 执行。FutureTask 一旦计算完成,就不能再重新开始或取消计算。

    FutureTask的构造方法,可以接受 Runnable,Callable 的子类实例

    //创建一个 FutureTask,一旦运行就执行给定的 Callable。
    public FutureTask(Callable<V> callable);
    //创建一个 FutureTask,一旦运行就执行给定的 Runnable,并安排成功完成时 get 返回给定的结果 。
    public FutureTask(Runnable runnable, V result)
    class MyCallable implements Callable<Double>{
    
        @Override
        public Double call() {
             double d = 0;
             try {
                 System.out.println("异步计算开始.......");
                  d = Math.random()*10;
                 d += 1000;
                Thread.sleep(2000);
                 System.out.println("异步计算结束.......");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            return d;
        }
    }
    
    public class Test {
        public static void main(String[] args) throws InterruptedException, ExecutionException {
    
            FutureTask<Double> task = new FutureTask(new MyCallable());
            //创建一个线程,异步计算结果
            Thread thread = new Thread(task);
            thread.start();
            //主线程继续工作
            Thread.sleep(1000);
            System.out.println("主线程等待计算结果...");
            //当需要用到异步计算的结果时,阻塞获取这个结果
            Double d = task.get();
            System.out.println("计算结果是:"+d);
    
    
            //用同一个 FutureTask 再起一个线程
            Thread thread2 = new Thread(task);
            thread2.start();
        }
    }
    运行结果:
    异步计算开始.......
    主线程等待计算结果...
    异步计算结束.......
    计算结果是:1002.7806590582911

     四. 基于线程池的方式

    前面三种方法,都是显式地创建一个线程,可以直接控制线程,如线程的优先级、线程是否是守护线程,线程何时启动等等。而第四种方法,则是创建一个线程池,池中可以有1个或多个线程,这些线程都是线程池去维护,控制程序员不需要关心这些细节,只需要将任务提交给线程池去处理便可,非常方便。

    创建线程池的前提最好是你的任务量大,因为创建线程池的开销比创建一个线程大得多。

    创建线程池的方式
    ExecutorService 是一个比较重要的接口,实现这个接口的子类有两个 ThreadPoolExecutor (普通线程池)、ScheduleThreadPoolExecutor (定时任务的线程池)。你可以通过这两个类来创建一个线程池,但要传入各种参数,不太方便。为了方便用户,JDK中提供了工具类Executors,提供了几个创建常用的线程池的工厂方法。

    class MyThread implements Runnable{
    
        private String name;    
        public MyThread(String name) {
            this.name = name;
        }
        public void run() {
            System.out.println(name+"is running");
        }
    }
    
    public class test {
        public static void main(String[] args) {
            //创建一个只有一个线程的线程池
            ExecutorService executorService = Executors.newSingleThreadExecutor();
            //创建任务,并提交任务到线程池中
            executorService.execute(new MyThread("任务1"));
            executorService.execute(new MyThread("任务2"));
            executorService.execute(new MyThread("任务3"));
        }
    } 

    多线程应用场景
    一般线程之间比较独立,互不影响。
    一个线程发生问题,一般不影响其它线程。

  • 相关阅读:
    myeclipse部署maven项目到tomcat,src/main/resources里面配置文件部署不到webapp下classes
    MyEclipse自动生成Ant Build.xm
    MySQL This function has none of DETERMINISTIC, NO SQL...错误1418 的原因分析及解决方法
    解决openoffice进程异常退出的办法:
    【常见Web应用安全问题】---4、Directory traversal
    Errors running builder 'DeploymentBuilder' on project ' 解决方法
    linux CentOS 安装rz和sz命令 lrzsz
    (转)Maven的pom.xml文件结构之Build配置build
    spring整合xfire出现Document root element "beans", must match DOCTYPE root "null"错误解决方案
    linux解压zip、bz、bz2、z、gz、tar(解包)
  • 原文地址:https://www.cnblogs.com/shijianchuzhenzhi/p/12943772.html
Copyright © 2020-2023  润新知