并发编程之多线程基础(一)

java 同时被 2 个专栏收录
57 篇文章 0 订阅
2 篇文章 0 订阅

一、线程和进程

1.1. 什么是进程和线程

进程 :进程是受操作系统管理的基本运行单元;进程是操作系统的结构基础;他是系统进程资源分配和调度的一个独立单元。

线程 :线程可以理解为进程独立运行的子任务;线程是异步的,被调度的时机是随机的。线程是CPU调度的最小单元

1.2. 线程的优缺点

优点 :在进程内创建、终止线程比创建、终止进程要快;同一进程内的线程间切换比进程间的切换要快,尤其是用户级线程间的切换;程序的运行效率可能会提高。

缺点 :如果有大量的线程,会影响性能,因为操作系统需要在它们之间切换;更多的线程需要更多的内存空间;线程中止需要考虑对程序运行的影响;通常块模型数据是在多个线程间共享的,需要防止线程死锁情况的发生。

二、串行、并行和并发

2.1. 三者的含义

串行: 执行一个任务,在执行一个任务

并发: 在单核CPU中,轮流(时间片)处理多个任务,但是同一时刻,只有一个线程在执行。

并行: 在多核CPU中(多核CPU也会出现并发的情况),同时处理多个任务,在同一时间内,有个多线程在执行。

2.2. 计算机和多线程

单核CPU: 对于单核来说实现多线程主要是依赖操作系统内核的进程调度算法(也可以说是线程),例如有三个线程运行,操作系统会让单核CPU轮流运行这些线程,每个线程执行一个固定的时间片,但是由于CPU切换频率太,在我们的认知上认为这三个线程是同时运行的。但这其中由于操作系统需要频繁的切换线程,处理时间可能比串行的时候花费时间长,这就会出现多线程导致执行效率,但是从另一方面确实减少了用户响应时间。

在这里插入图片描述

多核CPU :对于多核CPU来说,虽然进程是操作系统进行资源分配和调度的一个独立单元,但是进程中包含的一系列的线程,线程是CPU调度和分配的基本单位。现在我们的笔记本是4核8线程/8核16线程(这里的4核、8核表示的是真正和物理核心,8线程、16线程是通过超线程技术,用一个物理核模拟两个虚拟核,但是操作系统看来就是8核心/16核心,通过超线程技术可以实现单个物理核实现线程级别的并行计算,性能和真实两核差距还是比较大的),这时会把线程1234分配到线程1234,其他的线程现在就需要等待分配;这个时候对于1234线程就是并行的,但是其他的线程就会先出现并发的情况。

在这里插入图片描述

详细解析文章链接: 多CPU/多核/多进程/多线程/并发/并行之间的关系

2.3. 多核CPU与内存

为了提高程序运行的性能,现代CPU在很多方面会对程序进行优化。CPU的处理速度是很快的,内存的速度次之,硬盘速度最慢。在cpu处理内存数据中,内存运行速度太慢,就会拖累cpu的速度。为了解决这样的问题,cpu设计了多级缓存策略。

CPU分为三级缓存: 每个CPU都有L1,L2缓存,但是L3缓存是多核公用的。

L1 Cache: CPU第一层高速缓存,分为数据缓存和指令缓存。它是封装在CPU芯片内部的高速缓存,用于暂时存储CPU运算时的部分指令和数据,存取速度与CPU主频相近。内置的L1高速缓存的容量和结构对CPU的性能影响较大,一级缓存容量越大,则CPU处理速度就会越快,对应的CPU价格也就越高。

L2 Cache: CPU外部的高速缓存,由于L1高速缓存的容量限制,为了再次提高CPU的运算速度,在CPU外部放置一高速存储器,即二级缓存。像一级缓存一样,二级缓存越大,则CPU处理速度就越快,整台计算机性能也就越好。一级缓存和二级缓存都位于CPU和内存之间,用于缓解高速CPU与慢速内存速度匹配问题。

L3 Cache: 它的作用是进一步降低内存延迟,同时提升大数据量计算时处理器的性能。具有较大L3缓存的处理器,能提供更有效的文件系统缓存行为及较短的消息和队列长度。一般多核共享一个L3缓存。

