多线程基础知识。
多线程基础知识
Java线程的6种状态及切换
1、初始状态(NEW)
实现Runnable接口或者继承Thread可以得到一个线程类,new一个实例出来,线程就进入了初始状态。
2.1、就绪状态(RUNNABLE之READY)
线程对象被创建后,其它线程调用了该对象的start()方法,从而来启动该线程。例如,thread.start()。处于就绪状态的线程,随时可能被CPU调度执行。
当前线程sleep()方法结束,其他线程join()结束,等待用户输入完毕,某个线程拿到对象锁,这些线程也将进入就绪状态。
当前线程时间片用完了,调用当前线程的yield()方法,当前线程进入就绪状态。
锁池里的线程拿到对象锁后,进入就绪状态。
2.2、运行中状态(RUNNABLE之RUNNING)
线程获取CPU权限进行执行。需要注意的是,线程只能从就绪状态进入到运行状态。
3、阻塞状态(BLOCKED)
阻塞状态是线程阻塞在进入synchronized关键字修饰的方法或代码块(获取锁)时的状态。
4、等待(WAITING)
处于这种状态的线程不会被分配CPU执行时间,它们要等待被显式地唤醒,否则会处于无限期等待的状态。
5、超时等待(TIMED_WAITING)
处于这种状态的线程不会被分配CPU执行时间,不过无须无限期等待被其他线程显示地唤醒,在达到一定时间后它们会自动唤醒。
6、终止状态(TERMINATED)
当线程的run()方法完成时,或者主线程的main()方法完成时,我们就认为它终止了。这个线程对象也许是活的,但是它已经不是一个单独执行的线程。线程一旦终止了,就不能复生。
相关方法区别
yield() 与 wait()的比较
(01) wait()是让线程由运行状态进入到等待(阻塞)状态,而yield是让线程由运行状态进入到就绪状态。
(02) wait()是会线程释放它所持有对象的同步锁,而yield()方法不会释放锁。
yield() 与 sleep()的比较
二者都是释放CPU的执行权,不释放持有的锁资源
sleep可以使优先级低的线程得到执行的机会, 而yield只能使同优先级的线程有执行的机会.
Start()和Run()的区别说明
- start方法的内部会调用本多次方法start0启动一个新线程,然后在新线程中执行相应的run方法。start()不能被重复调用,第二次调用后线程的状态已经不是NEW了,系统会抛出不合法的线程状态异常。
- 而直接调用run方法就和普通方法调用一样,不会开启新线程。
- 可以在run方法中通过Thread.currentThread().getName()打印当前线程的名字来查看二者的区别。
线程死锁
Demo代码:
package Thread;
public class DeadLockTest implements Runnable{
private String lockFirst;
private String lockSecond;
public DeadLockTest(String lockFirst, String lockSecond) {
this.lockFirst = lockFirst;
this.lockSecond = lockSecond;
}
@Override
public void run() {
synchronized(lockFirst) {
System.out.println(Thread.currentThread().getName() + "======" + lockFirst);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (lockSecond) {
System.out.println(Thread.currentThread().getName() + "======" + lockFirst);
}
}
}
public static void main(String[] args) throws InterruptedException {
DeadLockTest deadLockTest1 = new DeadLockTest("lockFirst", "lockSecond");
DeadLockTest deadLockTest2 = new DeadLockTest("lockSecond", "lockFirst");
Thread thread1 = new Thread(deadLockTest1);
Thread thread2 = new Thread(deadLockTest2);
thread1.start();
thread2.start();
thread1.join();
thread2.join();
}
}
执行打印:
Thread-0======lockFirst
Thread-1======lockSecond
检测死锁:
E:\新建文件夹\IdeaProjects\data-structure>jps
16528 Launcher
7696 Jps
12804 RemoteMavenServer36
1272 DeadLockTest
6136
E:\新建文件夹\IdeaProjects\data-structure>jstack 1272
2023-07-22 01:31:43
Full thread dump Java HotSpot(TM) 64-Bit Server VM (25.121-b13 mixed mode):
"Thread-1" #13 prio=5 os_prio=0 tid=0x000000001eac9800 nid=0x3ad4 waiting for monitor entry [0x000000001f8be000]
java.lang.Thread.State: BLOCKED (on object monitor)
at Thread.DeadLockTest.run(DeadLockTest.java:23)
- waiting to lock <0x000000076ba9f3a0> (a java.lang.String)
- locked <0x000000076ba9f3e0> (a java.lang.String)
at java.lang.Thread.run(Thread.java:745)
"Thread-0" #12 prio=5 os_prio=0 tid=0x000000001eac6000 nid=0x36f4 waiting for monitor entry [0x000000001f7be000]
java.lang.Thread.State: BLOCKED (on object monitor)
at Thread.DeadLockTest.run(DeadLockTest.java:23)
- waiting to lock <0x000000076ba9f3e0> (a java.lang.String)
- locked <0x000000076ba9f3a0> (a java.lang.String)
at java.lang.Thread.run(Thread.java:745)
"Service Thread" #11 daemon prio=9 os_prio=0 tid=0x000000001ea44800 nid=0x1e98 runnable [0x0000000000000000]
java.lang.Thread.State: RUNNABLE
"C1 CompilerThread3" #10 daemon prio=9 os_prio=2 tid=0x000000001e9b4000 nid=0xb2c waiting on condition [0x0000000000000000]
java.lang.Thread.State: RUNNABLE
"C2 CompilerThread2" #9 daemon prio=9 os_prio=2 tid=0x000000001e9a5000 nid=0x18f8 waiting on condition [0x0000000000000000]
java.lang.Thread.State: RUNNABLE
"C2 CompilerThread1" #8 daemon prio=9 os_prio=2 tid=0x000000001e99e800 nid=0xf90 waiting on condition [0x0000000000000000]
java.lang.Thread.State: RUNNABLE
"C2 CompilerThread0" #7 daemon prio=9 os_prio=2 tid=0x000000001e99a800 nid=0x3068 waiting on condition [0x0000000000000000]
java.lang.Thread.State: RUNNABLE
"Monitor Ctrl-Break" #6 daemon prio=5 os_prio=0 tid=0x000000001e98e800 nid=0x4618 runnable [0x000000001f0be000]
java.lang.Thread.State: RUNNABLE
at java.net.SocketInputStream.socketRead0(Native Method)
at java.net.SocketInputStream.socketRead(SocketInputStream.java:116)
at java.net.SocketInputStream.read(SocketInputStream.java:171)
at java.net.SocketInputStream.read(SocketInputStream.java:141)
at sun.nio.cs.StreamDecoder.readBytes(StreamDecoder.java:284)
at sun.nio.cs.StreamDecoder.implRead(StreamDecoder.java:326)
at sun.nio.cs.StreamDecoder.read(StreamDecoder.java:178)
- locked <0x000000076bbc9c08> (a java.io.InputStreamReader)
at java.io.InputStreamReader.read(InputStreamReader.java:184)
at java.io.BufferedReader.fill(BufferedReader.java:161)
at java.io.BufferedReader.readLine(BufferedReader.java:324)
- locked <0x000000076bbc9c08> (a java.io.InputStreamReader)
at java.io.BufferedReader.readLine(BufferedReader.java:389)
at com.intellij.rt.execution.application.AppMainV2$1.run(AppMainV2.java:48)
"Attach Listener" #5 daemon prio=5 os_prio=2 tid=0x000000001e94d800 nid=0x4164 waiting on condition [0x0000000000000000]
java.lang.Thread.State: RUNNABLE
"Signal Dispatcher" #4 daemon prio=9 os_prio=2 tid=0x000000001e8f8000 nid=0xbe8 runnable [0x0000000000000000]
java.lang.Thread.State: RUNNABLE
"Finalizer" #3 daemon prio=8 os_prio=1 tid=0x0000000003049000 nid=0x3728 in Object.wait() [0x000000001edbe000]
java.lang.Thread.State: WAITING (on object monitor)
at java.lang.Object.wait(Native Method)
- waiting on <0x000000076b908ec8> (a java.lang.ref.ReferenceQueue$Lock)
at java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:143)
- locked <0x000000076b908ec8> (a java.lang.ref.ReferenceQueue$Lock)
at java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:164)
at java.lang.ref.Finalizer$FinalizerThread.run(Finalizer.java:209)
"Reference Handler" #2 daemon prio=10 os_prio=2 tid=0x000000001c9dd800 nid=0x2440 in Object.wait() [0x000000001e8bf000]
java.lang.Thread.State: WAITING (on object monitor)
at java.lang.Object.wait(Native Method)
- waiting on <0x000000076b906b68> (a java.lang.ref.Reference$Lock)
at java.lang.Object.wait(Object.java:502)
at java.lang.ref.Reference.tryHandlePending(Reference.java:191)
- locked <0x000000076b906b68> (a java.lang.ref.Reference$Lock)
at java.lang.ref.Reference$ReferenceHandler.run(Reference.java:153)
"main" #1 prio=5 os_prio=0 tid=0x0000000002f52800 nid=0x2d0c in Object.wait() [0x00000000029af000]
java.lang.Thread.State: WAITING (on object monitor)
at java.lang.Object.wait(Native Method)
- waiting on <0x000000076ba9f438> (a java.lang.Thread)
at java.lang.Thread.join(Thread.java:1249)
- locked <0x000000076ba9f438> (a java.lang.Thread)
at java.lang.Thread.join(Thread.java:1323)
at Thread.DeadLockTest.main(DeadLockTest.java:39)
"VM Thread" os_prio=2 tid=0x000000001c9d9000 nid=0x288c runnable
"GC task thread#0 (ParallelGC)" os_prio=0 tid=0x0000000002f68000 nid=0x3d00 runnable
"GC task thread#1 (ParallelGC)" os_prio=0 tid=0x0000000002f69800 nid=0x2d50 runnable
"GC task thread#2 (ParallelGC)" os_prio=0 tid=0x0000000002f6b000 nid=0x3bbc runnable
"GC task thread#3 (ParallelGC)" os_prio=0 tid=0x0000000002f6c800 nid=0x4790 runnable
"GC task thread#4 (ParallelGC)" os_prio=0 tid=0x0000000002f70000 nid=0x4f34 runnable
"GC task thread#5 (ParallelGC)" os_prio=0 tid=0x0000000002f71000 nid=0x1af4 runnable
"GC task thread#6 (ParallelGC)" os_prio=0 tid=0x0000000002f74000 nid=0x10dc runnable
"GC task thread#7 (ParallelGC)" os_prio=0 tid=0x0000000002f75800 nid=0x4c80 runnable
"VM Periodic Task Thread" os_prio=2 tid=0x000000001eabc800 nid=0x14b4 waiting on condition
JNI global references: 16
Found one Java-level deadlock:
=============================
"Thread-1":
waiting to lock monitor 0x000000001c9e3678 (object 0x000000076ba9f3a0, a java.lang.String),
which is held by "Thread-0"
"Thread-0":
waiting to lock monitor 0x000000001c9e3728 (object 0x000000076ba9f3e0, a java.lang.String),
which is held by "Thread-1"
Java stack information for the threads listed above:
===================================================
"Thread-1":
at Thread.DeadLockTest.run(DeadLockTest.java:23)
- waiting to lock <0x000000076ba9f3a0> (a java.lang.String)
- locked <0x000000076ba9f3e0> (a java.lang.String)
at java.lang.Thread.run(Thread.java:745)
"Thread-0":
at Thread.DeadLockTest.run(DeadLockTest.java:23)
- waiting to lock <0x000000076ba9f3e0> (a java.lang.String)
- locked <0x000000076ba9f3a0> (a java.lang.String)
at java.lang.Thread.run(Thread.java:745)
Found 1 deadlock.
结合代码分析线程栈信息。上面这个输出非常明显,找到处于 BLOCKED 状态的线程,很快就定位问题。 jstack 本身也会把类似的简单死锁抽取出来,直接打印出来。
当然如果我们是开发自己的管理工具,需要用更加程序化的方式扫描服务进程、定位死锁,可以考虑使用 Java 提供的标准管理 API,ThreadMXBean,其直接就提供了 findDeadlockedThreads() 方法用于定位。我们可以通过定时任务扫描来定时获取相关监控。
如何在编程中尽量预防死锁呢?
1、尽量避免使用多个锁,并且只有需要时才持有锁
2、如果必须使用多个锁,尽量设计好锁的获取顺序
3、使用带超时的方法,为程序带来更多可控性
4、当是死循环引起的其他线程阻塞,会导致cpu飙升,可以先看下cpu的使用率。进而分析状态。