// 构造方法的参数是给线程指定名字,,推荐给线程起个名字(用setName()也可以)
Thread t1 = new Thread("t1") {
@Override
// run 方法内实现了要执行的任务
public void run() {
log.debug("hello");
}
};
t1.start();
把【线程】和【任务】(要执行的代码)分开,Thread 代表线程,Runnable 可运行的任务(线程要执行的代码)Test2.java
// 创建任务对象
Runnable task2 = new Runnable() {
@Override
public void run() {
log.debug("hello");
}
};
// 参数1 是任务对象; 参数2 是线程名字,推荐给线程起个名字
Thread t2 = new Thread(task2, "t2");
t2.start();
1.8后用lambda表达式来简化写法
// 创建任务对象
Runnable task2 = () -> log.debug("hello");
// 参数1 是任务对象; 参数2 是线程名字,推荐
Thread t2 = neaw Thread(task2, "t2");
t2.start();
也可以更简略一点
// 参数1 是任务对象; 参数2 是线程名字,推荐
Thread t2 = neaw Thread(() -> log.debug("hello"), "t2");
t2.start();
idea里 lamba表达式简化快捷键: alt +enter
方法1 是把线程和任务合并在了一起,方法2 是把线程和任务分开了,用 Runnable 更容易与线程池等高级 API 配合,用 Runnable 让任务类脱离了 Thread 继承体系,更灵活。通过查看源码可以发现,方法二其实到底还是通过方法一执行的!
需要注意的是:
FutureTask 能够接收 Callable 类型的参数,用来处理有返回结果的情况 Test3.java
public static void main(String[] args) throws ExecutionException, InterruptedException {
// 实现多线程的第三种方法可以返回数据也可以抛出异常,返回的数据需要用get接收
FutureTask futureTask = new FutureTask<>(new Callable<Integer>() {
@Override
public Integer call() throws Exception {
log.debug("多线程任务");
Thread.sleep(100);
return 100;
}
});
new Thread(futureTask,"我的名字").start();
log.debug("主线程");
//{}表示占位,实际值是后买你的参数,获取结果。必要时可以通过get方法获取执行结果,该方法会阻塞直到任务返回结果。
log.debug("{}",futureTask.get());
}
Future就是对于具体的Runnable或者Callable任务的执行结果进行取消、查询是否完成。
public interface Future<V> {
boolean cancel(boolean mayInterruptIfRunning);
boolean isCancelled();
boolean isDone();
V get() throws InterruptedException, ExecutionException;
V get(long timeout, TimeUnit unit)
throws InterruptedException, ExecutionException, TimeoutException;
}
Future提供了三种功能:
3.3 查看进程线程的方法
windows
任务管理器可以查看进程和线程数,也可以用来杀死进程
tasklist 查看进程
taskkill 杀死进程
tasklist | findstr java 查看所有的java线程
linux
ps -fe 查看所有进程
ps -fT -p 查看某个进程(PID)的所有线程
kill 杀死进程
top 按大写 H 切换是否显示线程
top -H -p 查看某个进程(PID)的所有线程
ps -fe | grep java 查看所有的java进程
Java
jps 命令查看所有 Java 进程
jstack 查看某个 Java 进程(PID)的所有线程状态
jconsole 来查看某个 Java 进程中线程的运行情况(图形界面)
jconsole 远程监控配置
需要以如下方式运行你的 java 类
java -Djava.rmi.server.hostname=`ip地址` -Dcom.sun.management.jmxremote -
Dcom.sun.management.jmxremote.port=`连接端口` -Dcom.sun.management.jmxremote.ssl=是否安全连接 -
Dcom.sun.management.jmxremote.authenticate=是否认证 java类
修改 /etc/hosts 文件将 127.0.0.1 映射至主机名
如果要认证访问,还需要做如下步骤
复制 jmxremote.password 文件
修改 jmxremote.password 和 jmxremote.access 文件的权限为 600 即文件所有者可读写
连接时填入 controlRole(用户名),R&D(密码)
例子:
拟机栈描述的是Java方法执行的内存模型:每个方法被执行的时候都会同时创建一个栈帧(stack frame)用于存储局部变量表、操作数栈、动态链接、方法出口等信息,是属于线程的私有的。当java中使用多线程时,每个线程都会维护它自己的栈帧!每个线程只能有一个活动栈帧,对应着当前正在执行的那个方法
因为以下一些原因导致 cpu 不再执行当前的线程,转而执行另一个线程的代码
sleep
、yield
、wait
、join
、park
、synchronized
、lock
等方法当 Context Switch 发生时,需要由操作系统保存当前线程的状态,并恢复另一个线程的状态,Java 中对应的概念 就是程序计数器(Program Counter Register),它的作用是记住下一条 jvm 指令的执行地址,是线程私有的.
public static void main(String[] args) {
Thread thread = new Thread(){
@Override
public void run(){
log.debug("我是一个新建的线程正在运行中");
FileReader.read(fileName);
}
};
thread.setName("新建线程");
thread.start();
log.debug("主线程");
}
输出:程序在 t1 线程运行, run()
方法里面内容的调用是异步的 Test4.java
11:59:40.711 [main] DEBUG com.concurrent.test.Test4 - 主线程
11:59:40.711 [新建线程] DEBUG com.concurrent.test.Test4 - 我是一个新建的线程正在运行中
11:59:40.732 [新建线程] DEBUG com.concurrent.test.FileReader - read [test] start ...
11:59:40.735 [新建线程] DEBUG com.concurrent.test.FileReader - read [test] end ... cost: 3 ms
将上面代码的thread.start();
改为 thread.run();
输出结果如下:程序仍在 main 线程运行, run()
方法里面内容的调用还是同步的
12:03:46.711 [main] DEBUG com.concurrent.test.Test4 - 我是一个新建的线程正在运行中
12:03:46.727 [main] DEBUG com.concurrent.test.FileReader - read [test] start ...
12:03:46.729 [main] DEBUG com.concurrent.test.FileReader - read [test] end ... cost: 2 ms
12:03:46.730 [main] DEBUG com.concurrent.test.Test4 - 主线程
直接调用 run()
是在主线程中执行了 run()
,没有启动新的线程使用的是main线程, 使用 start()
是启动新的线程,通过新的线程间接执行 run()
方法 中的代码
InterruptedException
异常【注意:这里打断的是正在休眠的线程,而不是其它状态的线程】sleep()
代替 Thread 的 sleep()
来获得更好的可读性小知识:TimeUint的sleep()
Running
进入 Runnable 就绪状态
,然后调度执行其它线程优先级
会提示(hint)调度器优先调度该线程
,但它仅仅是一个提示,调度器可以忽略它, 如果 cpu 比较忙,那么优先级高的线程会获得更多的时间片,但 cpu 闲时,优先级几乎没作用thread1.setPriority(Thread.MAX_PRIORITY); //设置为优先级最高
主线程
中调用t1.join
,则主线程
会等待t1线程执行完之后
再继续执行
private static void test1() throws InterruptedException {
log.debug("开始");
Thread t1 = new Thread(() -> {
log.debug("开始");
sleep(1);
log.debug("结束");
r = 10;
},"t1");
t1.start();
// t1.join();
// 这里如果不加t1.join(), 此时主线程不会等待t1线程给r赋值, 主线程直接就输出r=0结束了
// 如果加上t1.join(), 此时主线程会等待到t1线程执行完才会继续执行.(同步), 此时r=10;
log.debug("结果为:{}", r);
log.debug("结束");
}
图示如下:
思考:等待多个结果时的运行时间’
static int r1 = 0;
static int r2 = 0;
public static void main(String[] args) throws InterruptedException {
test2();
}
private static void test2() throws InterruptedException {
//线程t1
Thread t1 = new Thread(() -> {
sleep(1);
r1 = 10;
});
//线程t2
Thread t2 = new Thread(() -> {
sleep(2);
r2 = 20;
});
long start = System.currentTimeMillis();
t1.start();
t2.start();
t1.join();
t2.join();
long end = System.currentTimeMillis();
log.debug("r1: {} r2: {} cost: {}", r1, r2, end - start);
}
因为线程t1等待时间示1ms,线t2等待时间为2ms,线程t1运行结束后还要等待1ms,线程t2才结束。总共运行2ms,输出。
如图:
而调换之后:
static int r1 = 0;
static int r2 = 0;
public static void main(String[] args) throws InterruptedException {
test2();
}
private static void test2() throws InterruptedException {
//线程t1
Thread t1 = new Thread(() -> {
sleep(1);
r1 = 10;
});
//线程t2
Thread t2 = new Thread(() -> {
sleep(2);
r2 = 20;
});
long start = System.currentTimeMillis();
t1.start();
t2.start();
t2.join();
t1.join();
long end = System.currentTimeMillis();
log.debug("r1: {} r2: {} cost: {}", r1, r2, end - start);
}
线程t2join结束后,能立马输出,总时间还是两秒。
join()函数里面的参数
同时,join()里还可以写入参数,
当参数小于实际运行时间,主线程等待参数时间,再开始行动
static int r1 = 0;
static int r2 = 0;
public static void main(String[] args) throws InterruptedException {
test3();
}
public static void test3() throws InterruptedException {
Thread t1 = new Thread(() -> {
sleep(2);
r1 = 10;
});
long start = System.currentTimeMillis();
t1.start();
// join结束,等待结束
t1.join(1500);
long end = System.currentTimeMillis();
log.debug("r1: {} r2: {} cost: {}", r1, r2, end - start);
}
当参数大于实际运行时间,主线程会依照实际运行时间。
static int r1 = 0;
static int r2 = 0;
public static void main(String[] args) throws InterruptedException {
test3();
}
public static void test3() throws InterruptedException {
Thread t1 = new Thread(() -> {
sleep(2);
r1 = 10;
});
long start = System.currentTimeMillis();
t1.start();
//运行结束,等待结束
t1.join(3000);
long end = System.currentTimeMillis();
log.debug("r1: {} r2: {} cost: {}", r1, r2, end - start);
}
interrupt是特有的打断方法,本身并不会终止线程运行,常常和打断标记配合使用,使得线程终止更加“优雅”。
true
false
打断正在sleep的线程
private static void test1() throws InterruptedException {
Thread t1 = new Thread(()->{
sleep(1);
}, "t1");
t1.start();
sleep(0.5);
t1.interrupt();
log.debug(" 打断状态: {}", t1.isInterrupted());
}
输出
java.lang.InterruptedException: sleep interrupted
at java.lang.Thread.sleep(Native Method)
at java.lang.Thread.sleep(Thread.java:340)
at java.util.concurrent.TimeUnit.sleep(TimeUnit.java:386)
at cn.itcast.n2.util.Sleeper.sleep(Sleeper.java:8)
at cn.itcast.n4.TestInterrupt.lambda$test1$3(TestInterrupt.java:59)
at java.lang.Thread.run(Thread.java:745)
21:18:10.374 [main] c.TestInterrupt - 打断状态: false
打断正在运行的线程
private static void test2() throws InterruptedException {
Thread t2 = new Thread(()->{
while(true) {
Thread current = Thread.currentThread();
boolean interrupted = current.isInterrupted();
if(interrupted) {
log.debug(" 打断状态: {}", interrupted);
break;
}
}
}, "t2");
t2.start();
sleep(0.5);
t2.interrupt();
}
20:57:37.964 [t2] c.TestInterrupt - 打断状态: true
注意:isInterrupted和interrupted的区别
前者将打断标记变为true之后不将它自动变为false 而后者自动将它变为false
两阶段终止模式就源于堆打断方法和打断标记的利用
大致操作如下图:
简单来说就是:用一个监控线程监控所有线程,运行过程中时刻检测当前线程的打断标志是否为true
,当打断标志为true
时,停止线程。
而我们可以通过此来停止该线程。
当前线程正常运行时被打断可以得到上面我们说的结果,而睡眠中被打断,需要我们手动将标志改为true
.
实际代码如下:
public class Test7 {
public static void main(String[] args) throws InterruptedException {
Monitor monitor = new Monitor();
monitor.start();
Thread.sleep(3500);
monitor.stop();
}
}
class Monitor {
Thread monitor;
/**
* 启动监控器线程
*/
public void start() {
//设置线控器线程,用于监控线程状态
monitor = new Thread() {
@Override
public void run() {
//开始不停的监控
while (true) {
//判断当前线程是否被打断了
if(Thread.currentThread().isInterrupted()) {
System.out.println("处理后续任务");
//终止线程执行
break;
}
System.out.println("监控器运行中...");
try {
//线程休眠
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
//如果是在休眠的时候被打断,不会将打断标记设置为true,这时要重新设置打断标记
Thread.currentThread().interrupt();
}
}
}
};
monitor.start();
}
/**
* 用于停止监控器线程
*/
public void stop() {
//打断线程
monitor.interrupt();
}
}
打断 park 线程是一种将当前线程停止在park()那行的一种方法,可以由打断标志控制,当打断标志为true
park()失去它的作用,false
时生效
private static void test3() throws InterruptedException {
Thread t1 = new Thread(() -> { log.debug("park...");
LockSupport.park(); log.debug("unpark...");
log.debug("打断状态:{}", Thread.currentThread().isInterrupted()); }, "t1");
t1.start(); sleep(0.5);
t1.interrupt();
}
如果不手动复原被打断的线程不会自己复原打断标志,isInterrupted()
方法不会自动复原,所以这时我们可以使用 Thread.interrupted() 清除打断状态。
默认情况下,Java 进程需要等待所有线程都运行结束,才会结束。有一种特殊的线程叫做守护线程,只要其它非守护线程运行结束了,即使守护线程的代码没有执行完,也会强制结束。
log.debug("开始运行...");
Thread t1 = new Thread(() -> {
log.debug("开始运行...");
sleep(2);
log.debug("运行结束...");
}, "daemon");
// 设置该线程为守护线程
t1.setDaemon(true);
t1.start();
sleep(1);
log.debug("运行结束...");
//结果
08:26:38.123 [main] c.TestDaemon - 开始运行...
08:26:38.213 [daemon] c.TestDaemon - 开始运行...
08:26:39.215 [main] c.TestDaemon - 运行结束...
注意
垃圾回收器线程就是一种守护线程
Tomcat 中的 Acceptor 和 Poller 线程都是守护线程,所以 Tomcat 接收到 shutdown 命令后,不会等待它们处理完当前请求
在操作系统
的层面上,我们可以把线程的运行分为五种状态:
按照线程state()里的划分,我们可以把线程分成六种状态
对六种状态的测试:
@Slf4j(topic = "c.TestState")
public class TestState {
public static void main(String[] args) throws IOException {
Thread t1 = new Thread("t1") { // new 状态
@Override
public void run() {
log.debug("running...");
}
};
Thread t2 = new Thread("t2") {
@Override
public void run() {
while(true) { // runnable 状态
}
}
};
t2.start();
Thread t3 = new Thread("t3") {
@Override
public void run() {
log.debug("running...");
}
};
t3.start();
Thread t4 = new Thread("t4") {
@Override
public void run() {
synchronized (TestState.class) {
try {
Thread.sleep(1000000); // timed_waiting 显示阻塞状态
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
};
t4.start();
Thread t5 = new Thread("t5") {
@Override
public void run() {
try {
t2.join(); // waiting 状态
} catch (InterruptedException e) {
e.printStackTrace();
}
}
};
t5.start();
Thread t6 = new Thread("t6") {
@Override
public void run() {
synchronized (TestState.class) { // blocked 状态
try {
Thread.sleep(1000000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
};
t6.start();
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
log.debug("t1 state {}", t1.getState());
log.debug("t2 state {}", t2.getState());
log.debug("t3 state {}", t3.getState());
log.debug("t4 state {}", t4.getState());
log.debug("t5 state {}", t5.getState());
log.debug("t6 state {}", t6.getState());
}
}