目录
1. 创建和运行线程
方法一,直接使用 Thread
// 创建线程对象
Thread t = new Thread() {
public void run() {
// 要执行的任务
}
};
// 启动线程
t.start();
例如:
// 构造方法的参数是给线程指定名字,推荐
Thread t1 = new Thread("t1") {
@Override
// run 方法内实现了要执行的任务
public void run() {
log.debug("hello");
}
};
t1.start();
输出:
19:19:00 [t1] c.ThreadStarter - hello
方法二,使用 Runnable 配合 Thread
把【线程】和【任务】(要执行的代码)分开
- Thread 代表线程
- Runnable 可运行的任务(线程要执行的代码)
Runnable runnable = new Runnable() {
public void run(){
// 要执行的任务
}
};
// 创建线程对象
Thread t = new Thread( runnable );
// 启动线程
t.start();
例如:
// 创建任务对象
Runnable task2 = new Runnable() {
@Override
public void run() {
log.debug("hello");
}
};
// 参数1 是任务对象; 参数2 是线程名字,推荐
Thread t2 = new Thread(task2, "t2");
t2.start();
输出:
9:19:00 [t2] c.ThreadStarter - hello
Java 8 以后可以使用 lambda 精简代码
// 创建任务对象
Runnable task2 = () -> log.debug("hello");
// 参数1 是任务对象; 参数2 是线程名字,推荐
Thread t2 = new Thread(task2, "t2");
t2.start();
Thread 与 Runnable 的关系
分析 Thread 的源码,理清它与 Runnable 的关系
//Runnable源码
public interface Runnable {
public abstract void run();
}
//Thread源码(部分)
public class Thread implements Runnable {
/* What will be run. */
private Runnable target;
public Thread(Runnable target) {
init(null, target, "Thread-" + nextThreadNum(), 0);
}
private void init(ThreadGroup g, Runnable target, String name,
long stackSize, AccessControlContext acc,
boolean inheritThreadLocals) {
//...
this.target = target;
//...
}
@Override
public void run() {
if (target != null) {
target.run();
}
}
小结
- 方法1 是把线程和任务合并在了一起,方法2 是把线程和任务分开了
- 用 Runnable 更容易与线程池等高级API 配合
- 用 Runnable 让任务类脱离了 Thread 继承体系,更灵活
方法三,FutureTask 配合 Thread
FutureTask 能够接收 Callable 类型的参数,用来处理有返回结果的情况
// 创建任务对象
FutureTask<Integer> task3 = new FutureTask<>(() -> {
log.debug("hello");
return 100;
});
// 参数1 是任务对象; 参数2 是线程名字,推荐
new Thread(task3, "t3").start();
// 主线程阻塞,同步等待 task 执行完毕的结果
Integer result = task3.get();
log.debug("结果是:{}", result);
输出
19:22:27 [t3] c.ThreadStarter - hello
19:22:27 [main] c.ThreadStarter - 结果是:100
源码分析
//FutureTask源码(部分)
public class FutureTask<V> implements RunnableFuture<V> {
/** The underlying callable; nulled out after running */
private Callable<V> callable;
/** The result to return or exception to throw from get() */
private Object outcome; // non-volatile, protected by state reads/writes
public FutureTask(Callable<V> callable) {
if (callable == null)
throw new NullPointerException();
this.callable = callable;
this.state = NEW; // ensure visibility of callable
}
public void run() {
//...
try {
Callable<V> c = callable;
if (c != null && state == NEW) {
V result;
boolean ran;
try {
result = c.call();
ran = true;
} catch (Throwable ex) {
result = null;
ran = false;
setException(ex);
}
if (ran)
set(result);
}
}
//...
}
protected void set(V v) {
if (UNSAFE.compareAndSwapInt(this, stateOffset, NEW, COMPLETING)) {
outcome = v;
UNSAFE.putOrderedInt(this, stateOffset, NORMAL); // final state
finishCompletion();
}
}
public V get() throws InterruptedException, ExecutionException {
int s = state;
if (s <= COMPLETING)
s = awaitDone(false, 0L);
return report(s);
}
private V report(int s) throws ExecutionException {
Object x = outcome;
if (s == NORMAL)
return (V)x;
if (s >= CANCELLED)
throw new CancellationException();
throw new ExecutionException((Throwable)x);
}
}
//Callable源码
@FunctionalInterface
public interface Callable<V> {
/**
* Computes a result, or throws an exception if unable to do so.
*
* @return computed result
* @throws Exception if unable to compute a result
*/
V call() throws Exception;
}
说明:
- FutureTask内置了一个Callable对象,初始化方法将指定的Callable赋给这个对象。
- FutureTask实现了Runnable接口,并重写了Run方法,在Run方法中调用了Callable中的call方法,并将返回值赋值给outcome变量
- get方法就是取出outcome的值。
2 观察多个线程同时运行
主要是理解
- 交替执行
- 谁先谁后,不由我们控制
示例代码
@Slf4j(topic = "c.TestMultiThread")
public class TestMultiThread {
public static void main(String[] args) {
new Thread(() -> {
while(true) {
log.debug("running");
}
},"t1").start();
new Thread(() -> {
while(true) {
log.debug("running");
}
},"t2").start();
}
}
运行结果:
23:45:26.254 c.TestMultiThread [t2] - running
23:45:26.254 c.TestMultiThread [t2] - running
23:45:26.254 c.TestMultiThread [t2] - running
23:45:26.254 c.TestMultiThread [t2] - running
23:45:26.254 c.TestMultiThread [t1] - running
23:45:26.254 c.TestMultiThread [t1] - running
23:45:26.254 c.TestMultiThread [t1] - running
23:45:26.254 c.TestMultiThread [t1] - running
23:45:26.254 c.TestMultiThread [t1] - running
23:45:26.254 c.TestMultiThread [t1] - running
3. 查看进程线程的方法
windows
- 任务管理器可以查看进程和线程数,也可以用来杀死进程
- tasklist 查看进程
tasklist
|findstr
(查找关键字)
- taskkill 杀死进程
- taskkill /F(彻底杀死)/PID(进程PID)
Linux
-
ps -fe 查看所有进程
-
ps -fT -p 查看某个进程(PID)的所有线程
-
kill 杀死进程 top 按大写 H 切换是否显示线程
-
top -H -p 查看某个进程(PID)的所有线程
-
Java 进程的线程 全是Java没有线程名的解决
来源于互联网收集,https://blog.csdn.net/ActionTech/article/details/120435414
- 如果应用 Oracle jdk8 或者更早的 jdk ,那么还得通过 jstack 或者其余办法来对应线程号和逻辑线程名称。
- 如果应用 Open jdk8,倡议降级到222之后,这样能够通过top命令间接看到线程的名称,放慢诊断。
- 倡议利用在设置线程名时,尽量在15个字符内表白出惟一的含意,便于察看和剖析,当然,这一点 dble 做得不好,会在之后进行调整和批改
- 当然,社区还有一些其余工具,比方阿里的 Arthas 应该也能实现线程 id 和名字对应的性能,不过引入第三方总是件麻烦的事件,还是原生的更香。
[root@localhost home]# top -H -p 5861 top - 11:01:42 up 55 min, 2 users, load average: 0.18, 0.16, 0.15 Threads: 19 total, 0 running, 19 sleeping, 0 stopped, 0 zombie %Cpu(s): 8.2 us, 4.9 sy, 0.0 ni, 86.9 id, 0.0 wa, 0.0 hi, 0.0 si, 0.0 st KiB Mem : 3861296 total, 111468 free, 3287268 used, 462560 buff/cache KiB Swap: 2097148 total, 1814680 free, 282468 used. 329880 avail Mem PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND 5863 root 20 0 3622740 25452 12344 S 0.3 0.7 0:00.01 GC Thread#0 5861 root 20 0 3622740 25452 12344 S 0.0 0.7 0:00.00 java 5862 root 20 0 3622740 25452 12344 S 0.0 0.7 0:00.05 java 5864 root 20 0 3622740 25452 12344 S 0.0 0.7 0:00.00 G1 Main Marker 5865 root 20 0 3622740 25452 12344 S 0.0 0.7 0:00.00 G1 Conc#0 5866 root 20 0 3622740 25452 12344 S 0.0 0.7 0:00.00 G1 Refine#0 5867 root 20 0 3622740 25452 12344 S 0.0 0.7 0:00.01 G1 Young RemSet 5868 root 20 0 3622740 25452 12344 S 0.0 0.7 0:00.01 VM Thread 5869 root 20 0 3622740 25452 12344 S 0.0 0.7 0:00.00 Reference Handl 5870 root 20 0 3622740 25452 12344 S 0.0 0.7 0:00.00 Finalizer 5871 root 20 0 3622740 25452 12344 S 0.0 0.7 0:00.00 Signal Dispatch 5872 root 20 0 3622740 25452 12344 S 0.0 0.7 0:00.00 Service Thread 5873 root 20 0 3622740 25452 12344 S 0.0 0.7 0:00.00 C2 CompilerThre 5874 root 20 0 3622740 25452 12344 S 0.0 0.7 0:00.01 C1 CompilerThre 5875 root 20 0 3622740 25452 12344 S 0.0 0.7 0:00.00 Sweeper thread 5876 root 20 0 3622740 25452 12344 S 0.0 0.7 0:00.09 VM Periodic Tas 5877 root 20 0 3622740 25452 12344 S 0.0 0.7 0:00.00 Common-Cleaner 5878 root 20 0 3622740 25452 12344 S 0.0 0.7 0:00.00 t 5879 root 20 0 3622740 25452 12344 S 0.0 0.7 0:00.00 t1
-
Java
-
jps 命令查看所有 Java 进程
-
jstack 查看某个 Java 进程(PID)的所有线程状态
[root@localhost home]# jstack 4219 2022-07-26 10:20:12 Full thread dump Java HotSpot(TM) 64-Bit Server VM (25.321-b07 mixed mode): "Attach Listener" #11 daemon prio=9 os_prio=0 tid=0x00007fe458001000 nid=0x10da waiting on condition [0x0000000000000000] java.lang.Thread.State: RUNNABLE "DestroyJavaVM" #10 prio=5 os_prio=0 tid=0x00007fe48c009800 nid=0x107c waiting on condition [0x0000000000000000] java.lang.Thread.State: RUNNABLE "t1" #9 prio=5 os_prio=0 tid=0x00007fe48c195000 nid=0x1088 waiting on condition [0x00007fe4775f4000] java.lang.Thread.State: TIMED_WAITING (sleeping) at java.lang.Thread.sleep(Native Method) at java.lang.Thread.sleep(Thread.java:342) at java.util.concurrent.TimeUnit.sleep(TimeUnit.java:386) at RunPhenomenon.lambda$main$1(RunPhenomenon.java:31) at RunPhenomenon$$Lambda$2/303563356.run(Unknown Source) at java.lang.Thread.run(Thread.java:750) "t2" #8 prio=5 os_prio=0 tid=0x00007fe48c193800 nid=0x1087 waiting on condition [0x00007fe4776f5000] java.lang.Thread.State: TIMED_WAITING (sleeping) at java.lang.Thread.sleep(Native Method) at java.lang.Thread.sleep(Thread.java:342) at java.util.concurrent.TimeUnit.sleep(TimeUnit.java:386) at RunPhenomenon.lambda$main$0(RunPhenomenon.java:20) at RunPhenomenon$$Lambda$1/471910020.run(Unknown Source) at java.lang.Thread.run(Thread.java:750) "Service Thread" #7 daemon prio=9 os_prio=0 tid=0x00007fe48c0dc800 nid=0x1085 runnable [0x0000000000000000] java.lang.Thread.State: RUNNABLE "C1 CompilerThread1" #6 daemon prio=9 os_prio=0 tid=0x00007fe48c0ba000 nid=0x1084 waiting on condition [0x0000000000000000] java.lang.Thread.State: RUNNABLE "C2 CompilerThread0" #5 daemon prio=9 os_prio=0 tid=0x00007fe48c0b7000 nid=0x1083 waiting on condition [0x0000000000000000] java.lang.Thread.State: RUNNABLE "Signal Dispatcher" #4 daemon prio=9 os_prio=0 tid=0x00007fe48c0b5000 nid=0x1082 runnable [0x0000000000000000] java.lang.Thread.State: RUNNABLE "Finalizer" #3 daemon prio=8 os_prio=0 tid=0x00007fe48c082000 nid=0x1081 in Object.wait() [0x00007fe477cfb000] java.lang.Thread.State: WAITING (on object monitor) at java.lang.Object.wait(Native Method) - waiting on <0x00000000ec588ee8> (a java.lang.ref.ReferenceQueue$Lock) at java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:144) - locked <0x00000000ec588ee8> (a java.lang.ref.ReferenceQueue$Lock) at java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:165) at java.lang.ref.Finalizer$FinalizerThread.run(Finalizer.java:216) "Reference Handler" #2 daemon prio=10 os_prio=0 tid=0x00007fe48c07d800 nid=0x1080 in Object.wait() [0x00007fe477dfc000] java.lang.Thread.State: WAITING (on object monitor) at java.lang.Object.wait(Native Method) - waiting on <0x00000000ec586c00> (a java.lang.ref.Reference$Lock) at java.lang.Object.wait(Object.java:502) at java.lang.ref.Reference.tryHandlePending(Reference.java:191) - locked <0x00000000ec586c00> (a java.lang.ref.Reference$Lock) at java.lang.ref.Reference$ReferenceHandler.run(Reference.java:153) "VM Thread" os_prio=0 tid=0x00007fe48c074000 nid=0x107f runnable "GC task thread#0 (ParallelGC)" os_prio=0 tid=0x00007fe48c01e800 nid=0x107d runnable "GC task thread#1 (ParallelGC)" os_prio=0 tid=0x00007fe48c020800 nid=0x107e runnable "VM Periodic Task Thread" os_prio=0 tid=0x00007fe48c0e1800 nid=0x1086 waiting on condition JNI global references: 310
-
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(密码)
∗ 4. 原理之线程运行 \textcolor{Green}{*4. 原理之线程运行} ∗4.原理之线程运行
栈与栈帧
Java Virtual Machine Stacks (Java 虚拟机栈)
我们都知道 JVM 中由堆、栈、方法区所组成,其中栈内存是给谁用的呢?其实就是线程,每个线程启动后,虚拟 机就会为其分配一块栈内存。
- 每个栈由多个栈帧(Frame)组成,对应着每次方法调用时所占用的内存
- 每个线程只能有一个活动栈帧,对应着当前正在执行的那个方法
线程上下文切换(Thread Context Switch)
因为以下一些原因导致 cpu 不再执行当前的线程,转而执行另一个线程的代码
- 线程的 cpu 时间片用完
- 垃圾回收 (工作线程暂停)
- 有更高优先级的线程需要运行
- 线程自己调用了 sleep、yield、wait、join、park、synchronized、lock 等方法
当 Context Switch 发生时,需要由操作系统保存当前线程的状态,并恢复另一个线程的状态
,Java 中对应的概念 就是程序计数器(Program Counter Register)
,它的作用是记住下一条 jvm 指令的执行地址,是线程私有的
- 状态包括程序计数器、虚拟机栈中每个栈帧的信息,如局部变量、操作数栈、返回地址等
- Context Switch
频繁发生会影响性能