多线程基础
线程运行原理
栈帧图解





栈与栈帧
- JVM是由堆、栈、方法区所组成,栈的内存是给线程用的。
- 每个线程启动后,虚拟机就会为其分配一块栈内存
- 每个栈由多个栈帧(方法)组成,对应着每个方法调用时所占用的内存。
- 每个线程只能有一个活动栈帧。对应着当前正在执行的那个方法
栈帧图解

多线程
栈帧是以线程为基本单位,每个线程都有自己的栈内存,有自己的多个栈帧
线程上下文切换
因为某些原因cpu不再执行当前的线程,转而执行其他线程。
- 线程的CPU时间片用完。
- 垃圾回收(暂停当前所有的线程)
- 更高优先级的线程。
- 线程自己调用了sleep ,yield ,wait , join, park,synchronized lock
当线程上下文切换时,要保证当前线程的状态。并恢复另一个线程的状态,JAVA中对应的概念就是程序计数器,作用是记录了下一条JVM指令的执行地址,是线程私有的。
- 线程的状态包括程序计数器,虚拟机栈中每个栈帧的信息。
- 线程的上下文切换频繁会影响性能
线程的常见方法
| 方法名 | static | 功能说明 | 注意 |
| start() | 启动一个新的线程 | 只能调用一次,多次会出现异常 | |
| run() | 新的线程启动后会调用这个方法 | 如果实现了Runnable参数。则线程启动后会调用Runnable的run方法 | |
| join() | 等待线程运行结束 | ||
| join(long n) | 等待线程运行结束,最多等n秒 | ||
| getId() | 获取线程长整型的id | id唯一 | |
| getName() | 获取线程名 | ||
| setName(String) | 修改线程名 | ||
| getPriority() | 获取线程优先级 | ||
| setPriority(int) | 设置线程优先级 | java中规定优先级1-10级,较大的优先级能提高调度几率 | |
| getState() | 获取线程状态 | Java中线程状态是用6个,分别是NEW... | |
| isInterrupted() | 判断是否被打断 | 不会清除标记(打断标记) | |
| interrupt() | 打断线程 | 如果打断线程正在sleep,waitjoin会导致被打断的线程抛出InterruptedException,并清除打断标记。如果打断正在运行的线程,则会设置打断标记。park的线程被打断也会被设置打断标记 | |
| interrupted() | static | 判断当前线程是否被打断 | 会清除标记(打断标记) |
| currentThread() | static | ||
| isAlive() | 线程是否存活 | ||
| sleep(long n) | static | 让当前执行的线程,休眠n毫秒,休眠时间让出CPU时间片 | |
| yield() | static | 提示让出CPU |
interrupted()
1.打断阻塞
打断sleep,wait,join的线程,会设置标记为false
打断正在运行时的线程,就会标记为true
public static void main(String[] args) throws InterruptedException {Thread thread=new Thread(new Runnable() {@Overridepublic void run() {f1();}});Thread thread1=new Thread(new Runnable() {@Overridepublic void run() {f2();}});thread.start();thread.interrupt();System.out.println("线程的状态"+thread.isInterrupted());thread.join();
}
public static void f1(){int a=1;System.out.println(a);}
2.打断正常运行
public class StackTest {public static void main(String[] args) throws InterruptedException {Thread thread=new Thread(new Runnable() {@Overridepublic void run() {while (true){}}});thread.start();thread.interrupt();System.out.println("线程的状态"+thread.isInterrupted());thread.join();}public static void f1(){int a=1;System.out.println(a);}}
注意看,虽然打断成功了,但是线程并没有停止。这是因为这个打断只是告诉这个线程我要打断你了。具体停不停还得是看那个线程的自己的实现。

完整版
public class StackTest {public static void main(String[] args) throws InterruptedException {Thread thread=new Thread(new Runnable() {@Overridepublic void run() {while (true){Thread thread1=Thread.currentThread();if(thread1.isInterrupted()){System.out.println("线程被打断了");break;}}}});thread.start();thread.interrupt();System.out.println("线程的状态"+thread.isInterrupted());thread.join();}public static void f1(){int a=1;System.out.println(a);}

两阶段中止模式
在一个线程T1中如何优雅的中止线程T2?优雅就是给T2一个料理后事的机会
错误的思路
使用线程对象的stop()方法停止线程
stop()方法会真正的杀死线程。如果这时线程锁住了共享资源,那么被它杀死后就再也没有机会释放锁,其他线程将永远无法获取锁。
使用System.exit(int)方法停止线程。
目的:仅仅要停止一个线程,你这全停了。

具体实现
package algorithm;import lombok.extern.slf4j.Slf4j;public class Test3 {public static void main(String[] args) throws InterruptedException {TowPhaseTermination tpt=new TowPhaseTermination();tpt.start();Thread.sleep(10000);tpt.stop();}}class TowPhaseTermination{private Thread monitor;public void start(){monitor=new Thread(()->{while (true){Thread current= Thread.currentThread();if(current.isInterrupted()){System.out.println("料理后事...");break;}try {Thread.sleep(2000);System.out.println(current.getName()+"执行监控记录");} catch (InterruptedException e) {e.printStackTrace();//重新设置打断标记current.interrupt();}}});monitor.start();}public void stop(){monitor.interrupt();;}}