多线程基础知识


多线程基础知识。

多线程基础知识

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的使用率。进而分析状态。


文章作者: WangQingLei
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 WangQingLei !
  目录