Thread和ThreadGroup分析

Thread和ThreadGroup分析

Thread类分析

用Thread的时候一直就直接传递一个runnable或者futuretask,还从没有注意到Thread类还有几个别的构造函数

1. 构造函数,一切的起源

public Thread(ThreadGroup group, Runnable target, String name,
                  long stackSize) {
        init(group, target, name, stackSize);
 }

init方法分析

init方法入参是所有构造方法调用的方法,在调用的时候accinheritThreadLocals(是否要继承ThreadLocal)都是Null。

假设当前要创建的线程是A,当前线程是B。

在这个方法里面会设置线程属性,会将B线程变为A线程的父类。如果A线程乜有ThreadGroup。就会添加到B线程的ThreadGroup里面,并且使ThreadGroup中的未启动的线程的数量++

为一些属性设置默认值。如果需要继承B线程的ThreadLocal,就会利用B线程中的ThreadLocal创建一个ThreadLocalMap,并且赋值给A线程的inheritableThreadLocals属性。

 private void init(ThreadGroup g, Runnable target, String name,
                      long stackSize, AccessControlContext acc,
                      boolean inheritThreadLocals) {
        if (name == null) {
            throw new NullPointerException("name cannot be null");
        }

        this.name = name;
       // 拿到当前的线程,作为要创建线程的父线程
        Thread parent = currentThread();
        SecurityManager security = System.getSecurityManager();
   
       // 如果没有指定ThreadGroup
        if (g == null) {
            /* Determine if it's an applet or not */

            /* If there is a security manager, ask the security manager
               what to do. */
            if (security != null) {
                g = security.getThreadGroup();
            }

            // 如果上面的security还是没有,就把这个线程和当前线程放在一个group中。
            if (g == null) {
                g = parent.getThreadGroup();
            }
        }

        // 
        g.checkAccess();

        /*
         * Do we have the required permissions?
         */
        if (security != null) {
            if (isCCLOverridden(getClass())) {
                security.checkPermission(SUBCLASS_IMPLEMENTATION_PERMISSION);
            }
        }
        // 给group中 未启动的线程树++
        g.addUnstarted();
        //一波赋值操作,用过线程的都知道,这几个属性都是可以通过set方法修改的
        this.group = g;
        this.daemon = parent.isDaemon();
        this.priority = parent.getPriority();
        if (security == null || isCCLOverridden(parent.getClass()))
            this.contextClassLoader = parent.getContextClassLoader();
        else
            this.contextClassLoader = parent.contextClassLoader;
        this.inheritedAccessControlContext =
                acc != null ? acc : AccessController.getContext();
   
       // 赋值target方法
        this.target = target;
        setPriority(priority);
       // 当前线程inheritThreadLocals不为null并且父线程inheritableThreadLocals不为null
       // 如果要继承ThreadLocal。就会给当前线程创建一个threadLocal。并且赋值给inheritThreadLocals。
        if (inheritThreadLocals && parent.inheritableThreadLocals != null)
            this.inheritableThreadLocals =
                ThreadLocal.createInheritedMap(parent.inheritableThreadLocals);
        /* Stash the specified stack size in case the VM cares */
        this.stackSize = stackSize;

        /* Set thread ID */
        tid = nextThreadID();
    }

