了解Kotlin协程你需要加深了解的 Java 线程

协程铺垫知识 —— JAVA 线程

Question:

  1. 线程和进程的区别?
  2. cpu 个数、cpu 核心数、cpu 线程数?
  3. 并发和并行的区别?
  4. 内核态线程和用户态线程是什么?
  5. 守护线程和用户线程?
  6. 线程的状态有哪些?如何转换这些状态?
  7. 为什么会有线程阻塞?
  8. 线程池是啥捏?
  9. 如何获得线程结果?(Future、Callable)

1. 线程和进程的区别

进程和线程的主要差别在于它们是不同的操作系统资源管理方式。进程有独立的地址空间,一个进程崩溃后,在保护模式下不会对其它进程产生影响,而线程只是一个进程中的不同执行路径。

线程有自己的堆栈和局部变量,但线程之间没有单独的地址空间,在进程切换时,耗费资源较大,效率要差一些。但对于一些要求同时进行并且又要共享某些变量的并发操作,只能用线程,不能用进程。

1) 简而言之,一个程序至少有一个进程,一个进程至少有一个线程.

  1. 线程的划分尺度小于进程,使得多线程程序的并发性高。

  2. 另外,进程在执行过程中拥有独立的内存单元,而多个线程共享内存,从而极大地提高了程序的运行效率。别把它和栈内存搞混,每个线程都拥有单独的栈内存用来存储本地数据。

  3. 线程在执行过程中与进程还是有区别的。每个独立的线程有一个程序运行的入口、顺序执行序列和程序的出口。但是线程不能够独立执行,必须依存在应用程序中,由应用程序提供多个线程执行控制。

  4. 从逻辑角度来看,多线程的意义在于一个应用程序中,有多个执行部分可以同时执行。但操作系统并没有将多个线程看做多个独立的应用,来实现进程的调度和管理以及资源分配。

线程是属于进程的,线程运行在进程空间内,同一进程所产生的线程共享同一内存空间,当进程退出时该进程所产生的线程都会被强制退出并清除。线程可与属于同一进程的其它线程共享进程所拥有的全部资源,但是其本身基本上不拥有系统资源,只拥有一点在运行中必不可少的信息(如程序计数器、一组寄存器和栈)。

[图片上传失败...(image-a1770c-1559130882083)]

2. cpu 个数、cpu 核心数、cpu 线程数?

在 linux 终端中查看cpuinfo

couinfo.png

下面区别一下cpu、cpu cores、processors

CPU:独立的中央处理单元,体现在主板上是有多个CPU的槽位。

CPU cores:在每一个CPU上,都可能有多个核(core),每一个核中都有独立的一套ALU、FPU、Cache等组件,所以这个概念也被称作物理核

processor:这个主要得益于超线程技术,可以让一个物理核模拟出多个逻辑核,即processor。
简单来说就是,当有多个计算任务时,可以让其中一个计算任务使用ALU的时候,另一个则去使用FPU。
这样就可以充分利用物理核中的各个部件,使得同一个物理核中,也可以并行处理多个计算任务。

  • 多核处理器:一个cpu有多个核心处理器(通过cpu内部总线通信)

  • 多处理器:多个cpu(通过主板上的总线通信)

理论上来说,对于计算密集型的任务,线程数应该和CPU所能提供的并行数一致。那这里的“并行数”应该采取物理核数还是processor数呢?

对于计算密集型的任务,一般建议将线程数设置为物理核数。具体的,还需要针对不同的程序,做对应压力测试得到合适的参数选择。

3. 并发和并行的区别?

并行:同时执行几个任务

并发:轮流执行分时间执行几个任务(看起来像并行)

核数 > 线程数:并行

核数 < 线程数:并发

4. 内核态线程和用户态线程

内核态线程和用户态线程.png

5. 守护线程和用户线程

用户线程即运行在前台的线程,而守护线程是运行在后台的线程。 守护线程作用是为其他前台线程的运行提供便利服务,而且仅在普通、非守护线程仍然运行时才需要,比如垃圾回收线程就是一个守护线程。当 JVM 检测仅剩一个守护线程,而用户线程都已经退出运行时,JVM就会退出,因为没有如果没有了被守护这,也就没有继续运行程序的必要了。如果有非守护线程仍然存活,JVM就不会退出。

守护线程并非只有虚拟机内部提供,用户在编写程序时也可以自己设置守护线程。用户可以用Thread的setDaemon(true)方法设置当前线程为守护线程。

虽然守护线程可能非常有用,但必须小心确保其他所有非守护线程消亡时,不会由于它的终止而产生任何危害。因为你不可能知道在所有的用户线程退出运行前,守护线程是否已经完成了预期的服务任务。一旦所有的用户线程退出了,虚拟机也就退出运行了。 因此,不要在守护线程中执行业务逻辑操作(比如对数据的读写等)。

6. 线程的状态有哪些?如何转换这些状态?

status

7. 为什么会有线程阻塞?

  1. 当线程执行 Thread.sleep()时,它一直阻塞到指定的毫秒时间之后,或者阻塞被另一个线程打断;

  2. 当线程碰到一条 wait()语句时,它会一直阻塞到接到通知 notify()、被中断或经过了指定毫秒时间为止(若制定了超时值的话)。wait、notify需要对对象加同步。

public class ObjectWaitTest {
    public static Object waitObject = new Object();

    public static void notifyAllThread() {
        System.out.println("notifyAllThread");
        synchronized (waitObject) {
            waitObject.notifyAll();
        }
    }
    public static void notifyThread() {
        System.out.println("notifyThread");
        synchronized (waitObject) {
            waitObject.notify();
        }
    }
    static class MyThread extends Thread {
        public Object waitObject = null;
        private boolean isStop = false;

