1:继承Thread类创建线程
现象:创建线程的第一种方式,继承Thread,重写run方法,如果直接调用run方法,则和调用普通方法一样,不会创建线程
结果显示是 main线程调用
如果调用start,是 Thread-0 调用
调用run方法,很简单,MyThread类继承Thread,重写了run,所以调用run实际调用的是子类MyThread类的run,
我们来看一下start方法
大致意思是:
start这个方法触发这个线程开始执行,jvm会调用这个线程的run方法。
导致这两个线程并发执行:当前线程调用完start就就结束了,另一个线程要执行run方法。
启动这个线程超过1次是不合法的,尤其是线程已经执行完成不能再次被重启。
我们通过注释可以看出只有调用start方法才会创建线程,而且是准备就绪,没有执行,具体执行需要
cpu分配时间片给该线程,这个线程只能被启动一次,start方法上面有个synchronized锁,应该就是控制多次启动的。
多次启动,会报非法状态
来看一下start方法:
首先是判断线程状态,0为New新建,不是则抛异常,如果是则放入线程组,然后调用
start0方法启动,创建该线程,创建过程设置开关,如果创建失败则从线程组删除
这个start0方法是jvm提供的方法,应该就是为线程开辟内存,分配资源等工作,具体怎么cpu怎么调到run方法的
还不知道,后面研究后 在补充 ?????????
来看一下run方法
如果这个thread构建的时候以一个runnable对象作为参数,那么启动的时候,将会调用,否则不会被调用,
当然Thread的子类应该重写这个方法
当继承Thread时,已经重写run方法,所以jvm底层调用run时,其实直接调用子类的方法,而不是该方法。
2:实现runnable的方式创建线程
来看一下构造Thread这个方法
new Thread(runnable);
到最后把入参的target赋值给了内部维护的target变量,
所以当调用start后,创建一个线程就绪状态,等待分配时间片。
再来看一下Thread类的run方法:
因为没有被重写,所以会调用Thread类的run方法,target就是入参传进来的MyRunnable对象,所以
会调用重写的run方法。
3: 线程池方式创建线程
先来看一下创建线程池对象:
ExecutorService executors = Executors.newSingleThreadExecutor();
将创建的线程池对象委托给了
FinalizableDelegatedExecutorService 对象,
在这个
DelegatedExecutorService类里面,将入参委托给了ExecutorService来维护。
在回过头来看线程池的execute方法:
然后调用到ThreadPoolExecutor方法
由于线程池里面的参数比较多,我们就假设线程池工作的线程数小于核心数:
先重点关注一下线程池的执行逻辑,具体线程池的细节可以在线程池一节在重点分析:
看一下new Worker(firstTask) 方法:
将runnable赋值给worker类中的变量,然后使用线程工厂创建一个线程,同时将当前worker对象作为参数传进了线程中。
因为Worker类也实现了Runnable接口
由于我们创建线程时使用的是默认线程工厂:
所以看一下newThread的逻辑
将worker作为入参传了进去,然后维护在Thread类中的target变量上。
当把worker对象放到集合workers中,添加成功后,会调用线程的start方法。
调用start方法,当cpu分配时间片给Thread时,就触发了Worker对象的run方法
调用runWorker方法,就会调用MyRunnable方法的run方法,
就会调用我们自己实现的MyRunnable方法中。