美文网首页
读书也要记:Java 线程(基础)

读书也要记:Java 线程(基础)

作者: 鸣鸣那只羊 | 来源:发表于2017-03-12 23:54 被阅读78次

    近来读了好几篇“老马说编程”写的 Java 多线程文章,这里记录一下第一篇的摘要,以及拓展查找了点相关的东西,算是复习巩固(有些冷知识平时还真没留意过)。原文:(65) 线程的基本概念 / 计算机程序的思维逻辑

    在 Java 中创建线程有两种方式,一种是继承 Thread,另外一种是实现 Runnable 接口。

    extends Thread
    • 继承 Thread 的类,重载 run 方法,方法签名是固定的,public,没有参数,没有返回值,不能抛出 checked Exception (可以抛出unchecked Exception),下图帮大家复习一下两种 Exception

      checked & unchecked Exception
    • 调用 Thread 的 start 方法后,线程开始执行;如果不调用 start,run 的代码会在当前线程执行

    • 怎么确认在跑的那一个线程呢?

    /**
         * Returns a reference to the currently executing thread object.
         * @return  the currently executing thread.
         */
        public static native Thread currentThread();
    

    难得看到 native 关键字,所以 Thread 是 JNI (Java Native Interface),略懵逼,哪位高人来指导下?

    • 线程 id 是递增的整数,线程name可以设置
        /* For generating thread ID */
        private static long threadSeqNumber;
        private static synchronized long nextThreadID() {
            return ++threadSeqNumber;
        }
    
        /* For autonumbering anonymous threads. */
        private static int threadInitNumber;
        private static synchronized int nextThreadNum() {
            return threadInitNumber++;
        }
        public Thread() {
            init(null, null, "Thread-" + nextThreadNum(), 0);
        }
    
    implements Runnable
    • 仅仅实现 Runnable 是不够的,需要new Thread,启动线程都是调用 Thread 对象的 start 方法
    程序什么时候退出,关于 daemon 线程
    • 启动线程会启动一条单独的执行流,整个程序只有在所有线程都结束的时候才退出
    • 但 daemon 线程是例外,当整个程序中剩下的都是 daemon 线程的时候,程序就会退出。
    • daemon 线程一般是其他线程的辅助线程,在它辅助的主线程退出的时候,它就没有存在的意义了。
    • Java 也会创建多个线程,除了 main 线程外,至少还有一个负责垃圾回收的线程,这个线程就是 daemon 线程,在 main 线程结束的时候,垃圾回收线程也会退出。
    关于 Thread 的小知识
    • sleep():睡眠期间,该线程会让出CPU,但睡眠的时间不一定是确切的给定毫秒数,可能有一定的偏差。
    • yield():告诉操作系统的调度器,我现在不着急占用CPU,你可以先让其他线程运行。(想起 Python 的 yield 😄)
    • ** join()**:让调用join的线程等待该线程结束,比如希望main线程在子线程结束后再退出
    public static void main(String[] args) throws InterruptedException {
        Thread thread = new HelloThread();
        thread.start();
        thread.join();
    }  
    
    • 已经 deprecated 的方法
    • public final void stop()
    • public final void suspend()
    • public final void resume()

    stop() 会引起线程涉及的 monitor 状态的不确定性,所以不建议被调用,那么怎么 stop 一个 Thread 啊,懵逼了吧。

    The Answer: In Java there's no clean, quick or reliable way to stop a thread.
    Instead, Threads rely on a cooperative mechanism called Interruption. This means that Threads could only signal other threads to stop, not force them to stop.

    • 一种办法是通过一个简单的 flag 变量来控制
    public class Task implements Runnable {
        private volatile boolean isRunning = true;
    
        public void run() {
            while (isRunning) {
                //do work
            }
        }
    
        public void kill() {
            isRunning = false;
        }
    }
    
    • 类似还有通过 interrupt 来中断线程
    public class Task1 implements Runnable {
      public void run() {
                while (!Thread.currentThread().isInterrupted()) {
                          // do work
               }
        }
    }
    // in other thread, call interrupt()
    myThread.interrupt();
    
    • 如果run里面不判断 Thread.currentThread().isInterrupted() 直接执行 interrupt() 会怎样呢?哪位读者来回答一下 _

    • 多线程的共享变量

    • 不同执行流可以访问和操作相同的变量

    • 不同执行流可以执行相同的程序代码

    • 当多条执行流执行相同的程序代码时,每条执行流都有单独的栈,方法中的参数和局部变量都有自己的一份

    • [内存可见性] 多个线程可以共享访问和操作相同的变量,但一个线程对一个共享变量的修改,另一个线程不一定马上就能看到,甚至永远也看不到。原因:在计算机系统中,除了内存,数据还会被缓存在CPU的寄存器以及各级缓存中,当访问一个变量时,可能直接从寄存器或CPU缓存中获取,而不一定到内存中去取,当修改一个变量时,也可能是先写到缓存中,而稍后才会同步更新到内存中。

    • 多线程的成本

    • 创建开销(空间和时间):每个线程创建必要的数据结构、栈、程序计数器。

    • 线程切换开销(时间):上下文切换,这个切换不仅耗时,而且使CPU中的很多缓存失效,是有成本的。

    • 如果执行的任务都是CPU密集型的,那创建超过CPU数量的线程是没有必要的,并不会加快程序的执行。

    • IO密集型的,创建超过 CPU 核数的线程是会提高效率的(线程数到多少合适呢?Netty的源码里有相关的解说)


    新加坡的“南京大排档”

    相关文章

      网友评论

          本文标题:读书也要记:Java 线程(基础)

          本文链接:https://www.haomeiwen.com/subject/topxnttx.html