JAVA基础:Thread深入学习

生活

不断规划与寻找自己的人生,想法把自己变重要;

Thread实例

线程是进程的最小执行单位,今天来看看JAVA中的Thread。

创建Thread,并且运行的方法大家都会了。咱们往深走。

public class MyThread  extends  Thread{

    @Override
    public void run() {
        System.out.println("这就是个傻逼线程");
    }

    public static void main(String[] args) {
        new MyThread().start();
    }
}

Thread生命周期

下面来看下线程的生命周期,非常重要:
在这里插入图片描述
新建:创建对象即进入新建状态
就绪:执行start()方法,即进入就绪状态。进入就绪状态并不是马上执行而是等待CPU调度,调度到就执行
执行:抢占到CPU时间片,执行run方法
阻塞:sleep wait阻塞【注意wait的阻塞会放弃CPU资源 sleep并不会放弃】
死亡:线程执行完毕或因异常退出

Thread关键成员

下面来了解一下Thread的关键成员:

   //是否守护线程
    private boolean     daemon = false;
    //jvm状态
   private boolean     stillborn = false;
   //线程执行目标
    private Runnable target;
    //线程组
   private ThreadGroup group;
   //线程类加载器
   private ClassLoader contextClassLoader;


//这两个着重在明天的ThreadLocal学习中研究
    ThreadLocal.ThreadLocalMap threadLocals = null;
    ThreadLocal.ThreadLocalMap inheritableThreadLocals = null;


//线程优先级
//最低
   public final static int MIN_PRIORITY = 1;
   //普通
   public final static int NORM_PRIORITY = 5;
//最高
    public final static int MAX_PRIORITY = 10;
//线程状态 0代表新建   
 private volatile int threadStatus = 0;

创建一个Thread

ok,下面来看下如何创建一个线程,核心方法在init,简单了解一下:

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

        this.name = name.toCharArray();
	//获取创建这个线程的当前线程{即这个线程的父线程}
        Thread parent = currentThread();
        SecurityManager security = System.getSecurityManager();
        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();
            }

            /* If the security doesn't have a strong opinion of the matter
               use the parent thread group. */
            if (g == null) {
                g = parent.getThreadGroup();
            }
        }

        /* checkAccess regardless of whether or not threadgroup is
           explicitly passed in. */
        g.checkAccess();

        /*
         * Do we have the required permissions?
         */
        if (security != null) {
            if (isCCLOverridden(getClass())) {
                security.checkPermission(SUBCLASS_IMPLEMENTATION_PERMISSION);
            }
        }

        g.addUnstarted();

	//创建线程在初始化的操作里,会直接把父线程的线程状态(用户线程还是守护线程)、优先级 都拷贝过来
        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();
        this.target = target;
        setPriority(priority);
        if (parent.inheritableThreadLocals != null)
        //这一块非常重要,在明天的ThreadLocal里再说
            this.inheritableThreadLocals =
                ThreadLocal.createInheritedMap(parent.inheritableThreadLocals);
        /* Stash the specified stack size in case the VM cares */
        this.stackSize = stackSize;

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

start

来看下 当你执行start的时候做了什么,

 public synchronized void start() {
         //当不上新建状态就抛异常
         //意味着一个Thread不能多次start
        if (threadStatus != 0)
            throw new IllegalThreadStateException();

        /* Notify the group that this thread is about to be started
         * so that it can be added to the group's list of threads
         * and the group's unstarted count can be decremented. */
        group.add(this);

        boolean started = false;
        try {
            start0();
            started = true;
        } finally {
            try {
                if (!started) {
                    group.threadStartFailed(this);
                }
            } catch (Throwable ignore) {
                /* do nothing. If start0 threw a Throwable then
                  it will be passed up the call stack */
            }
        }
    }

//实际调用到native方法,具体就是拿到cpu时间片,然后执行run方法,细节不去深入
    private native void start0();

守护线程

JAVA中的线程分为两个:用户线程和守护线程。
守护线程会在所有用户线程执行完毕才会执行,然后销毁,
注意:
1、通过thread对象的setDeamon设置为true来设置为守护线程

 public final void setDaemon(boolean on) {
        checkAccess();
        if (isAlive()) {
            throw new IllegalThreadStateException();
        }
        daemon = on;
    }

2、通过上面的源码可知,不能再线程执行过程中设置为守护线程,也就是说setDaemon必须在线程执行前执行
3、有init方法可知在一个守护线程里创建一个线程,那它也是守护线程。

sleep /wait

1、sleep是Thread里的方法,使线程休眠一段时间后醒来,在休眠期间不会释放已经获得的对象锁。
2、wait是Object里的方法,必须放在synchronized中使用,因为他会对对象的锁标记进行使用。一个对象执行了wait方法后就会释放他获得的所有锁,进入等待队列,直到执行notify或者notifyAll方法才会被唤醒参与锁的竞争。

线程礼让 yield

    public static native void yield();

由执行状态变成就绪状态,让出CPU给其他线程,随后又马上参与竞争。

线程加入 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");
        }

//在B线程 执行A线程.join,那么B线程必须等待A线程执行完毕才能执行,可以看到这个就是一个while判断,当B线程还在执行的时候,一直会阻塞,
        if (millis == 0) {
            while (isAlive()) {
                wait(0);
            }
        } else {
            while (isAlive()) {
                long delay = millis - now;
                if (delay <= 0) {
                    break;
                }
                wait(delay);
                now = System.currentTimeMillis() - base;
            }
        }
    }

线程中断

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

        synchronized (blockerLock) {
            Interruptible b = blocker;
            if (b != null) {
                interrupt0();           // Just to set the interrupt flag
                b.interrupt(this);
                return;
            }
        }
        interrupt0();
    }

线程的中断同样要调用native方法,在执行join,wait,sleep方法时执行了中断就会抛出异常。

可以通过下面两个方法获取到线程中断的状态,是否中断

//获取线程的中断状态并清除 也就是第二次查询的时候 不再是中断
 public static boolean interrupted() {
        return currentThread().isInterrupted(true);
    }
    //获取线程的中断状态
    public boolean isInterrupted() {
        return isInterrupted(false);
    }

过时的方法

Thread里有三个过时方法,最好不要使用:
1、stop():关闭线程,本质上是不安全的,因为他会解锁已经锁定的监视器。之前由这些监视器保护的对象中可能有不一致的情况,现在都可以被其他线程看到。

那么如何停止?
可以用中断来代替停止。

2、suspend resume
suspend挂起一个线程,resume恢复一个线程。
suspend挂起线程时不会释放锁,直到执行resume方法,被挂起的线程才能继续执行,从而其他阻塞在这个锁的线程才能得以执行。
当先执行了resume 在执行suspend时,就会导致死锁。也可称之为冻结。

那么如何挂起和恢复一个线程?
wait/notify

后记

明天来学习ThreadLocal,睡了 真累

猜你喜欢

转载自blog.csdn.net/qq_28605513/article/details/84868084
今日推荐