bilibili尚硅谷周阳老师JUC并发编程与源码分析课程笔记第一章——线程基础知识复习

线程基础知识复习

JUC是什么?

java.util.concurrent在并发编程中使用的工具包

在这里插入图片描述

为什么学习并用好多线程极其重要?

硬件方面

摩尔定律:
它是由英特尔创始人之一Gordon Moore(戈登·摩尔)提出来的。其内容为:
当价格不变时,集成电路上可容纳的元器件的数目约每隔18-24个月便会增加一倍,性能也将提升一倍。
换言之,每一美元所能买到的电脑性能,将每隔18-24个月翻一倍以上。这一定律揭示了信息技术进步的速度。

可是从2003年开始CPU主频已经不再翻倍,而是采用多核而不是更快的主频。

摩尔定律失效了。。。

在主频不再提高且核数在不断增加的情况下,要想让程序更快就要用到并行或并发编程。

软件方面

面试B格可以高一点点

充分利用多核处理器

提高程序性能,高并发系统

提高程序吞吐量,异步+回调等生产需求

弊端及问题

线程安全性问题,例如:i++是非原子操作、集合类安全否

线程锁问题,例如:synchronized锁,但是是重量级锁

线程性能问题,例如:了解偏向锁、轻量级锁

start线程开启源码分析

package com.fastech.juc;

public class ThreadBaseDemo {

    public static void main(String[] args) {
        Thread t1 = new Thread(() -> {}, "t1");
        t1.start();
    }

}

查询start()方法源码

public synchronized void start() {
    if (threadStatus != 0)
        throw new IllegalThreadStateException();

    group.add(this);

    boolean started = false;
    try {
        start0();
        started = true;
    } finally {
        try {
            if (!started) {
                group.threadStartFailed(this);
            }
        } catch (Throwable ignore) {
            /* do nothing. If start0 threw a Throwable then
              it will be passed up the call stack */
        }
    }
}

// native表示JNI本地接口调用,调的是第三方C语言编写的底层函数,或者是操作系统的底层代码
// JNI代表Java native interface,Java本地接口
private native void start0();

Java语言本身底层就是C++语言,OpenJDK就是指C++源语级别对Java语言的描述,OpenJDK源码网址:https://openjdk.org/

start0()方法具体做了哪些操作,其实就是OpenJDK源码里面的openjdk8\jdk\src\share\native\java\lang\thread.c、openjdk8\hotspot\src\share\vm\prims\jvm.cpp、openjdk8\hotspot\src\share\vm\runtime\thread.cpp三个类

OpenJDK和JNI一般是一一对应的,Thread.java对应就是thread.c,start0其实就是JVM_StartThread。此时查看源代码可以看到在jvm.h中找到了声明,jvm.cpp中有实现

thread.c

在这里插入图片描述
jvm.cpp

在这里插入图片描述

在这里插入图片描述

thread.cpp

在这里插入图片描述

如上图,可以看到start0()这个方法实际上是通过jvm配合操作系统分配线程帮助我们启动线程,os::start_thread(thread);代表由操作系统分配原生的基础线程

Java多线程相关概念

1把锁

synchronized(后面细讲)

2个并

  • 并发(concurrent):是在同一实体上的多个事件,是在一台机器上“同时”处理多个任务,同一时刻,其实是只有一个事情在发生。
    • 例如:抢票、秒杀,这种同一时刻多个请求线程对同一份资源发起集火攻击称为并发
  • 并行(parallel):是在不同实体上的多个事件,是在多台处理器上同时处理多个任务,同一时刻,大家都在做事情,你做你的,我做我的,各干各的。
    • 例如:泡面,买包泡面回家,一边用热水器烧水,一边撕开调料包泡面,这种同一时刻,大家都在做事情,你做你的,我做我的,各干各的称为并行

在这里插入图片描述

如图,两个队列一台咖啡机即并发,两个队列两台咖啡机即并行

3个程

  • 进程:简单的说,在系统中运行的一个应用程序就是一个进程,每个进程都有它自己的内存空间和系统资源

  • 线程:也被称为轻量级进程,在同一个进程内会有1个或多个线程,是大多数操作系统进行时序调度的基本单元。

  • 管程:

    • Monitor(监视器),也就是我们平时所说的锁。
  • Monitor其实是一种同步机制,它的义务是保证(同一时间)只有一个线程可以访问被保护的数据和代码。

  • JVM中同步是基于进入和退出监视器(Monitor管程对象)来实现的,每个对象实例都会有一个Monitor对象。即每个对象都可以作为Monitor(监视器)、都可以作为管程、都可以作为锁

  • Monitor对象和Java对象一同创建并销毁,底层由C++语言实现。

  Object o = new Object();
  new Thread(() -> {
    synchronized (o) // 此时o这个对象作为Monitor(监视器),即锁
    {

    }
  }, "t2").start();

