Skip to content

Latest commit

 

History

History
250 lines (191 loc) · 8.12 KB

thread.md

File metadata and controls

250 lines (191 loc) · 8.12 KB

Thread

对于 Thread 的理解,需要明白 Thread 中的所有常用的方法的含义,使用场景

ThreadFactory

可以方便的定义 thread name,daemon status,priority, thread Group

一个默认的实现java.util.concurrent.Executors.DefaultThreadFactory

ThreadGroup

start

    Thread t = new Thread(() -> System.out.println(Thread.currentThread().getName()+" start ..."));
    t.start();// 启动这个新的线程

start 会调用一个start0方法,让 jvm 启用一个线程

Causes this thread to begin execution; the Java Virtual Machine calls the run method of this thread.

run

// 这个 run 方法其实是在我们调用 Thread#start 方法之后,由 JVM 使用新的线程调用的
// JVM 保证在线程创建之后,会调用 Thread#run 方法
// 如果我们自己在代码中直接调用 Thread#run 方法,run 方法也会执行,但不是在新的线程中执行的
@Override
public void run() {
    if (target != null) {
        target.run();
    }
}

interrupt

我们知道启动一个线程是用start()方法,但是如何关闭(安全的)一个线程呢?

使用 volatile 标记(变量) + interrupt

  • volatile 变量,如果线程检查到的状态是关闭的,那么此变量不接受新的任务即可
  • volatile 变量,保证可见性(一个线程修改变量的结果,对其他线程可见)
  • interrupt 使阻塞(blocked)状态的线程,出现InterruptedException异常,终止线程

join

Waits for this thread to die.

  • 一个线程等待另一个线程完成后,该线程继续执行
  • join 实现的是 wait() + notifyAll(notify)
public static void main(String[] args) throws Exception {
    Runnable r = () -> {
        System.out.println("run ...");
        try {
            Thread.sleep(3 * 1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("run end ...");
    };
    Thread thread1 = new Thread(r);
    Runnable r2 = () -> {
        try {
            System.out.println("run2 ...");
            //thread1.join();
            System.out.println("run2 end ...");
        } catch (Exception e) {
            e.printStackTrace();
        }
    };
    Thread thread2 = new Thread(r2);
    thread1.start();
    thread2.start();
}
// t2 在t1之前结束
// run ...
// run2 ...
// run2 end ...
// run end ...
// use thread1.join();
// t2必在t1完成后结束(t2一直等待t1结束)
// run ...
// run2 ...
// run end ...
// run2 end ...

yield

线程让出 cpu(别用)

sleep

Sleep函数就是干这事的,他告诉操作系统“在未来的多少毫秒内我不参与CPU竞争”。

Thread.Sleep(0)的作用,就是“触发操作系统立刻重新进行一次CPU竞争”

// use TimeUnit
TimeUnit.MILLISECONDS.sleep(200);
// use Thread
Thread.sleep(200);

InterruptedException

InterruptedException 是如何产生的 demo

如果一个线程在sleep状态(wait,join,sleep),调用 interrupt 会出现InterruptedException异常

public static void main(String[] args) throws InterruptedException {
    Runnable r = () -> {
        try {
            System.out.println("[I am " + Thread.currentThread().getName() + "] thread");
            Thread.sleep(2 * 1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    };
    Thread t = new Thread(r);
    t.start();
    // throw InterruptedException
    System.out.println("[I am " + Thread.currentThread().getName() + "] thread");
    t.interrupt();
}

日志:

[I am main] thread
[I am Thread-0] thread
java.lang.InterruptedException: sleep interrupted
    at java.lang.Thread.sleep(Native Method)
    at java.lang.Thread.run(Thread.java:748)

stop

如何正确的终止一个线程,也是一门艺术 😂

线程的状态转化

threads-state

线程的状态在多数情况下对我们来说是无感知的。但是在遇到线程问题,如死锁等,需要通过 jstack 命令生成线程快照 排查问题的时候,是十分有帮助的。

jstack 日志片段:

ExecutorsDemo 代码


"pool-1-thread-2" #12 prio=5 os_prio=0 tid=0x000000005b486000 nid=0x460 waiting on condition [0x000000005bd2f000]
   java.lang.Thread.State: WAITING (parking)
        at sun.misc.Unsafe.park(Native Method)
        - parking to wait for  <0x00000000d62af108> (a java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject)
        at java.util.concurrent.locks.LockSupport.park(LockSupport.java:175)
        at java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.await(AbstractQueuedSynchronizer.java:2039)
        at java.util.concurrent.LinkedBlockingQueue.take(LinkedBlockingQueue.java:442)
        at java.util.concurrent.ThreadPoolExecutor.getTask(ThreadPoolExecutor.java:1074)
        at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1134)
        at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
        at java.lang.Thread.run(Thread.java:748)

"pool-1-thread-1" #11 prio=5 os_prio=0 tid=0x000000005b49d000 nid=0x3624 waiting on condition [0x000000005b01e000]
   java.lang.Thread.State: WAITING (parking)
        at sun.misc.Unsafe.park(Native Method)
        - parking to wait for  <0x00000000d62af108> (a java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject)
        at java.util.concurrent.locks.LockSupport.park(LockSupport.java:175)
        at java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.await(AbstractQueuedSynchronizer.java:2039)
        at java.util.concurrent.LinkedBlockingQueue.take(LinkedBlockingQueue.java:442)
        at java.util.concurrent.ThreadPoolExecutor.getTask(ThreadPoolExecutor.java:1074)
        at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1134)
        at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
        at java.lang.Thread.run(Thread.java:748)

上面的 ExecutorsDemo 中,我们使用 Executors.newFixedThreadPool(2) 方法创建了二个线程,从上面的日志,我们可以快速的看到

代码由于 LinkedBlockingQueue.take 方法造成阻塞.

创建线程的方式

  1. 实现 Runnable 接口,把 Runnable 提交给 Thread (推荐方式)
  2. 继承 Thread 类,重写 run 方法
// 实现 Runnable 接口
public class ThreadTest {
    public static void main(String[] args) {

        new Thread(() -> System.out.println("run")).start();

        new ThreadRun().start();

    }

}
// 继承 Thread 类,重写 run 方法
class ThreadRun extends Thread {
    @Override
    public void run() {
        System.out.println("ThreadRun");
    }
}

好文连接