2. 属性分析

  private volatile String name;  // 名字
    private int            priority; // 优先级
    private Thread         threadQ;   //
    private long           eetop;

    /* Whether or not to single_step this thread. */
   // 判断当前线程是否是single_step(单步执行)
    private boolean     single_step;

    /* Whether or not the thread is a daemon thread. */
 // 是否是守护线程,默认是false
    private boolean     daemon = false;

  // jvm的状态
    private boolean     stillborn = false;

  //需要运行的Runnable。
    private Runnable target;
 // 这个线程所在的线程组。
    private ThreadGroup group;

    /* The context ClassLoader for this thread */
    private ClassLoader contextClassLoader;

    /* The inherited AccessControlContext of this thread */
    private AccessControlContext inheritedAccessControlContext;

    /* For autonumbering anonymous threads. */
 // 给匿名的线程的自动编号
    private static int threadInitNumber;

  
   // 和这个线程有关的ThreadLocal的值,这个map保留着ThreadLocal
    ThreadLocal.ThreadLocalMap threadLocals = null;

    // 内部的threadLocal
    ThreadLocal.ThreadLocalMap inheritableThreadLocals = null;

     // 栈的大小,如果为0的话,JVM机会忽略。
    private long stackSize;

    /*
     * JVM-private state that persists after native thread termination.
     */
    private long nativeParkEventPointer;

    /*
     * Thread ID
     */
    private long tid;

    /* For generating thread ID */
    private static long threadSeqNumber;

    
   // 线程状态,初始化为0,表示没有运行
    private volatile int threadStatus = 0;


    
     // 提供给LockSupport的park方法用的
    volatile Object parkBlocker;

    /* The object in which this thread is blocked in an interruptible I/O
     * operation, if any.  The blocker's interrupt method should be invoked
     * after setting this thread's interrupt status.
     */
 // 此线程在可中断 I/O 操作中被阻塞的对象,如果设置,就会设置此线程的中断状态后应调用阻塞程序的中断方法
    private volatile Interruptible blocker;

    private final Object blockerLock = new Object();

     // 最低优先级
    public final static int MIN_PRIORITY = 1;

   // 默认的优先级
    public final static int NORM_PRIORITY = 5;

    // 最大
    public final static int MAX_PRIORITY = 10;
// 栈帧对象。一个StackTraceElement表示一个栈帧
private static final StackTraceElement[] EMPTY_STACK_TRACE
        = new StackTraceElement[0];
  // 权限检查
    private static final RuntimePermission SUBCLASS_IMPLEMENTATION_PERMISSION =
                    new RuntimePermission("enableContextClassLoaderOverride");
    private volatile UncaughtExceptionHandler uncaughtExceptionHandler;
   // 线程异常处理,这是JVM自己调用的
    private static volatile UncaughtExceptionHandler defaultUncaughtExceptionHandler;
 long threadLocalRandomSeed;
    @sun.misc.Contended("tlr")
    int threadLocalRandomProbe;

    /** Secondary seed isolated from public ThreadLocalRandom sequence */
    @sun.misc.Contended("tlr")
    int threadLocalRandomSecondarySeed;

3. start方法分析

在start方法里面,简单来说,就是启动线程,将当前线程添加到group中。

 public synchronized void start() {
       // 线程开始的状态必须是0
        if (threadStatus != 0)
            throw new IllegalThreadStateException();

       // 将当前线程添加到所在的组里面,与此同时,这个组里面未启动的线程数--,启动的数++
        group.add(this);
      //
        boolean started = false;
        try {
           // 这是真正的启动一个线程
           // 但是这个方法是native方法
            start0();
            started = true;
        } finally {
            try {
               //从group中移除这个线程
                if (!started) {
                    group.threadStartFailed(this);
                }
            } catch (Throwable ignore) {
                 // 这里异常什么都不要做,如果 start0 真的异常的话,直接从JVM层面就开始报错了。
               // 异常栈出来了。
            }
        }
    }

4. interrupt方法分析

中断当前线程,中断对于线程来说就是一个标志位置。

在等待状态的线程,中断的时候会清除中断标志,并且会抛出InterruptedException

如果此线程在 InterruptibleChannel 上的 I/O 操作中被阻塞,则channel将关闭,就会设置线程的中断状态,并且线程将收到 java.nio.channels.ClosedByInterruptException。

如果此线程在 java.nio.channels.Selector 中被阻塞,就会设置线程的中断状态将被设置,当前线程就会从select方法中返回,可能就没有结果而已。

对于blocking或者runnable的线程,中断也只是一个标志位置,那么,对于blocking状态的线程,他是不响应中断的。

public void interrupt() {
    if (this != Thread.currentThread())
        checkAccess();

    synchronized (blockerLock) {
       // 如果这个线程有一个可以中断的io操作,在设置完当前线程的中断标志之后,就会尝试去中断他
        Interruptible b = blocker;
        if (b != null) {
            interrupt0();           // Just to set the interrupt flag
            b.interrupt(this);
            return;
        }
    }
    interrupt0();
}

