每个方法对应一个栈帧,栈内存会在方法一结束就释放掉栈帧
JVM中由堆, 栈, 方法区所组成 , 栈中的内存给线程使用.
每个栈由多个栈帧组成, 对应着每次方法调用时所占的内存.
每个线程只能有一个活动栈帧, 对应着当前正在执行的那个方法.
导致线程上下文切换的原因
线程的CPU时间片用完
垃圾回收
有更高优先级的线程需要运行
线程自己调用了sleep, yield , wait , join , park , synchronized, lock方法
发生上下文切换时, 当前线程的程序计数器会存放正在执行的虚拟机字节码指令的地址 ,同时局部变量, 操作数栈, 返回地址等等都会保存在栈中, 以便于切换时恢复使用.
上下文切换的成本很高, 需要恢复其他线程栈中的所有方法栈和局部变量, 同时需要保存当前线程栈中的所有方法栈和局部变量.
启动一个线程, 在线程内运行run方法中的代码
start方法只是让线程进入就绪状态, 里面的代码执不执行需要看CPU的时间片有没有分给他.
每个线程的start方法只能调用一次, 如果调用了多次就会出现异常.
等待一个线程结束, 或者指定一个超时时间,最多等待n毫秒
比如线程一需要线程二的结果, 需要在线程一当写上 t2.join()
此时t2就会一直抢占cpu资源, 直到线程结束
这样就能让线程一只能等待线程二结束,拿到结果之后才能运行
setPriority(int)
修改线程的优先级 , Java中线程优先级是 1 - 10
,较大的优先级能提高线程被CPU调度的机率 , 只是提高几率, 具体谁先执行还是看操作系统.
getPriority()
获取当前线程的优先级
6 种状态
NEW
: 新建状态 , 线程刚被创建, start之前的方法.RUNNABLE
: 当线程已被占用, 在虚拟机中正常的执行, 就处于此状态.BLOCKED
: 当一个线程试图获取一个对象锁时, 而该对象锁被其他的线程持有, 此时就进入了BLOCKED状态 , 线程获取到锁时, 就变成 了RUNNABLE状态WAITING
: 就是休眠状态, 一个线程等待另一个线程执行一个动作时,一般是wait(n), sleep(n), 该线程就进入了waiting状态, 这个状态是不能被唤醒的, 必须等待另一个线程调用notify()或者notifyAll方法才能唤醒TIME_WAITING
: 休眠一定时间的状态, 到了指定时间和受到唤醒通知(notify)时就会结束.TERMINATED
: 从RUNNABLE状态正常退出而死亡, 或者因为没有捕获的异常而终止了RUNNABLE状态而死亡. public static void main(String[] args) throws ExecutionException, InterruptedException {
Thread thread=new Thread(()->{
System.out.println("线程开始执行");
},"线程1");
System.out.println(thread.getState()); //NEW
thread.start();
System.out.println(thread.getState()); //RUNNABLE
thread.join(); //此时线程一直占用直到结束
System.out.println(thread.getState()); //TERMINATED
}
interrupted()
, 打断线程, 如果被打断的线程正在sleep , wait , join会导致被打断的线程抛出InterruptedExecption, 并且清除打断标记(打断标记为false). 如果打断正在运行的线程, 则会设置打断标记(打断标记设置为true).isInterrputed()
, 判断是否被打断, 不会清除标记.获取当前正在执行的线程
sleep方法只能写在线程的内部, 就是调用Thread.sleep(n) , 写在什么线程内部就让他休眠
让当前执行的线程休眠n毫秒, 休眠时间让出cpu的时间片给其他线程
让步, Thread.yield()方法作用是:暂停当前正在执行的线程对象(及放弃当前拥有的cup资源),并执行其他线程。
但是不一定保证他能起到让步的效果 , 因为CPU可能再次调度.
stop
, suspend
(暂停线程) resume
(恢复线程运行) 这三种都会造成线程死锁
yield会让当前线程从Running进入Runnable状态, 然后调度执行其他同优先级的线程. 如果这时没有同优先级的线程, 那么就不能保证让当前线程暂停的效果
public static void main(String[] args) throws ExecutionException, InterruptedException {
Thread thread=new Thread(()->{
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
System.out.println("线程内方法线程开始执行");
},"线程1");
System.out.println(thread.getState()); //NEW
thread.start();
System.out.println(thread.getState()); //RUNNABLE
TimeUnit.SECONDS.sleep(1); //主线程先休眠一秒再执行打印thread的线程状态
System.out.println(thread.getState()); //TIMED_WAITING
thread.join();
System.out.println(thread.getState()); //TERMINATED
}
线程的优先级为1-10
, 默认优先级为5
线程优先级会提示调度器优先调度该线程, 但它仅仅是一个提示, 调度器可以忽略它
如果cpu比较忙, 那么优先级高的线程会获得更多的时间片, 但是cpu闲的时候, 优先级就没了作用
interrupt
public class ThreadCreate {
public static void main(String[] args) throws InterruptedException {
Thread thread=new Thread(()->{
System.out.println("开始执行"); //线程一开始, 打断标记为false
System.out.println(Thread.interrupted()); //获取当前标记为false, 不做改变
System.out.println("继续执行,打断标记为: "+Thread.currentThread().isInterrupted()); //仍未false
LockSupport.park(); // 发现打断标记为false , 暂停线程 ,由主线程打断
System.out.println("继续执行,打断标记为: "+Thread.currentThread().isInterrupted()); //被打断, 标记未true
System.out.println(Thread.interrupted()); //发现当前标记未true,返回, 清除并设置未false
System.out.println("继续执行,打断标记为: "+Thread.currentThread().isInterrupted()); //此时未false
LockSupport.park(); // 发现打断标记为false , 暂停线程 ,由主线程打断
System.out.println("没有park就执行"); //线程被暂停, 一直等待打断
});
thread.start();
TimeUnit.SECONDS.sleep(2);
thread.interrupt();
}
}
默认情况下, java进程只有当所有进程都结束之后, 才会结束 .
守护进程就是只要其他非守护进程运行结束了, 即时守护线程的代码没有执行完, 也会强制结束.
垃圾回收线程就是一个守护线程, 如果程序停止, 垃圾回收器就会强制结束
Tomcat的acceptor和Poller线程也都是守护线程 , 当Tomcat受到shutdown时, 就会强制结束
public class ThreadCreate {
public static void main(String[] args) throws InterruptedException {
Thread thread=new Thread(()->{
while (true){
if (Thread.currentThread().isInterrupted()){
break;
}
}
System.out.println("进程执行"); //守护进程的代码没有执行完也会随着非守护进程的结束而结束
});
thread.setDaemon(true); //设置为守护线程, 当其他线程执行完了, 就会强制结束
thread.start();
System.out.println(Thread.currentThread().getName()+"结束");
}
}
**调用alive方法, 判断线程是否存活, 如果存活, 则调用不限制等待Wait()的方**法 ,进入锁的waitset,如果线程结束, 则唤醒.
park对应WAITING状态
wait , notify 和 notifyAll 必须配合 Object Monitor一起使用, 而park, unpark不需要
park 和 unpark是以线程为单位来阻塞和唤醒线程, 而notify只能唤醒一个等待线程, 而notifyAll可以唤醒所以等待线程, 就不那么精确
park 和 unpar可以先unpark, 而wait 和 notify就不能先notify.
原理
每个线程都有自己的一个parker对象, 由三部分组成 _counter
, _cond
和_mutex
.
调用park方法时
调用unpark方法时
先调用unpark, 再调用park方法时