CPU查找数据的顺序为: CPU -> L1 -> L2 -> L3 -> 内存 -> 硬盘
在这里插入图片描述

推荐一个写的不错的博客文章:多线程之CPU多核缓存架构与内存屏障

2.3. 总结

多核CPU对于内存控制,我们还需要了解缓冲一致性协议,CPU性能优化的运行时指令重排序以及内存屏障。而Java的JMM内存模型就是建立在此基础上。

三、创建线程

创建线程有两种方式,一种是通过继承Thread类,重写run方法;另一种是实现Runnable接口,实现Run方法。

3.1. 第一种

class ThreadChile extends Thread {
    @Override
    public void run() {
        super.run();
        System.out.println("子线程 :" + Thread.currentThread().getName());
    }
}
public class Thread1 {

    public static void main(String[] args) {
        ThreadChile chile = new ThreadChile();
        chile.start();
        System.out.println("主线程 :" + Thread.currentThread().getName());
    }
}

3.2. 第二种(推荐)

class ThreadChild2 implements Runnable{

    @Override
    public void run() {
        for (int i = 0; i < 5; i++) {
            System.out.println("子线程 . " + Thread.currentThread().getName() + " 输出 " + i);
        }
    }
}
/**
 * @author long
 */
public class Thread3 {
    public static void main(String[] args) {
        Thread child2 = new Thread(new ThreadChild2());
        child2.setName("Thread-child");
        child2.start();
        for (int i = 0; i < 5; i++) {
            System.out.println("主线程 . " + Thread.currentThread().getName() + " 输出 " + i);
        }
    }
}

实现Runnable接口所具有的优势:

  • 避免Java单继承的问题

  • 适合多线程处理同一资源

  • 代码可以被多线程共享,数据独立,很容易实现资源共享

  • 线程池只能放入实现Runable或callable类线程,不能直接放入继承Thread的类

3.3. 其他

我们也可以通过lambda和匿名内部类创建线程,这是上面的两种方式的变形:

第一个:lambda创建线程

public class Thread4 {
    public static void main(String[] args) {
        new Thread(() -> {
            for (int i = 0; i < 5; i++) {
                System.out.println("子线程 . " + Thread.currentThread().getName() + " 输出 " + i);
            }
        }).start();
        for (int i = 0; i < 5; i++) {
            System.out.println("主线程 . " + Thread.currentThread().getName() + " 输出 " + i);
        }
    }
}

第二个:匿名内部类

public class Thread5 {

    public static void main(String[] args) {
        new Thread(new Runnable() {
            @Override
            public void run() {
                for (int i = 0; i < 10; i++) {
                    System.out.println("基于子类的方式 => 子线程 . " + Thread.currentThread().getName() + " 输出 " + i);
                }
            }
        }).start();

        new Thread() {
            @Override
            public void run() {
                for (int i = 0; i < 10; i++) {
                    System.out.println("基于接口的实现 => 子线程 . " + Thread.currentThread().getName() + " 输出 " + i);
                }
            }
        }.start();
    }

}

3.4. 衍生方式

下面介绍的方式,本质上还是上面两种的衍生版本,真正创建线程方式只有两种!但是我们会发现不管是继承Thread类还是实现Runnable接口,都有两个问题:

  • 第一个是无法抛出更多的异常;

  • 第二个是线程执行完毕之后并无法获得线程的返回值

第一种方法:实现Callable接口,并结合Future实现。

  • 定义一个Callable的实现类,并实现call方法,这个call方法是带返回值的

  • 接着通过FutureTask的构造方法,把这个Callable实现类传进去

  • 然后TutureTask作为Thread类target,创建Thread线程对象

  • 通过FutureTask的get方法获取线程的执行结果

/**
 * @author yuelong
 */
public class ThreadCall implements Callable<Integer> {

    @Override
    public Integer call() throws Exception {
        System.out.println(Thread.currentThread().getName() + "......");
        Thread.sleep(1000);
        return new Random().nextInt(100);
    }

    public static void main(String[] args) throws ExecutionException, InterruptedException {
        FutureTask<Integer> task = new FutureTask<>(new ThreadCall());
        new Thread(task).start();
        Integer integer = task.get();
        System.out.println(integer);
    }

}

