Java程序创建启动的时候,默认就有一个线程,也就是主线程,在运行了。
//创建线程对象
Thread t = new Thread() {
@Override
public void run() {
//要执行的任务
}
};
//设置线程名称 默认是Tread-x x表示数字从0递增
t.setName("t1");
//启动线程
t.start();
把【线程】和【任务】(要执行的代码)分开
Runnable runnable = new Runnable() {
@Override
public void run() {
// 要执行的任务
}
};
//创建线程对象 可以使用两个参数的构造,第二个参数表示设置线程名称
Thread t = new Thread( runnable );
//启动线程
t.start();
Java 8 以后可以使用功能lambda精简代码
有且仅有一个抽象方法的接口,会在接口上方添加注解,这种接口可以使用lambda 精简代码
lambda语法可以跳转这篇
此处为语雀内容卡片,点击链接查看:https://www.yuque.com/liziing/qm71mg/hqlcf3pgsey339bi
FutureTask 能够接收 Callable 类型的参数, 用来处理有返回结果的情况
FutureTask task = new FutureTask<>(new Callable() {
@Override
public Integer call() throws Exception {
// 执行的任务
//返回的值
return xxx;
}
});
Thread t = new Thread(task, "t3");
t.start();
//主线程阻塞, 同步等待 task 执行完毕的结果
task.get();
JVM由堆、栈、方法区所组成,栈内存是给线程用的,每个线程启动,虚拟机就会为其分配一块栈内存。
使用cpu -> 不使用cpu 称为一次线程上下文切换
因为以下一些原因导致 cpu 不在执行当前的线程, 转而执行 另一个线程的代码
当Context Switch 发生时,需要有操作系统保存当前的状态,并恢复另一个线程的状态,Java中对应的概念是程序计数器(Program Counter Register),它的作用是记住下一条jvm指令的执行地址,是线程私有的。
线程需要通过start方法的方式来自己调用run(),才能够起到异步执行的效果
如果直接通过手动来调用run(),这样是通过主线程来调用的,所以不能够起到异步的效果
public static void main(String[] args) {
Thread t1 = new Thread("t1");
@Override
public void run() {
log.debug(Thread.currentThread().getName() ...);
FileReader.read(Constants.MP4_FULL_PATH);
}
};
t1.run();
log.debud("do other things ...);
sleep实现
在没有利用cpu来计算时,不要让 while(true) 空转浪费cpu,这时可以使用yield 或 sleep 来让出cpu的使用权给其他程序
场景:
static int r = 0;
public static void main(String[] args) throws InterruptedException {
test1();
}
private static void test1() throws InterruptedException {
Thread t1 = new Thread(() -> {
sleep(1);
r = 10;
});
t1.start();
// t1.join(); // 解决方法
log.debug("结果为:{}", r);
}
因为线程1等待了1秒,所以主线程已经输出了 r 的结果,结果为0
解决:
可以让主线程 sleep 1秒来得到 r = 10 的结果,但是这种方式需要我们知道sleep具体的等待时间,所以不好控制
可以让 要等待哪个线程结束就让哪个线程调用 join 方法,这样就会等待 线程执行结束之后才会往下执行
join (毫秒数) 可以添加参数,指的是具体等待多少毫秒之后结束,如果线程还没有执行完,也会直接结束
但是如果等待超过了线程的执行时间,那么具体会以 线程执行时间为准,不会真的等待那么长时间,会提前结束
调用下面方法,如果判断是true,然后会将true置为false,这是两者区别
park() 不是 Thread中的方法,是LockSupport中的方法,线程中调用park方法之后,会让线程停在park位置,
当其他线程调用了park所在的线程的interrupt方法,也就是打断之后,那么就会终止park,继续执行park所在线程中的代码。
但是一旦打断之后,打断标记为true,在park线程中第二次调用park就失效了,因为已经是打断标记是true了,所以这时候可以用到上面的interrupted方法,直接置为false。
才能使得第二次调用有效停止。
默认情况下,Java进程需要等待所有线程都运行结束,才会结束。
有一种特殊的线程叫做守护线程,调用线程的setDaemon(true)
只要其他非守护线程运行结束了,即使守护线程的代码还没有执行完,也会强制结束。
注意: