zoukankan      html  css  js  c++  java
  • JAVA线程基础

    一、线程状态

    由于参考的维度不一样,线程状态划分也不一样,我这里简单的分为5大类,并且会说明状态变迁的详细过程:

    1、新建(new):新创建了一个线程,但是并未执行start方法。

    2、就绪(runnable):执行start方法后,该线程位于可运行的线程池中,等待被CPU选中执行。

    3、运行(running):线程池中可运行的线程被CPU选中执行。

    4、阻塞(BLOCKED):线程因为某种原因放弃了CPU的使用权,暂时停止运行。

    5、死亡(dead):线程run()、main()方法结束。

    下面我们来看下就绪、运行、阻塞这三种状态之间的变迁过程:

    1、running---->runnable

    线程所占有的时间片结束或者调用了Thread.yield()方法。

    2、running---->blocked

    进入阻塞状态的原因分为三种:

    a、等待阻塞:运行的线程执行wait方法,JVM会把该线程放入等待队列,这个时候线程释放了原本占有的锁。

    b、同步阻塞:运行的线程在竞争对象的同步锁时,若该同步锁被别的线程占用,JVM会把该线程放入锁池中。

    c、其他阻塞:运行的线程执行sleep、join方法或者发出了I/O请求,JVM会把线程设置为阻塞状态。这种过程的线程不会释放版本占有的锁。

    3、blocked--->runnable

    a、等待阻塞:被其他线程用notify、notifyAll方法唤醒,唤醒之后该线程进入锁池,进行对象同步锁的竞争。

    b、同步阻塞:竞争到对象的同步锁后进入到可运行状态。

    c、其他阻塞:sleep状态超时、join等待线程终止或者超时、或者I/O处理完毕,线程重新进入可运行状态。

    二、守护线程与非守护线程

    JAVA线程分为两类:守护线程(Daemon Thread)和用户线程(User Thread)。任何线程都可以是守护线程或者用户线程,唯一的区别就是虚拟机在退出时判断不一样。

    虚拟机在所有非守护线程结束后自动离开,只要还有一个用户线程,虚拟机就不会提供运行。守护线程是用来服务用户线程的,如果没有用户线程在运行,守护线程也会结束。

    守护线程最典型的使用场景就是GC(垃圾回收器)

    public class DaemonTest
    {
        public static void main(String[] args)
        {
            Thread thread = new Thread(new Runnable()
            {
    
                @Override
                public void run()
                {   
                    try
                    {
                        Thread.sleep(1000);
                    }
                    catch (InterruptedException e)
                    {
                        e.printStackTrace();
                    }
                    finally
                    {
                        System.out.println("DaemonThread finally run");
                    }
                }});
         //设置为守护线程 thread.setDaemon(
    true); thread.start();
         System.out.println("main Thread"); } }

     执行结果:

    main Thread

    这里可以看到,将线程thread设置为守护线程的时,这里只执行了用户线程,而没有执行被我们人工设置为守护线程的thread。这是由于用户线程先执行结束之后,没有其他的用户线程,JVM就退出了,守护线程也随之结束。

    如果将一个线程设置为守护线程的时候,需要注意不要将执行关闭和清理资源等动作放在finally代码块里执行,因为在上述这种场景,finally块中的代码并没有如我们认为的那样一定会执行。

    三、等待/通知机制

    等待/通知的相关方法是任意java对象都具备的,因为这些方法被定义在所有对象的超类java.lang.Object上,方法和描述如下:

    等待/通知已经被提炼出来一个经典范式,分别针对等待方(消费者)和通知方(生产者),原则如下:

    1、等待方

    a、获取对象锁

    b、如果条件不满足,那么调用对象的wait方法,被通知后仍要检查条件。

    c、条件满足则执行对应的逻辑

    2、通知方

    a、获得对象的锁

    b、改变条件

    c、通知所有等待在对象上的线程。

    import java.text.SimpleDateFormat;
    import java.util.Date;
    
    public class WaitNotifyTest
    {
        static Object lock = new Object();
        
        static boolean flag = false;
        
        public static void main(String[] args)
        {
            Thread waitThread = new Thread(new Wait(), "WaitThread");
            waitThread.start();
            Thread notifyThread = new Thread(new Notify(), "notifyThread");
            notifyThread.start();
        }
        
        static class Wait implements Runnable
        {
            
            @Override
            public void run()
            {
                synchronized (lock)
                {
                    while (!flag)
                    {
                        System.out.println(Thread.currentThread()
                            + "flag is false,wait@"
                            + new SimpleDateFormat("HH:mm:ss").format(new Date()));
                        try
                        {
                            lock.wait();
                        }
                        catch (InterruptedException e)
                        {
                            e.printStackTrace();
                        }
                    }
                    // 满足条件时,完成工作
                    System.out.println(Thread.currentThread()
                        + "flag is true,running@"
                        + new SimpleDateFormat("HH:mm:ss").format(new Date()));
                }
            }
        }
        
        static class Notify implements Runnable
        {
            @Override
            public void run()
            {
                synchronized (lock)
                {
                    System.out.println(Thread.currentThread() + "hold lock,notify@"
                        + new SimpleDateFormat("HH:mm:ss").format(new Date()));
                    lock.notify();
                    flag = true;
                }
                synchronized (lock)
                {
                    System.out.println(Thread.currentThread()
                        + "hold lock again,sleep@"
                        + new SimpleDateFormat("HH:mm:ss").format(new Date()));
                }
            }
        }
    }

     执行结果:

    Thread[WaitThread,5,main]flag is false,wait@09:55:09
    Thread[notifyThread,5,main]hold lock,notify@09:55:09
    Thread[notifyThread,5,main]hold lock again,sleep@09:55:09
    Thread[WaitThread,5,main]flag is true,running@09:55:09

    线程waitThread 首先获取对象的锁,判断条件不满足之后,调用了对象的wait方法,从而放弃了对象锁进入了对象的等待队列waitQueue中,变成了等待状态。由于waitThread 释放的锁随即被线程notifyThread 所占有,线程notifyThread 在处理逻辑的同时调用了对象的notify方法,将waitThread从waitQueue转移到了SynchronizedQueue中,此时waitThread变成了阻塞状态。NotifyThread执行完毕释放锁之后,WaitThread获取对象锁之后从wait方法返回继续执行。

    这里需要重点关注两个地方:

    1、线程notifyThread调用兑现的notify方法后,waitThread线程并不是立即就从wait方法返回了,它必须要等线程notifyThread执行完毕释放锁之后,才能竞争上岗。

    2、线程waitThread从wait方法返回后,理论上从wait方法后面的代码开始执行,但是如果有判断条件,则必须重新进行判断。

    四、Thread.join()的使用

    public class JoinTest
    {
        public static void main(String[] args) throws Exception
        {
            Thread thread = new Thread(new Runnable()
            {
    
                @Override
                public void run()
                {
                    for(int i=0; i<5; i++)
                    {
                        System.out.println(i);
                    }
                    
                }});
            thread.start();
            thread.join();
            System.out.println("hello world");
            
        }
    }

     执行结果:

    0
    1
    2
    3
    4
    hello world

    Thread.join的含义是等待该线程终止。

    程序中如果不调用线程thread的join方法,打印的结果应该是hello world在最前面。在调用thread.join方法后,main线程要等待thread执行结束才能继续。

    join方法的源码片段:

    while (isAlive()) {
            wait(0);
            }

    这个就是运用等待/通知的经典范式来实现的,首先判断条件不满足,调用线程的wait方法。当线程终止时,会调用线程自身的notifyAll方法,通知所有等待在该线程对象上的线程。

  • 相关阅读:
    better-scroll 介绍
    promise 异步编程
    vue网址路由的实时检测
    浏览器本地存储的使用
    获取元素的位置
    如何设置动画的运动效果
    实现对称加密及非对称公钥加密
    Centos 7系统启动修复
    Centos 7服务启动文件
    内核编译-4.12
  • 原文地址:https://www.cnblogs.com/dongguacai/p/5976201.html
Copyright © 2011-2022 走看看