第二种是通过线程池创建线程,此处JDK自带的Exectors来创建线程池对象。

这里需要定义一个Runnable接口的实现类,接着创建固定数量的线程池,最后通过ExecutorSerivce对象execute方法传入线程对象执行。

/**
 * @author yuelong
 */
public class ThreadPool implements Runnable {
    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName() + "正在执行......");
    }

    public static void main(String[] args) {
        ExecutorService newFixedThreadPool = Executors.newFixedThreadPool(5);
        for (int i = 0; i < 10; i++) {
            newFixedThreadPool.execute(new ThreadPool());
        }
        newFixedThreadPool.shutdown();
    }
}

3.5. run和start方法的区别

我们先看一个start函数的源码:这个方法是Thread类的方法,用来异步启动一个线程,然后主动立即返回。该启动的线程不会马上运行,会放到等待队列中等待CPU调度,只有线程真正被CPU调度用run()方法执行。

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 */
          }
      }
  }

  private native void start0();

接着run方法的源码:这个方式是实现Runnable的run方法,

@Override
public void run() {
    if (target != null) {
        target.run();
    }
}

总结来说:调用start方法方可启动线程,而run方法只是thread的一个普通方法调用,还是在主线程里执行。

3.6. 总结

创建线程只有两种方法:实现Runnable和继承Thread类重写run方法。这两个方法最终都会调用Thread.start方法,而start方法最终会调用run方法。所以本质上来说创建线程的方法只有一种,就是构建一个Thread类。

四、线程的生命周期

线程一共有五种状态:新建(New)、就绪(Runnable)、运行(Running)、阻塞(Bolocked)和死亡(Dead)。

4.1. 线程状态转换

在这里插入图片描述

4.2. 状态详解

新建(New):代码通过new Thread()创建一个线程之后,该线程就处于新建状态,仅仅由Java虚拟机为其分配内存,并初始化其成员变量的值。不会执行线程的线程执行体。

就绪(Runnable):线程对象调用start()方法后,该线程就处于就绪状态。但是线程并没有开始运行,只是表示可以运行,线程何时运行取决于JVM中线程调度其的调度。

运行(Running):处于就绪状态的线程获得CPU时间片,开始执行run方法的线程执行体,则该线程处于运行状态,这里需要注意线程只能从就绪状态进入到运行状态。

阻塞(Boloked):线程因为某种原因放弃了CPU的使用权,暂时停止运行,知道线程进入就绪状态,才有机会转到运行状态。

死亡(Dead):当run或者call方法执行完成,线程正常结束;线程抛出一个未捕获的Exception或者Error;直接调用该线程的stop()方法来结束该线程,但是容易造成死锁(11已经删除该方法)

五、线程常用方法

5.1. 静态方法

方法名备注
activeCount返回当前执行的线程所在的线程组中活动的线程数目
currentThread返回当前正在执行的线程
holdsLock返回当前执行的线程是否持有指定对象的锁
interrupted返回当前执行的线程是否已经被中断
sleep使当前执行的线程睡眠多少毫秒
yieId使当前执行的线程自愿暂时放弃对处理器的使用权并允许其他线程执行

5.2. 实例方法

函数名备注
getId返回该线程的Id
getName返回线程的名字
getPriority返回该线程的优先级
getState返回该线程状态
interrupt使线程中断
isInterrupted返回该线程是否被中断
isAlive返回该线程是否处于活动状态
isDaemon返回该线程是否为守护线程
join等待该线程终止
start使该线程开始执行
toString返回线程的信息
setDaemon将该线程标记为守护线程或者用户线程
setName设置线程的名字
setPriority改变线程的优先级

六、守护线程

6.1. 守护线程和用户线程

Java中通常有两种线程:用户线程守护线程(也被称为服务线程)。

通常情况下,我们使用Thread创建的线程在默认情况下都属于用户线程。

通过Thread.setDaemon(false)设置为用户线程,通过Thread.setDaemon(true)设置为守护线程线程。

属性的设置要在线程启动之前,否则会报IllegalThreadStateException异常。