ps:管程的概念来自于书籍——JVM第3版

在这里插入图片描述

用户守护线程理论

线程分类

Java线程分为用户线程和守护线程,一般不做特别说明配置,默认都是用户线程

  • 用户线程(User Thread):是系统的工作线程,它会完成这个程序需要完成的业务操作。
  • 守护线程(Daemon Thread):
    • 是一种特殊的线程为其他线程服务的,在后台默默地完成一些系统性的任务,比如垃圾回收线程就是最典型的例子。
    • 守护线程作为一个服务线程,没有服务对象就没有必要继续运行了,如果用户线程全部结束了,意味着程序需要完成的业务操作已经结束了,系统可以退出了。所以假如当系统只剩下守护线程的时候,守护线程伴随着JVM一同结束工作。

线程的daemon属性

源码解读
/**
 * 返回值:true表示守护线程;false表示用户线程
 */
public final boolean isDaemon() {
    return daemon;
}

/**
 * 将此线程标记为守护线程或用户线程,当所有运行的线程都是守护线程时,Java虚拟机退出
 * 参数:true表示将此线程标记为守护线程;false表示将此线程标记为用户线程
 */
public final void setDaemon(boolean on) {
    checkAccess();
    if (isAlive()) {
        throw new IllegalThreadStateException();
    }
    daemon = on;
}
代码演示
package com.fastech.juc;

import java.util.concurrent.TimeUnit;

public class DaemonDemo {

    public static void main(String[] args) {
        Thread t1 = new Thread(() -> {
            System.out.println(Thread.currentThread().getName() + "\t 开始运行," +
                    (Thread.currentThread().isDaemon() ? "守护线程" : "用户线程"));
            while (true) {

            }
        }, "t1");

        t1.start();

        try {
            TimeUnit.SECONDS.sleep(3);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        System.out.println(Thread.currentThread().getName() + "\t ---end 主线程");
    }

}

输出结果:
t1	 开始运行,用户线程
main	 ---end 主线程
... 	// 程序未停止
结论

1、一般不做特别说明配置,默认都是用户线程

2、main线程结束不代表t1线程结束,这两个线程都是用户线程,相互独立

代码修改1
package com.fastech.juc;

import java.util.concurrent.TimeUnit;

public class DaemonDemo {

    public static void main(String[] args) {
        Thread t1 = new Thread(() -> {
            System.out.println(Thread.currentThread().getName() + "\t 开始运行," +
                    (Thread.currentThread().isDaemon() ? "守护线程" : "用户线程"));
            while (true) {

            }
        }, "t1");

        t1.setDaemon(true);

        t1.start();

        try {
            TimeUnit.SECONDS.sleep(3);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        System.out.println(Thread.currentThread().getName() + "\t ---end 主线程");
    }

}

输出结果:
t1	 开始运行,守护线程
main	 ---end 主线程
结论

1、t1作为守护线程默默守护着用户线程,用户线程结束守护线程也就结束了

代码修改2
package com.fastech.juc;

import java.util.concurrent.TimeUnit;

public class DaemonDemo {

    public static void main(String[] args) {
        Thread t1 = new Thread(() -> {
            System.out.println(Thread.currentThread().getName() + "\t 开始运行," +
                    (Thread.currentThread().isDaemon() ? "守护线程" : "用户线程"));
            while (true) {

            }
        }, "t1");

        t1.start();

        t1.setDaemon(true);

        try {
            TimeUnit.SECONDS.sleep(3);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        System.out.println(Thread.currentThread().getName() + "\t ---end 主线程");
    }

}

输出结果:
t1	 开始运行,用户线程
Exception in thread "main" java.lang.IllegalThreadStateException
	at java.lang.Thread.setDaemon(Thread.java:1359)
	at com.fastech.juc.DaemonDemo.main(DaemonDemo.java:24)
结论

1、setDaemon(true)方法必须在start()之前设置,否则报IllegalThreadStateException异常

理由

因为你不能把正在运行的常规线程设置为守护线程

小总结

如果用户线程全部结束意味着程序需要完成的业务操作已经结束了,守护线程随着JVM一同结束工作

  • 18
    点赞
  • 25
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值