Thread

一、构造方法

1、ThreadGroup group

所属线程组,默认值是Thread.currentThread().getParentGroup(),这句代码是在创建线程的时候执行,所以默认的parentGroup是父线程的线程组

2、Runnable target

这个不用多介绍,其实是策略模式的一种实现,将线程的具体执行单元抽取出来,以策略的方式执行。如果不传,就必须重写Thread.run()方法

3、String name

线程名称,如果不传,默认值是Thread-0、Thread-1、Thread-2 … Thread-n。线程名称不能重复,否则会抛异常

4、long stackSize

线程的栈内存大小,每个平台都不一样,在某些平台中,这个参数可能不管用。默认值是???

二、API

  • setDeamon(boolean) 设置守护线程。true守护线程,parent线程结束后,自动结束子线程,false非守护线程,parent线程结束后,不自动结束子线程。默认非守护线程

  • join 让parent线程等待我执行完毕,在我执行完毕之前,父线程将一直等待我。也可以使用join(mills)指定等待时间,如果超时,父线程会被自动唤醒

  • sleep 让当前线程休眠一段时间,到时间自动唤醒,休眠期间当前线程不会放弃任何锁,也不会放弃CPU的执行权

  • interrupt Object.wait, Thread.sleep, Thread.join会让阻塞线程执行,这时使用interrupt会打断线程的阻塞,线程会收到InterruptedException异常

  • interrupted 判断当前线程是否被中断,静态方法

  • isInterrupted 判断当前线程是否被中断,成员方法

  • isAlive Tests if this thread is alive. A thread is alive if it has been started and has not yet died.

  • Thread.holdsLock(obj) 静态方法 判断线程是否持有obj锁

  • setUncaughtExceptionHandler 设置线程异常监听器

  • enumerate 将当前线程组和所有子线程组中存活的线程查找出来,并存放在入参中

  • yield 暂时放弃CPU执行权,然后所有其他线程根据优先级争取CPU执行权

  • Thread.activeAccount() 静态方法 评估所在线程组和所有子线程组存活的线程数量,是一个不准确的数量,此方法是用来debug或者监控系统用的

三、线程状态

状态 描述 时机
NEW 刚刚创建出来,还没有start new Thread()
RUNNABLE 已经start,可执行状态。可能在执行,也可能在等待CPU资源调度 thread.start()
BLOCKED 等待MONITOR,进入或重新进入synchronized method/block MONITOR.wait() -> MONITOR.notify()
WAITING 等待MONITOR.notify,或者等待被join的线程结束 MONITOR.wait()、Thread.join()
TIMED_WAITING 等待timeout结束 MONITOR.wait(timeout)、Thread.sleep(timeout)、thread.join(timeout)
TERMINATED 线程执行结束 执行完毕(正常退出和异常退出)

四、锁

1、synchronized

同步方法和静态同步方法的MONITOR都是谁?

public class SynchronizedTest {

    /**
     * 这个案例能证明:
     *      非静态同步方法的MONITOR是当前对象
     *      静态同步方法的MONITOR是当前Class
     */
    public static void main(String[] args) {
        NonStaticSync obj = new NonStaticSync();

        // 这两个线程不能同时打印, 说明其中一个在等待另一个释放锁, 所以两个锁一定是相同的
        // 证明非静态同步方法的MONITOR是当前对象
        new Thread(obj::test1).start();
        new Thread(obj::test2).start();

        // 这两个线程不能同时打印, 说明其中一个在等待另一个释放锁, 所以两个锁一定是相同的
        // 证明静态同步方法的MONITOR是当前Class
        new Thread(StaticSync::test1).start();
        new Thread(StaticSync::test2).start();
    }

    static class NonStaticSync {