        public MyThread(Object waitObject) {
            this.waitObject = waitObject;
        }

        public void run() {
            while (true) {
                synchronized (waitObject) {
                    if (isStop) {
                        System.out.println(Thread.currentThread().getId() + " is stop");
                        try {
                            waitObject.wait();
                        } catch (InterruptedException e) {
                            // TODO Auto-generated catch block
                            e.printStackTrace();
                        }
                        System.out.println(Thread.currentThread().getId() + " is resume");
                        System.out.println(Thread.currentThread().getId() + " will  exit");
                        throw new RuntimeException(Thread.currentThread().getId() +" exit");
                    }
                }
            }
        }

        public void suspendThread() {
            this.isStop = true;
        }
    }

}

public class Main {
    public static void main(String[] args) {
        ObjectWaitTest.MyThread tm1 = new ObjectWaitTest.MyThread(ObjectWaitTest.waitObject);
        tm1.setName("tm1");
        tm1.start();
        ObjectWaitTest.MyThread tm2 = new ObjectWaitTest.MyThread(ObjectWaitTest.waitObject);
        tm2.setName("tm2");
        tm2.start();
        try {
            Thread.currentThread().sleep(3000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        tm1.suspendThread();
        tm2.suspendThread();
        try {
            Thread.currentThread().sleep(3000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        ObjectWaitTest.notifyAllThread();
//        ObjectWaitTest.notifyThread();
//        try {
//            Thread.currentThread().sleep(3000);
//        } catch (InterruptedException e) {
//            e.printStackTrace();
//        }
//        ObjectWaitTest.notifyThread();
    }

}

运行结果

result.png
  1. 挂起和唤醒线程,suspend() 使线程进入阻塞状态,只有对应的 resume() 被调用的时候,线程才会进入可执行状态。(但这种方式产生死锁的风险很大,因为线程被挂起以后不会释放锁,可能与其他线程、主线程产生死锁,已经被deprecated,不建议使用)

  2. yield() 会使的线程放弃当前分得的cpu时间片,但此时线程任然处于可执行状态,随时可以再次分得cpu时间片。yield() 方法只能使同优先级的线程有执行的机会。调用 yield() 的效果等价于调度程序认为该线程已执行了足够的时间从而转到另一个线程。(暂停当前正在执行的线程,并执行其他线程,且让出的时间不可知)

  3. join()也叫线程加入。是当前线程A调用另一个线程B的join()方法,当前线程转A入阻塞状态,直到线程B运行结束,线程A才由阻塞状态转为可执行状态。

  4. I/O 操作。常见的一种方式是 InputStream 的 read()方法,该方法一直阻塞到从流中读取一个字节的数据为止,它可以无限阻塞,因此不能指定超时时间;I/O 操作是由操作系统完成的,JVM 无法控制。

  5. 线程也可以阻塞等待获取某个对象锁的排他性访问权限(即等待获得 synchronized 语句必须的锁时阻塞)。

注意,并非所有的阻塞状态都是可中断的,以上阻塞状态的前两种可以被中断,后两种不会对中断做出反应。

8. 线程池

一种线程使用模式。线程过多会带来调度开销,进而影响缓存局部性和整体性能。而线程池维护着多个线程,等待着监督管理者分配可并发执行的任务。这避免了在处理短时间任务时创建与销毁线程的代价。线程池不仅能够保证内核的充分利用,还能防止过分调度。可用线程数量应该取决于可用的并发处理器、处理器内核、内存、网络sockets等的数量。 例如,线程数一般取cpu数量+2比较合适,线程数过多会导致额外的线程切换开销。

线程池是需要自定义的。

9. 如何获得线程结果?(Future、Callable)

Future 有 get() :获取异步执行的结果,如果没有结果可用,此方法会阻塞直到异步计算完成。

public class CallableDemon implements Callable<Integer> {
    private int sum;
    public Integer call() throws Exception {
        System.out.println("Callable子线程开始计算啦!");
        Thread.sleep(2000);

        for(int i=0 ;i<5000;i++){
            sum=sum+I;
        }
        System.out.println("Callable子线程计算结束!");
        return sum;
    }
}

public class CallableTest {

    public static void main(String[] args) {
        //创建线程池
        ExecutorService es = Executors.newSingleThreadExecutor();
        //创建Callable对象任务
        CallableDemon calTask=new CallableDemon();
        //提交任务并获取执行结果
        Future<Integer> future =es.submit(calTask);
        //关闭线程池
        es.shutdown();

        //创建线程池
//        ExecutorService es = Executors.newSingleThreadExecutor();
//        //创建Callable对象任务
//        CallableDemon calTask=new CallableDemon();
//        //创建FutureTask
//        FutureTask futureTask=new FutureTask<Integer>(calTask);
//        //执行任务
//        es.submit(futureTask);
//        //关闭线程池
//        es.shutdown();
        try {
            Thread.sleep(2000);
            System.out.println("主线程在执行其他任务");

            if(future.get()!=null){
                //输出获取到的结果
                System.out.println("futureTask.get()-->"+future.get());
            }else{
                //输出获取到的结果
                System.out.println("futureTask.get()未获取到结果");
            }

        } catch (Exception e) {
            e.printStackTrace();
        }
        System.out.println("主线程执行完成");
    }
}

参考文章:

https://juejin.im/entry/5b77c798f265da43296c3b96

https://blog.csdn.net/pange1991/article/details/53860651

http://www.importnew.com/20551.html

https://www.cnblogs.com/zhaoyan001/p/7049627.html

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,921评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,635评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,393评论 0 338
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,836评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,833评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,685评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,043评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,694评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 42,671评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,670评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,779评论 1 332
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,424评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,027评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,984评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,214评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,108评论 2 351
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,517评论 2 343