6.2. 守护线程特点

  1. 程序中的所有的用户线程结束之后,不管守护线程处于什么状态,java虚拟机都会自动退出;

  2. 调用线程的实例方法setDaemon()来设置线程是否是守护线程;

  3. setDaemon()方法必须在线程的start()方法之前调用,在后面调用会报异常,并且不起效;

  4. 在守护线程中启动的子线程也是守护线程。

6.3. 守护线程的使用场景

针对于守护线程的特点,java 守护线程通常可用于开发一些为其它用户线程服务的功能。比如说心跳检测,事件监听等。Java 中最有名的守护进程当属GC(垃圾回收)

七、线程优先级

Java 中的线程优先级的范围是1~10,默认的优先级是5。10级最高。

在一个线程中开启另外一个新线程,则新开线程称为该线程的子线程,子线程初始优先级与父线程相同。

线程的优先级是为了在多线程环境中便于系统对线程的调度,优先级越高先执行机会越大,并不是一定先执行。

  • 0
    点赞
  • 1
    评论
  • 0
    收藏
  • 一键三连
    一键三连
  • 扫一扫,分享海报

相关推荐
<p> <b><span style="background-color:#FFE500;">【超实用课程内容】</span></b> </p> <p> <br /> </p> <p> <br /> </p> <p> 本课程内容包含讲解<span>解读Nginx的基础知识,</span><span>解读Nginx的核心知识、带领学员进行</span>高并发环境下的Nginx性能优化实战,让学生能够快速将所学融合到企业应用中。 </p> <p> <br /> </p> <p style="font-family:Helvetica;color:#3A4151;font-size:14px;background-color:#FFFFFF;"> <b><br /> </b> </p> <p style="font-family:Helvetica;color:#3A4151;font-size:14px;background-color:#FFFFFF;"> <b><span style="background-color:#FFE500;">【课程如何观看?】</span></b> </p> <p style="font-family:Helvetica;color:#3A4151;font-size:14px;background-color:#FFFFFF;"> PC端:<a href="https://edu.csdn.net/course/detail/26277"><span id="__kindeditor_bookmark_start_21__"></span></a><a href="https://edu.csdn.net/course/detail/27216">https://edu.csdn.net/course/detail/27216</a> </p> <p style="font-family:Helvetica;color:#3A4151;font-size:14px;background-color:#FFFFFF;"> 移动端:CSDN 学院APP注意不是CSDN APP哦 </p> <p style="font-family:Helvetica;color:#3A4151;font-size:14px;background-color:#FFFFFF;"> 本课程为录播课,课程永久有效观看时长,大家可以抓紧时间学习后起讨论哦~ </p> <p style="font-family:"color:#3A4151;font-size:14px;background-color:#FFFFFF;"> <br /> </p> <p class="ql-long-24357476" style="font-family:"color:#3A4151;font-size:14px;background-color:#FFFFFF;"> <strong><span style="background-color:#FFE500;">【学员专享增值服务】</span></strong> </p> <p class="ql-long-24357476" style="font-family:"color:#3A4151;font-size:14px;background-color:#FFFFFF;"> <b>源码开放</b> </p> <p class="ql-long-24357476" style="font-family:"color:#3A4151;font-size:14px;background-color:#FFFFFF;"> 课件、课程案例代码完全开放给你,你可以根据所学知识,自行修改、优化 </p> <p class="ql-long-24357476" style="font-family:"color:#3A4151;font-size:14px;background-color:#FFFFFF;"> 下载方式:电脑登录<a href="https://edu.csdn.net/course/detail/26277"></a><a href="https://edu.csdn.net/course/detail/27216">https://edu.csdn.net/course/detail/27216</a>,播放页面右侧点击课件进行资料打包下载 </p> <p> <br /> </p> <p> <br /> </p> <p> <br /> </p>
<p><strong><span style="color: #337fe5;">[JAVA工程师必会知识点并发编程]</span></strong></p> <p style="font-size: 14px; background-color: #ffffff;"> </p> <p style="color: #008781;"> </p> <p class="ql-long-24357476" style="color: #222226; font-family: "font-size:14px; background-color: #ffffff;"> </p> <p class="MsoNormal"><strong><span style="color: #000000;">1、现在几乎</span><span style="color: #000000;">100%</span><span style="color: #000000;">的公司面试都必须面试并发编程,尤其是互联网公司,对于并发编程的要求更高,并发编程能力已经成为职场敲门砖。</span></strong></p> <p class="MsoNormal"><strong><span style="color: #000000;">2</span><span style="color: #000000;">、现在已经是移动互联和大数据时代,对于应用程序的性能、处理能力、处理时效性要求更高了,传统的串行化编程无法充分利用现有的服务器性能。</span></strong></p> <p class="MsoNormal"><strong><span style="color: #000000;">3</span><span style="color: #000000;">、并发编程是几乎所有框架的底层基础,掌握好并发编程更有利于我们学习各种框架。想要让自己的程序执行、接口响应、批处理效率更高,必须使用并发编程。</span></strong></p> <p class="MsoNormal"><strong><span style="color: #000000;">4、并发编程是中高级程序员的标配,是拿高薪的必备条件。</span></strong></p> <p> </p> <p><span style="color: #337fe5;">【主讲讲师】</span></p> <p><span style="color: #337fe5;"><span style="color: #000000;">尹洪亮Kevin:</span><br /><span style="color: #000000;">现任职某互联网公司首席架构师,负责系统架构、项目群管理、产品研发工作。</span><br /><span style="color: #000000;">10余年软件行业经验,具有数百个线上项目实战经验。</span><br /><span style="color: #000000;">擅长JAVA技术栈、高并发高可用伸缩式微服务架构、DevOps。</span><br /><span style="color: #000000;">主导研发的蜂巢微服务架构已经成功支撑数百个微服务稳定运行</span><br /><br /></span></p> <p style="color: #008781;"><strong><span style="color: #337fe5;">【推荐你学习这门课的理由:</span><span style="color: #e53333;">知识体系完整+丰富学习资料】</span></strong></p> <p style="margin-left: 18pt; text-indent: -18pt; background: white;">1<span style="color: #000000;">、 本课程总计122课时,由五大体系组成,目的是让你次性搞定并发编程。分别是并发编程基础、进阶、精通篇、Disruptor高并发框架、RateLimiter高并发访问限流吗,BAT员工也在学。</span></p> <p style="color: #008781; background: white;"><span style="color: #000000;">2、课程附带附带3个项目源码,几百个课程示例,5个高清PDF课件。</span></p> <p style="color: #008781; background: white;"><span style="color: #000000;">3、本课程0基础入门,从进程、线程、JVM开始讲起,每个章节只专注于个知识点,每个章节均有代码实例。</span></p> <p style="color: #008781; background: white;"><span style="color: #000000;"> </span></p> <p style="color: #008781; background: white;"><span style="color: #337fe5;">【课程分为基础篇、进阶篇、高级篇】</span></p> <p style="color: #008781; background: white;"><span style="color: #337fe5;">基础篇<br /></span></p> <p style="color: #008781; background: white;"><span style="color: #000000;">基础篇从进程与线程、内存CPU时间片轮训讲起,包含线程的3种创建方法、可视化观察线程、join、sleep、yield、interrupt,Synchronized、重入锁、对象锁、类锁、wait、notify、线程上下文切换、守护线程、阻塞式安全队列等内容。</span></p> <p style="color: #008781; background: white;"><span style="color: #337fe5;">二、进阶篇</span></p> <p style="color: #008781; background: white;"><span style="color: #000000;">进阶篇课程涵盖volatied关键字、Actomic类、可见性、原子性、ThreadLocal、Unsafe底层、同步类容器、并发类容器、5种并发队列、COW容器、InheritableThreadLocal源码解析等内容。</span></p> <p style="color: #008781; background: white;"><span style="color: #337fe5;">三、精通篇</span></p> <p style="color: #008781; background: white;"><span style="color: #000000;">精通篇课程涵盖JUC下的核心工具类,CountDownLath、CyclicBarrier、Phaser、Semaphore、Exchanger、ReentrantLock、ReentrantReadWriteLock、StampedLock、LockSupport、AQS底层、悲观锁、乐观锁、自旋锁、公平锁、非公平锁、排它锁、共享锁、重入锁、线程池、CachedThreadPool、FixedThreadPool、ScheduledThreadPool、SingleThreadExecutor、自定义线程池、ThreadFactory、线程池切面编程、线程池动态管理等内容,高并发设计模式,Future模式、Master Worker模式、CompletionService、ForkJoin等</span></p> <p style="color: #008781; background: white;"><span style="color: #337fe5;">课程中还包含</span></p> <p style="color: #008781; background: white;"><span style="color: #000000;">Disruptor高并发无锁框架讲解:Disruptor支持每秒600万订单处理的恐怖能力。深入到底层原理和开发模式,让你又懂又会用。</span></p> <p style="color: #008781; background: white;"><span style="color: #000000;">高并发访问限流讲解:涵盖木桶算法、令牌桶算法、Google RateLimiter限流开发、Apache JMeter压力测试实战。</span></p> <p> </p> <p class="ql-long-24357476" style="color: #222226; font-family: "font-size:14px; background-color: #ffffff;"><strong><span style="color: #337fe5;">【学完后我将达到什么水平?】</span></strong></p> <p class="ql-long-24357476" style="color: #222226; font-family: "font-size:14px; background-color: #ffffff;"><span style="color: #000000; font-family: ";">1、 吊打并发编程相关的笔试题、面试题。</span></p> <p class="ql-long-24357476" style="color: #222226; font-family: "font-size:14px; background-color: #ffffff;"><span style="color: #000000; font-family: ";">2、 重构自己并发编程的体系知识,不再谈并发色变。</span></p> <p class="ql-long-24357476" style="color: #222226; font-family: "font-size:14px; background-color: #ffffff;"><span style="color: #000000; font-family: ";">3、 精准掌握</span><span style="color: #000000; font-family: ";">JAVA</span><span style="color: #000000; font-family: ";">各种并发工具类、方法、关键字的原理和使用。</span></p> <p class="ql-long-24357476" style="color: #222226; font-family: "font-size:14px; background-color: #ffffff;"><span style="color: #000000; font-family: ";">4、 轻松上手写出更高效、更优雅的并发程序,在工作中能够提出更多的解决方案。</span></p> <p class="MsoNoSpacing" style="margin-left: 18pt; text-indent: -18pt;"><span style="color: #000000;"> </span></p> <p class="MsoListParagraph" style="margin-left: 18.0pt; text-indent: -18.0pt;"> </p> <p> </p> <p class="ql-long-24357476" style="color: #008781;"> </p> <p class="MsoListParagraph" style="color: #008781; margin-left: 36pt; text-indent: -36pt;" align="left"><strong><span style="color: #337fe5;">【</span><span style="color: #337fe5;">面向人群</span><span style="color: #337fe5;">】</span></strong></p> <p class="MsoListParagraph" style="color: #008781; margin-left: 36pt; text-indent: -36pt;" align="left"><span style="color: #000000;">1、 总感觉并发编程很难、很复杂、不敢学习的人群。</span></p> <p class="MsoListParagraph" style="color: #008781; margin-left: 36pt; text-indent: -36pt;" align="left"><span style="color: #000000;">2、 准备跳槽、找工作、拿高薪的程序员。</span></p> <p class="MsoListParagraph" style="color: #008781; margin-left: 36pt; text-indent: -36pt;" align="left"><span style="color: #000000;">3、 希望提高自己的编程能力,开发出更高效、性能更强劲系统的人群。</span></p> <p class="MsoListParagraph" style="color: #008781; margin-left: 36pt; text-indent: -36pt;" align="left"><span style="color: #000000;">4、 想要快速、系统化、精准掌握并发编程的人群。</span></p> <p class="MsoListParagraph" style="color: #008781; margin-left: 18pt; text-indent: -18pt;"><strong>【课程知识体系图】</strong></p> <p class="MsoListParagraph" style="color: #008781; margin-left: 18pt; text-indent: -18pt;"><img src="https://img-bss.csdnimg.cn/202007100721287398.png" alt="" /></p>
©️2020 CSDN 皮肤主题: 撸撸猫 设计师:马嘣嘣 返回首页
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值