ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

Thread 类详解

2022-05-09 23:01:44  阅读:133  来源: 互联网

标签:Thread void t1 start 详解 线程 public


初始化过程

public Thread() {
    // 4个参数
    // 第1个,ThreadGroup 线程组
    // 第2个,Runnable
    // 第3个,name,默认情况下,如果你不指定线程的名称,
    //             那么自动给你生成的线程名称就是,Thread-0,Thread-1......
    // 第4个,stackSize
    init(null, null, "Thread-" + nextThreadNum(), 0);
}

进入 init 方法,会发现如下代码块

// 当你创建线程的时候,获取到的是 currentThread(),是当前线程创建你的那个线程
// 比如说,你创建一个线程的时候,默认它的父线程就是创建它的那个线程,
//        比如说 main 线程创建一个 ServiceAliveMonitor 线程,此时 ServiceAliveMonitor 线程的父线程就是 main 线程
Thread parent = currentThread();

// 下面这段代码的意思是,ThreadGroup 是不指定的,那么就会自动给你处理一下,给你分配一个线程组,
// 每个线程组必须属于一个 ThreadGroup的,如果你没有指定线程组,那么默认的线程组就是父线程的线程组
// 如果说,你的父线程是 main 线程的话,那么你的线程组就是 main 线程的线程组(main 线程组)
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();
    }
      
}
......
// 默认情况下,如果你没有指定你是否为 daemon 的话,那么你的 daemon 的状态是有父线程决定的
// 就是说,如果你的父线程是 daemon 线程,那么你也是 daemon 线程
// daemon 守护线程
this.daemon = parent.isDaemon();
// 同理,你的优先级如果没有指定的话,那么就跟父线程的优先级保持一致
// 若不设置优先级,默认为5
this.priority = parent.getPriority();

其实每个线程都有一个线程 id,threadId,第一个分配的线程,它的 id 是 1,之后的线程是2,3,4...以此类推,先关代码块如下

public Thread() {
    init(null, null, "Thread-" + nextThreadNum(), 0);
}

private static int threadInitNumber;

private static synchronized int nextThreadNum() {
    return threadInitNumber++;
}

初始化总结

  • 创建你的线程,就是你的父线程
  • 如果你没有指定 ThreadGroup,你的 ThreadGroup 就是父线程的 ThreadGroup
  • 你的 daemon 状态默认是父线程的 daemon 状态
  • 你的优先级默认是父线程的优先级
  • 如果你没有指定线程的名称,那么默认就是 Thread-0 的格式名称
  • 你的线程 id 全局递增,从 1 开始的

start()

永远都不能对一个线程多次调用和执行 start() 方法

// It is never legal to start a thread more than once. 
// In particular, a thread may not be restarted once it has completed execution.
// 这句话的意思是,永远都不能对一个线程多次调用和执行 start() 方法,这个是不对的
public synchronized void start() {......}

进入 start() 方法,你会看到如下代码块

// 如果你的线程一旦执行过一次以后,那么它的 threadStatus就会变成非 0 的一个状态
// 如果 threadStatus 是非 0 的状态,说明它之前已经被执行过了
// 这里会有一个判断,如果你对一个线程多次执行 start() 方法,会抛出非法非线程状态异常IllegalThreadStateException
if (threadStatus != 0)
            throw new IllegalThreadStateException();

// group 就是之前给分配的,如果你自己指定了那么就是你自己创建的那个 ThreadGroup
// public Thread(ThreadGroup group, String name){}
// 上面是 Thread 的一个构造方法,可以传 ThreadGroup
// 否则就是你的父线程的 threadGroup,
// 这行代码就是将当前线程加入了它属于的那个线程组
group.add(this);

// 下面这段主要是调用 start0() 这个方法
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 */
    }
}
// 会结合底层的一些代码和机制,实际的启动一个线程
// 一旦 start0() 成功启动之后,就会去执行我们覆盖掉的那个 run() 方法
//	@Override
//	public void run() {
// 		if (target != null) {
//			target.run();
// 		}
//	}
// 或者是如果你传入进去的是那个 Runnable 对象,人家就会执行那个 Runnable 对象的方法
private native void start0();

需要注意的点

  • 一旦启动了线程之后,就不能再重新启动了,不能多次调用 start() 方法,因为启动之后, threadStatus 就会变成非 0 状态了,此时就不能重新调用了
  • 你启动线程之后,这个线程机会加入之前处理好的那个线程组中
  • 启动一个线程实际上走的是 native 方法,start0()会实际的启动一个线程
  • 一个线程启动之后就会执行 run() 方法