5. setPriority

优先级的必须是在10和1之间,设置的优先级不能大于线程所在组的最大优先级。

public final void setPriority(int newPriority) {
    ThreadGroup g;
    checkAccess();
    if (newPriority > MAX_PRIORITY || newPriority < MIN_PRIORITY) {
        throw new IllegalArgumentException();
    }
    if((g = getThreadGroup()) != null) {
        if (newPriority > g.getMaxPriority()) {
            newPriority = g.getMaxPriority();
        }
        setPriority0(priority = newPriority);
    }
}

6. setName分析

看这个方法,好像能在线程启动之后还能修改线程的名字。好像很有意思。比如像下面的一样

public final synchronized void setName(String name) {
    checkAccess();
    if (name == null) {
        throw new NullPointerException("name cannot be null");
    }

    this.name = name;
    if (threadStatus != 0) {
        setNativeName(name);
    }
}

动态修改线程名字

一开始是t,运行之后修改为b

在这里插入图片描述

7. 线程运行时候的几个状态

 public enum State {
         // 新创建但是还没有7运行
        NEW,

       // 这个状态,表示线程可能在,也可能不(就绪,等待时间片的调度)在运行。
        RUNNABLE,

        // 获取锁失败
        BLOCKED,

        // 等待状态
        WAITING,

        // 带超时时间的等待
        TIMED_WAITING,

        // 结束,表示线程执行完成
        TERMINATED;
    }

8. 对于 UncaughtExceptionHandler属性的说明,(重点)

这属性,肯定有对应的set方法, 它是Thread类中的一个内部类

这个接口是提供给JVM调用的,会处理线程没有处理的异常。在线程发送未捕获的异常马上要终止的时候,JVM会调用getUncaughtExceptionHandler方法查询对应的方法, 将异常线程和异常作为参数传递,如果没有设置,就会将ThreadGroup作为

UncaughtExceptionHandler,本身,ThreadGroup就是实现了UncaughtExceptionHandler接口,如果ThreadGroup也没有,就给默认的处理。

    @FunctionalInterface  
public interface UncaughtExceptionHandler {
    
        void uncaughtException(Thread t, Throwable e);
    }

那么看看ThreadGroup中是怎么写的.

 public void uncaughtException(Thread t, Throwable e) {
        if (parent != null) {
            parent.uncaughtException(t, e);
        } else {
            Thread.UncaughtExceptionHandler ueh =
                Thread.getDefaultUncaughtExceptionHandler();
            if (ueh != null) {
                ueh.uncaughtException(t, e);
            } else if (!(e instanceof ThreadDeath)) {
                System.err.print("Exception in thread \""
                                 + t.getName() + "\" ");
                e.printStackTrace(System.err);
            }
        }
    }

看这个一下就懂了,线程异常了之后,的提示文本是从哪里来的

在这里插入图片描述

9. Join方法分析

public final synchronized void join(long millis)
throws InterruptedException {
    long base = System.currentTimeMillis();
    long now = 0;

    if (millis < 0) {
        throw new IllegalArgumentException("timeout value is negative");
    }
   // 为0表示一直等待
    if (millis == 0) {
        while (isAlive()) {
            wait(0);
        }
    } else {
       //避免重复唤醒。
        while (isAlive()) {
            long delay = millis - now;
            if (delay <= 0) {
                break;
            }
           // 一直等待,
            wait(delay);
            now = System.currentTimeMillis() - base;
        }
    }
}

要注意到,join方法的锁,表示当前线程要在join的thread上面等待,那就是说,A,B两个线程,在B线程里面执行A.join,会让B线程等待在A线程的Thread对象所关联的锁上面。

在这个线程终止的时候,就会调用this.notifyAll方法,唤醒之前等待的B线程,B就可以继续执行了。

线程类就分析到这里了,分析了一些方法,主要是UncaughtExceptionHandler和可以动态的设置线程的名字让我很惊艳,还有ThreadGroup的相关内容。下面就分析分析ThreadGroup。

ThreadGroup分析