        public synchronized void test1() {
            System.out.println("NonStaticSync.test1");
            try {
                Thread.sleep(5_000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

        private static final Object MONITOR = new Object();

        public void test2() {
            synchronized (this) {
//        synchronized (MONITOR) {
                System.out.println("NonStaticSync.test2");
                try {
                    Thread.sleep(5_000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }

    }

    static class StaticSync {

        public static synchronized void test1() {
            System.out.println("StaticSync.test1");
            try {
                Thread.sleep(5_000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

        public static void test2() {
            synchronized (StaticSync.class) {
                System.out.println("StaticSync.test2");
                try {
                    Thread.sleep(5_000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }

    }

}

2、死锁是怎么发生的,怎么排查死锁,怎么避免死锁

同步代码多层级调用,避免同步代码多层级调用,就可以避免死锁

jstack pid可以查看死锁

jconsole 也可以检查死锁

static synchronized method

五、如何停止线程

1、优雅的停止线程

public class StopThreadGraceful {

    public static void main(String[] args) throws InterruptedException {
        Worker worker = new Worker();
        worker.start();

        Thread.sleep(5_000);
        worker.interrupt();
    }

    private static class Worker extends Thread {

        @Override
        public void run() {
            while (true) {
                if (isInterrupted()) {
                    break;
                }
                doWork();
            }
            // 释放资源等操作
        }

//        @Override
//        public void run() {
//            while (true) {
//                doWork();
//                try {
//                    Thread.sleep(1000);
//                } catch (InterruptedException e) {
//                    break;
//                }
//            }
//            // 释放资源等操作
//        }

        private void doWork() {
            System.out.println("working...");
        }
    }

}

2、强制停止线程

public class StopThreadForce {

    /**
     * 强制停止线程的思路是: 守护线程
     */
    public static void main(String[] args) throws InterruptedException {
        ThreadService threadService = ThreadService.newInstance();
        threadService.submit(() -> {
            while (true) {
                System.out.println("------");
            }
        });

        Thread.sleep(5_000);
        threadService.shutdown();
    }

    public static class ThreadService {

        private Thread hostThread;

        private ThreadService() {}

        public static ThreadService newInstance() {
            return new ThreadService();
        }

        public void submit(Runnable runnable) {
            hostThread = new Thread(() -> {
                Thread targetThread = new Thread(runnable);
                targetThread.setDaemon(true);
                targetThread.start();
                try {
                    targetThread.join();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            });
            hostThread.start();
        }

        public void shutdown() {
            hostThread.interrupt();
        }

    }

}

六、线程间通信

两种方式:共享内存 or 消息传递

1、共享内存

2、消息传递 Object.notify() & Object.wait()

七、Thread.sleep和Object.wait的区别是什么

1、sleep时Thread的方法,而wait是Object的方法

2、sleep不会放弃CPU执行权,但是wait会放弃CPU执行权

3、sleep不需要MONITOR,wait必须在同步代码块中执行,MONITOR.wait()

4、sleep不需要唤醒,wait则需要唤醒

八、手写线程池

public class SimpleThreadPool {

    private static final int DEFAULT_SIZE = 10;
    private static final String THREAD_NAME_PREFIX = "simple-thread-pool";
    private static final String THREAD_GROUP_NAME = THREAD_NAME_PREFIX;

    private static int seq = 0;

    private LinkedList<Thread> threads;

    private final LinkedList<Runnable> tasks = new LinkedList<>();;

    private int size;

    private ThreadGroup group;

    public SimpleThreadPool() {
        this(DEFAULT_SIZE);
    }

    public SimpleThreadPool(int size) {
        this.size = size;
        if (size <= 0) {
            this.size = DEFAULT_SIZE;
        }
        init();
    }

    public void submit(Runnable task) {
        synchronized (tasks) {
            tasks.addFirst(task);
            tasks.notifyAll();
        }
    }

    private void init() {
        group = new ThreadGroup(THREAD_GROUP_NAME);
        threads = new LinkedList<>();

        for (int i = 0; i < size; i++) {
            Thread thread = createThread();
            threads.addFirst(thread);
        }
    }

    private Thread createThread() {
        Thread thread = new Thread(group, () -> {
            while (true) {
                Runnable runnable;
                synchronized (tasks) {
                    while (tasks.isEmpty()) {
                        try {
                            tasks.wait();
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                    runnable = tasks.removeLast();
                }
                if (runnable != null) {
                    runnable.run();
                }
            }
        }, THREAD_NAME_PREFIX + "-" + ++seq);
        thread.start();
        return thread;
    }

    public static void main(String[] args) {
        SimpleThreadPool pool = new SimpleThreadPool(3);

        Random random = new Random();

        while (true) {
            int count = random.nextInt(20);
            System.out.println("count = " + count);
            IntStream.range(0, count).forEach(i -> {
                pool.submit(() -> {
                    System.out.println(Thread.currentThread());
                    try {
                        Thread.sleep(100);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                });
            });
            try {
                Thread.sleep(5000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

}
0%