sleep()

这个方法主要是指定要等待多少毫秒

在 jdk1.5 之后引入了 TimeUtil 这个类,可以使用TimeUnit.HOURS.sleep(n)指定睡眠几个小时,使用TimeUnit.SECONDS.sleep(n)指定睡眠几秒钟

但是在平时使用或者开源项目中,在线程 sleep 这块,还是用的最最原始的 sleep,因为可以通过毫秒树,动态的传入一个外卖配置的一个值,比如300毫秒就传300,30秒就传 30 * 1000,1分钟就传 1 * 60 * 1000

如果用 TimeUtil 的话,在外面怎么配?你要配置休眠的 5 分钟的话,还得加上 1 个单位,代码里要判断一下你休眠的时间单位,如果是分钟,那么还得用 TimeUtil.MINIUTE 来进行休眠,不太方便

yield()

// 官方对该方法有如下注释
/**
* A hint to the scheduler that the current thread is willing to yield
* its current use of a processor. The scheduler is free to ignore this
* hint.
*
* <p> Yield is a heuristic attempt to improve relative progression
* between threads that would otherwise over-utilise a CPU. Its use
* should be combined with detailed profiling and benchmarking to
* ensure that it actually has the desired effect.
*
* <p> It is rarely appropriate to use this method. It may be useful
* for debugging or testing purposes, where it may help to reproduce
* bugs due to race conditions. It may also be useful when designing
* concurrency control constructs such as the ones in the
* {@link java.util.concurrent.locks} package.
*/
// 意思是说,该方法只是对线程调度器的一个建议,而且也只是建议具有相同优先级的其他线程可以运行
// 
// yield,使当前线程正在执行的时候停止下来进入等待队列
// 回到等待队列里在系统的调度算法里还是依然有可能把你这个线程拿回去来继续执行
// 当然更大的可能性是把原来等待的那些线程拿出来一个来执行
// 因此,yield的意思是我让出一下 CPU,后面你能不能抢到那我不管

public static native void yield();

join()

main 线程里面若开启了一个其他线程,这个时候只要你一旦开启了其他线程之后,那么 main 线程就会跟其他线程开始并发的运行,一会儿执行 mian 线程,一会儿执行其他线程的代码

若有两个线程 t1 线程和 t2 线程,在 t1 线程调用了 join 方法,那么就会导致 t1 线程阻塞住,他会等待其他线程(t2线程)的代码逻辑执行结束,然后 t1 线程才会继续执行

public final synchronized void join(long millis)
  • 需求:
  • 若有三个线程 t1,t2,t3
  • 要保证 t1 线程先执行完毕,再执行 t2 线程,最后执行 t3 线程
// 在 t2 线程调用 t1.join()
// 在 t3 线程调用 t2.join()
public class Thread_Join {
    public static void main(String[] args) {
        testJoin();
    }

    private static void testJoin() {
        Thread t1 = new Thread(()->{
            for (int i = 0; i < 10; i++) {
                System.out.println("t1-" + i);
            }
        });

        Thread t2 = new Thread(()->{
            try {
                t1.join();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            for (int i = 0; i < 10; i++) {
                System.out.println("t2-" + i);
            }
        });

        Thread t3 = new Thread(()->{
            try {
                t2.join();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            for (int i = 0; i < 10; i++) {
                System.out.println("t3-" + i);
            }
        });
        t1.start();t2.start();t3.start();
    }
}

interrupt()

通过interrupt()方法可以中断线程。如果你是 while循环,可以判断如果没有被中断,那么就正常工作,如果别人中断了这个线程,那么 while 循环的条件判断里,就会发现说,被中断了。被中断以后,你的 while 循环被发现了,就会退出循环,这个线程就终止了

public class InterruptDemo {
    public static void main(String[] args) throws Exception{
        MyThread thread = new MyThread();
        thread.start();
        Thread.sleep(1000);
        thread.setShouldRun(false);
        thread.interrupt();
    }

    private static class MyThread extends Thread {
        private Boolean shouldRun = true;
        @Override
        public void run() {
            while(shouldRun) {
                try {
                    System.out.println("线程1在执行工作......");
                    Thread.sleep(30 * 1000);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }

        public void setShouldRun(Boolean shouldRun) {
            this.shouldRun = shouldRun;
        }
    }
}

标签:Thread,void,t1,start,详解,线程,public
来源: https://www.cnblogs.com/xiaoyangabc/p/16251443.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有