目录
[方法三 :FutureTask配合Thread](#方法三 :FutureTask配合Thread)
创建线程
方法一:直接使用Thread
@Slf4j
public class Test {
public static void main(String[] args) {
Thread t = new Thread(){
@Override
public void run() {
log.debug("aaa");
}
};
t.start();
}
}
方法二:使用Runnable配合Thread
@Slf4j
public class Test {
public static void main(String[] args) {
Runnable runnable = () -> log.debug("aaa"); // 用来lamba表达式优化
Thread t = new Thread(runnable);
t.start();
}
}
方法三 :FutureTask配合Thread
@Slf4j
public class Test {
public static void main(String[] args) throws ExecutionException, InterruptedException {
FutureTask<Integer> task = new FutureTask<>(new Callable<Integer>() {
@Override
public Integer call() throws Exception {
log.debug("running");
Thread.sleep(1000);
return 100;
}
});
Thread t1 = new Thread(task,"t1");
t1.start();
// 等待task返回的结果
log.debug("{}",task.get());
}
}
线程交替运行
@Slf4j
public class Test {
public static void main(String[] args){
new Thread(() -> {
while(true){
log.debug("running");
}
},"t1").start();
new Thread(() -> {
while(true){
log.debug("running");
}
},"t2").start();
}
}
线程运行原理
每次开启一个线程都会产生一个线程的栈给线程使用,实际上就是一开始分配的虚拟机栈。
-
每个栈都有多个栈帧
-
线程只能有一个活动栈帧
线程执行的过程
先分配栈给线程,线程调用main方法,在栈分配一个栈帧给main方法,栈帧保存锁记录、局部变量表、操作数栈、返回地址(返回到原来的栈帧方法的下一条指令)
线程上下文切换
导致上下文切换的条件
-
时间片用完
-
优先级
-
垃圾回收
-
自己调用sleep,wait等
线程的状态包括
-
程序计数器,记录执行到什么位置
-
虚拟机栈,包括所有的栈帧信息
常用方法
start
线程进入就绪状态,等待调度器调用。这里相当于是开启了一个新的线程
run
执行Runnable里面的方法。这里并没有开启线程,只是通过本线程执行代码
如果开启了两次start就会出现线程状态异常的问题IllegalThreadStateException
线程的两个状态
-
NEW
-
start之后就是Runnable等待被调度
sleep
线程睡眠,并且把状态改为Timed Waiting。被打断的时候会抛出异常InterruptedException。可以通过TimeUnit.SECONDS.sleep来规定睡眠时间的单位。睡眠可以使用在while循环自转的地方,如果长时间自转就会消耗CPU的使用时间,其它线程无法使用
yield
其实就是把线程状态从Running转变到Runnable暂时让出cpu(谦让),重新去竞争,具体实现看任务调度器,不一定礼让成功
interrupt
唤醒线程,如果线程处于睡眠状态那么就会抛出异常
@Slf4j
public class Test {
public static void main(String[] args) throws InterruptedException {
Thread t = new Thread("t1"){
@Override
public void run() {
log.debug("enter sleep ...");
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
log.debug("wake up ....");
e.printStackTrace();
}
}
};
t.start();
Thread.sleep(1000);
t.interrupt();
}
}
@Slf4j
public class Test {
public static void main(String[] args) throws InterruptedException {
Thread t = new Thread(() -> {
log.debug("sleep。。。。");
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
},"t");
t.start();
Thread.sleep(1000);
log.debug("interupt");
t.interrupt();
log.debug("打断标记 : {}",t.isInterrupted());
}
}
打断正常运行的线程
import lombok.extern.slf4j.Slf4j;
@Slf4j
public class Test {
public static void main(String[] args) throws InterruptedException {
Thread t1 = new Thread(() ->{
while(true){
boolean interrupted = Thread.currentThread().isInterrupted(); // 打断标记
if(interrupted){
log.debug("被打断了, 退出循环");
break;
}
}
},"t1");
t1.start();
Thread.sleep(1000);
log.debug("interupt");
t1.interrupt();
}
}
setPriority
设置优先级是给调度器进行提示,先执行这个线程,但是仍然没有办法控制线程。
join
join实际上就是卡点,就是一定要等待调用join的线程完成之后才能够执行下面的代码
@Slf4j
public class Test {
static int r = 10;
public static void main(String[] args) throws InterruptedException {
test1();
}
public static void test1() throws InterruptedException {
log.debug("开始");
Thread t = new Thread(() -> {
log.debug("开始");
try {
Thread.sleep(1);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
log.debug("结束");
r = 10;
},"t");
t.start();
t.join(); // 主线程等t线程结束
log.debug("结果是 : {}",r); // 打印10
}
}