多线程是并行计算实现的方式, 但是在单cpu中实际上没有真正的并行,只不过是多个任务通过cpu的快速轮转,产生多任务同一时间运行的错觉.而其中的任务就是进程. (当然多核CPU,并行还是真实存在的). 一个进程中至少有一个线程,线程运行在进程中,但是cpu的调度的是进程中的线程,所以一个线程可以占据多个cpu核.
每一个线程有自己的局部变量,程序计数器,生命周期.
eg. 操作系统创建一个JVM进程,所有的java线程都是在jvm进程中,但是线程是最小的调度单位,CPU调度的是进程中的线程.一个线程可以占据多个核. 另外,Java可以进行多进程编程,启动一个新的子进程,就相当于一个新的虚拟机.
但是python中由于GIL锁导致一个线程,只能运行在一个核上也就相当于串行化多进程.
new 创建一个Thread是只是创建了一个线程的实例,依然是java的对象, 不处于执行状态,为new状态.
线程通过start启动并不会立即执行,这个时期属于runnable状态, runable状态必须听令于CPU的调度,才会进入running状态. 同时runnable状态不会直接进入blocked和terminated状态,runnable只会意外终止和running状态.
cpu轮询选中线程进入running状态, stop进入 terminated状态,调用sleep,或wait线程进入waitSet中,IO操作,锁,进入blocked状态. cpu调度器放弃线程回到runnable,线程主动调用yield,放弃cpu执行权,进入runnable
blocked 可以直接stop进入terminated状态 , JVM Crash 阻塞结束,完成休眠,唤醒,拿到锁资源进入runnable
Thread被构造为New状态,事实上threadStatus 内部属性为0
不能两次启动Thread,否则就会出现IllegalThreadStateException异常
线程被启动后加入到ThreadGroup中,线程状态受到ThreadGroup影响
一个线程的生命周期结束,再调用start方法是不允许的,Terminated无法回到runnable/running状态.
thread.start()
TimeUnit.SECONDS.sleep(2)
thread.start()
Thread中的run方法就是空的实现,run和start采用模板的设计方法,run本身就是空的方法,留实现类实现逻辑,start会调用线程的启动,运行,run,以及关闭的操作.
首先,实现线程的执行单元有两种方式,
无论Runnable和Thread都是实现Runnable的接口
Thread 的 run 方法是不能共享的,A 线程不能把B的run当自己的资源,实现资源共享使用static
Runnable 只要使用同一个Runnable,构造不同的Thread实例,就可以资源共享.
任何一个线程都是由另一个线程创建的,main线程由JVM创建的,程序里面的父线程都是main线程.
main 线程 所在的 ThreadGroup 称为 main
构造一个线程没有指定 ThreadGroup 会和父进程同属一个ThreadGroup
创建 Thread时可以设置StackSize ,stackSize 越大代表线程的递归深度越深,stackSize越小创建线程的数量越多
堆内存是被所有线程共享的内存区域.
元空间,是堆内存的一部分,JVM为每一个类加载器分配一块内存列表,进行线性分配,块的大小跟类加载器的种类相关,类加载器具备回收条件,之前会单独回收类加载器空间,现在直接把相对应的元空间回收,减少内存碎片.
进程的内存大小 = 堆内存 + 线程数量 + 栈内存
如果JVM进程中没有一个非守护线程,那么JVM就会退出,守护线程可以自动结束生命周期,守护线程主要是为了进行后天工作.
守护线程必须在启动前设置才有效
yield 会提醒调度器放弃当前的CPU资源,如果cpu资源不紧张则会忽略这种提醒 yield 让线程从running 切换到 runnable
yield 只是一个提示, cpu调度器并不会担保每次满足yield提示.
优先级也是hint操作,一般优先级高的可以优先获得cpu,但是闲时优先级高低不会有任何作用. 如果优先级大于组的优先级,那么指定的优先级则会失效,则是组的最大优先级.
main 的优先级是5,它派生出来的程序的优先级都是5
ClassLoader getContextClassLoader() 获取线程的上下文类加载器,线程由哪一个类加载的,默认与父线程保持一致.
ClassLoader setContextClassLoader() 打破java类加载器的委托机制.
一个线程被 wait,sleep,join 则线程会进入阻塞状态 如果另外一个线程调用当前线程的interrupted 则可以打断阻塞 抛出InterruptedException异常
一个线程内部存在interrupt flag 标识,一个线程被interrupt,则flag被设置,如果正在执行可中断方法,调用interrupt,那么flag会被清除.
必须如果run 中用 wait,sleep,join 则在执行完interrupt后,状态任然为 false,没有可中断,则中断标识为true.
Thread 的 join同样是可中断方法, 如果其他线程执行该线程interrupt方法,同样会捕捉到中断信号,并将标识位擦除.
join某个线程A,会使当前的线程B处于等待,直到A生命周期结束,期间B处于blocked状态. join在start后执行
线程Stop后不会释放掉monitor锁.
线程最好的退出方式捕获中断异常进行退出, t.interrupt(); 如果在线程中执行某个可中断方法,则可以通过捕获中断信号来决定是否退出. 即在catch中决定是否退出
由于线程的interrupt标识可能被擦除,使用volatile修饰开关flag关闭线程
private volatile boolean closed = false;
while (!closed && !isTnterrupted()){
}