For investors
股价:
5.36 美元 %For investors
股价:
5.36 美元 %认真做教育 专心促就业
1Java中线程的创建方法
方法1 (仅使用Thread类,重写run方法)
package chapter2;
//这个程序要运行必须在IDEA中装好lombok插件,并有lombok和slf4j-simple包
import lombok.extern.slf4j.Slf4j; //导入注解
@Slf4j(topic = "c.Test1")
/*
new 类名或接口名(){
重写方法;
}; //注意分号
//以上就是内部类的格式,其实这整体就相当于是new出来的一个对象
本质:其实是继承该类或者实现接口的子类匿名对象
个人理解:一般我们实现接口或者继承某个对象的时候会另外在定义一个新的有名称的类。匿名内部类由于只使用
一次,就没必要特意的再去重新定义一下。
*/
/*方法1:直接使用Thread*/
public class Test1 {
public static void main(String[] args){ //create the thread
Thread t = new Thread(){
public void run(){
log.warn("running");
}
};
t.setName("thread1");
t.start(); // start the thread
log.warn("running");
}
}
方法2(使用Thread配合Runable接口使用,推荐)
目的:将运行的代码与线程的创建分开
package chapter2;
//这个程序要运行必须在IDEA中装好lombok插件,并有lombok和slf4j-simple包
import lombok.extern.slf4j.Slf4j; //导入注解
//方法2: Runable对象配合Thread对象使用
@Slf4j(topic = "c.Test2")
public class Test2 {
public static void main(String[] args){ //create the thread
// Runnable r = new Runnable() { //定义匿名类实现Runable()接口
// @Override
// public void run() {
// log.warn("running");
// }
// };
// annoyomous class create(lambda style)
// 定义匿名类实现Runable接口,并使用lamba方式简化
Runnable r = () -> log.warn("running");
new Thread(r, "thread1").start();
log.warn("running");
}
}
2种方法小结
方法1与方法2的联系:方法2中的Runable对象会作为参数传递给Thread的私有变量target,然后在run方法中被调用。本质上2种方法都是对线程对象中的run()方法进行修改。
@Override
public void run() {
if (target != null) {
target.run();
}
}
使用方法2方便与高级API线程池进行配合
使用方法2更加灵活,符合JAVA组合优于继承的思想。
可以使用jps查看所有进程,jstack PID查看该进程所有的线程
方法3: FutureTask对象配合Thread对象使用(需要线程返回值)。
package chapter2;
//这个程序要运行必须在IDEA中装好lombok插件,并有lombok和slf4j-simple包
import lombok.extern.slf4j.Slf4j;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;
//方法2: Runable对象配合Thread对象使用
@Slf4j(topic = "c.Test2")
public class Test3 {
public static void main(String[] args) throws InterruptedException, ExecutionException { //create the thread
// FutureTask对象能够返回进程的执行结果
// 源码中FutureTask对象实现了runable接口,因此可以传递给Thread对象
FutureTask task = new FutureTask(new Callable() {
@Override
public Integer call() throws Exception {
log.warn("Runing");
Thread.sleep(1000);
return 100;
}
});
Thread t = new Thread(task,"t1");
t.start();
log.warn("{}", task.get());
}
}
2 Java线程内部原理
基本概念:
栈与栈帧:线程执行后,被分配一个栈内存。线程之间的栈内存互不干扰。
栈帧(frames):每个栈内存有许多栈帧组成,一个栈帧对应一次方法调用(利用栈的特性先进后出实现多次方法调用)
活动栈帧:每个线程当前只有一个活动栈帧,即当前线程执行的方法
Java中线程上下文切换(Thread Context Switch)
何时发生?
垃圾回收
时间片用完
更高优先级线程
线程主动sleep,yield,wait,join,park,synchronized,lock
3 线程中常用方法
thread中部分方法:
方法名
start()|run()创建线程并让线程处于就绪态(runable)|执行线程代码,不会创建线程
sleep()让线程由 running 变为 Time waiting(一定时间内无法被调度)
yield() 屈服,让步; 放弃; 让路;让线程由 running 变为 runable(仍然可被调度)
join()等待线程运行结束(线程同步,需要运行的结果)
interrupt()打断线程(包括阻塞的与正在运行的)
下面3个方法是过时方法容易破坏同步代码块,造成死锁。
stop()使用2阶段终止模式进行替代
suspend()
resume()
sleep():可以配合interrupt使用,interrupt可以wake up线程
yield():放弃CPU的使用权,依赖于CPU的调度器,会出现线程yield之后,任务调度器仍然调度该线程的情况。
interrupt():
中断线程的阻塞状态(采用sleep(),join(),wait()方法后的线程)
中断正常的线程,会使得线程中断标记为True
结合中断标记去优雅的停止线程
package chapter2;
//这个程序要运行必须在IDEA中装好lombok插件,并有lombok和slf4j-simple包
import lombok.extern.slf4j.Slf4j;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;
//方法2: Runable对象配合Thread对象使用
@Slf4j(topic = "c.Test2")
public class Test4 {
public static void main(String[] args) throws InterruptedException, ExecutionException { //create the thread
Thread t1 = new Thread(() -> {
while(true){
if(Thread.currentThread().isInterrupted()){ //当前线程是否被中断过
// System.out.println("break the loop");
log.warn("break the loop");
break;
}
}
},"t1");
t1.start();
Thread.sleep(1000); // main线程休眠1s
log.warn("interrupt t1");
t1.interrupt(); // 中断线程t1
}
}
运行结果:
[main] WARN c.Test2 - interrupt t1
[t1] WARN c.Test2 - break the loop
Process finished with exit code 0
interrput 与park结合使用
package chapter2;
//这个程序要运行必须在IDEA中装好lombok插件,并有lombok和slf4j-simple包
import lombok.extern.slf4j.Slf4j;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.locks.LockSupport;
//方法2: Runable对象配合Thread对象使用
@Slf4j(topic = "c.Test7")
public class Test7 {
public static void main(String[] args) throws InterruptedException{ //create the thread
log.warn("the main thread!");
parktest();
}
// park处于调度的考虑让当前线程不执行,等待获取许可证,除非被当前线程unpark方法调用。
private static void parktest() throws InterruptedException{
Runnable r = ()->{
// 定义匿名类实现Runable接口,由于Runable是函数式接口,因此可以通过lamba进行简化
log.warn("park...");
LockSupport.park();
log.warn("unpark...");
// interrupted() 方法会查看中断状态,将中断标记重置为假。
// isinterrupted() 方法仅查看中断状态
// park之后,用interrupt恢复后,必须恢复中断标记为假,否则再次park会失效。
log.debug("interrupt state: {}", Thread.interrupted());
};
Thread t1 = new Thread(r,"t1");
t1.start();
t1.sleep(1000);
t1.interrupt(); // 通过interrupt可以恢复park的进程,并将中断标志变为True
}
}
执行结果:
[main] WARN c.Test7 - the main thread!
[t1] WARN c.Test7 - park...
[t1] WARN c.Test7 - unpark...
注意点:
park方法能够让线程停止运行
park之后,可以用interrupt恢复运行,但必须手动恢复中断标记为假,否则再次park会失效。
区分2个方法interrupt以及isinterrupted
二阶段终止模式代码示例
package chapter2;
import lombok.extern.slf4j.Slf4j;
import java.util.concurrent.ExecutionException;
@Slf4j(topic = "c.Test6")
public class Test6 {
public static void main(String[] args) throws InterruptedException, ExecutionException {
TwoStageTermination tmp = new TwoStageTermination();
tmp.start();
Thread.sleep(5000);
tmp.stop();
}
}
/*
* 每隔2s运行并检查,可通过外部线程杀死该线程
* */
@Slf4j(topic = "c.TwoStageTermination")
class TwoStageTermination
{
private Thread monitor;
public void stop(){ /* 正常状态停止(中断)监控进程*/
monitor.interrupt(); /* 设置中断标记为true*/
}
public void start(){
monitor = new Thread(() -> {
while(true){
Thread current = Thread.currentThread();
if(current.isInterrupted()){
log.warn("stop the thread of monitor!");
break;
}
try{
current.sleep(2000);
log.warn("sleep 2 seconds !");
log.warn("check the status...");
}catch(Exception e){
e.printStackTrace();
current.interrupt(); /* 设置中断标记为true*/
}
}
},"monitor_thread");
monitor.start();
}
}
运行结果:
[monitor_thread] WARN c.TwoStageTermination - sleep 2 seconds !
[monitor_thread] WARN c.TwoStageTermination - check the status...
[monitor_thread] WARN c.TwoStageTermination - sleep 2 seconds !
[monitor_thread] WARN c.TwoStageTermination - check the status...
java.lang.InterruptedException: sleep interrupted
at java.lang.Thread.sleep(Native Method)
at chapter2.TwoStageTermination.lambda$start$0(Test6.java:36)
at java.lang.Thread.run(Thread.java:748)
[monitor_thread] WARN c.TwoStageTermination - stop the thread of monitor!
注意点:
线程在sleep状态被interrupt会触发异常,中断标记为false
线程运行状态被interrupt,中断标记为true。
二阶段终止模式可以用于过时的stop方法
守护线程(daemon)
知识点:
通常Java进程会在主线程以及其他线程运行结束后才会结束。
但对于守护线程而言,进程结束无需考虑守护线程是否已经停止。
守护进程的实际例子:
Java中的垃圾回收线程
Tomcat中的Acceptor以及Poller线程都是守护线程
4 线程的状态
从操作系统的角度
可以划分为:开始,可执行(就绪),执行,阻塞,结束
从Java程序的角度
在Java的源代码Thread.java文件中定义了6种状态,分别是
NEW:线程刚刚被创建时,还没有start()的状态
RUNABLE: Java中的RUNABLE包含了操作系统层面的运行,阻塞,可运行状态。
BLOCKED,WAITING,TIMED_WAITINGJava API层面的阻塞
TIMED_WAITING:使用sleep方法可能会出现
WAITING: 使用join方法后可能会出现
BLOCKED:使用synchronize方法可能会出现