见名知意。这是用来管理一组线程的。将线程放在一个组里面,便于管理。

一个线程组代表一组线程。 一个线程组还可以包含其他线程组。 除了初始线程组之外的每个线程都有一个父线程组,这种关系可以组成一个树。一个节点下面可以有多个子节点,但是只能有一个父节点。一个线程可以访问关于它自己的线程组的信息,但不能访问关于它的线程组的父线程组或任何其他线程组的信息。

下面就详细的看看他的属性和方法

1. 属性分析

 //线程组 
private final ThreadGroup parent;

    String name;
    int maxPriority;
    boolean destroyed;
     boolean daemon;
     boolean vmAllowSuspension;
    // 没有启动的线程
    int nUnstartedThreads = 0;
  // 启动的线程
    int nthreads;
  // 当前线程组包含的线程
    Thread threads[];
  // 
    int ngroups;
 // 当前组下面的组
    ThreadGroup groups[];

2. 构造方法

还是简单的设置一些属性而已

    private ThreadGroup(Void unused, ThreadGroup parent, String name) {
        this.name = name;
        this.maxPriority = parent.maxPriority;
        this.daemon = parent.daemon;
        this.vmAllowSuspension = parent.vmAllowSuspension;
        this.parent = parent;
        parent.add(this);
    }

在构造Thread的时候会将ThreadGroup传递进去。在start方法的时候会将线程添加进来

3. add方法分析

可以看到,一开始的时候就创建了长度为4的线程,并且扩容就是*2,给下标赋值,nUnstartedThreads–(nUnstartedThreads表示当前没启动的线程数)

当然这里还有对应的remove方法,这里就不写了,就是将线程移出数组。

 void add(Thread t) {
        synchronized (this) {
            if (destroyed) {
                throw new IllegalThreadStateException();
            }
            if (threads == null) {
                threads = new Thread[4];
            } else if (nthreads == threads.length) {
                threads = Arrays.copyOf(threads, nthreads * 2);
            }
            threads[nthreads] = t;

            // This is done last so it doesn't matter in case the
            // thread is killed
            nthreads++;

            // The thread is now a fully fledged member of the group, even
            // though it may, or may not, have been started yet. It will prevent
            // the group from being destroyed so the unstarted Threads count is
            // decremented.
            nUnstartedThreads--;
        }
    }

4. activeCount分析

这只是一个快照的方式,所以,就不需要持有锁。

会统计当前组下面所有的组中总共的线程数。

 public int activeCount() {
        int result;
        
        int ngroupsSnapshot;
        ThreadGroup[] groupsSnapshot;
        synchronized (this) {
           // 如果当前的group已经销毁掉了,直接返回0
            if (destroyed) {
                return 0;
            }
           // 当前运行的线程数量
            result = nthreads;
          // 当前线程所拥有的group数量
            ngroupsSnapshot = ngroups;
            if (groups != null) {
                groupsSnapshot = Arrays.copyOf(groups, ngroupsSnapshot);
            } else {
                groupsSnapshot = null;
            }
        }
        for (int i = 0 ; i < ngroupsSnapshot ; i++) {
           // 统计当前节点下面所有组的线程数
            result += groupsSnapshot[i].activeCount();
        }
        return result;
    }

5. interrupt分析

简单的说,就会遍历整个下面所有的线程,然后调用他的中断方法,既然是ThreadGroup了,肯定里面存放的线程都是一类的。

 public final void interrupt() {
        int ngroupsSnapshot;
        ThreadGroup[] groupsSnapshot;
        synchronized (this) {
            checkAccess();
            for (int i = 0 ; i < nthreads ; i++) {
                threads[i].interrupt();
            }
            ngroupsSnapshot = ngroups;
            if (groups != null) {
                groupsSnapshot = Arrays.copyOf(groups, ngroupsSnapshot);
            } else {
                groupsSnapshot = null;
            }
        }
        for (int i = 0 ; i < ngroupsSnapshot ; i++) {
            groupsSnapshot[i].interrupt();
        }
    }

关于Thread和ThreadGroup分析就分析到这里了。 如有不正确的地方,欢迎指出。谢谢。

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值