JUC并发编程——多线程入门

简介: JUC并发编程——多线程入门

正文


一、为什么要有多线程


         随着计算机的发展,单核的CPU发展到多核的CPU,CPU的性能越来越高,为了充分发挥CPU的计算性能和提高CPU硬件资源的利用率于是在进程的基础上演变出了多线程。使用多线程可以提高程序效率,快速响应给客户端,给用户更加好的体验,每个线程之间并行执行互不影响。


二、名词解释


1、进程和线程


进程:进程是一个具有独立功能的程序(例如 QQ、微信、酷狗等应用),进程是系统进行资源分配和调度的一个独立单位。那么什么是java程序的进程呢?Java编写的程序都运行在Java虚拟机(JVM)中,每当使用Java命令启动一个Java应用程序时,就会启动一个JVM进程,这个应用就是一个JAVA进程。


线程:线程是进程的一个实体,是指“进程代码段”的一次顺序执行流程。线程是CPU调度的最小单位。一个进程可以有一个或者多个线程(例如 QQ是一个进程,发消息是一个线程,视频也是一个线程),各个线程之间共享进程的内存空间、系统资源,进程是操作系统资源分配的最小单位。


2、并发和并行


串行:一次只能执行一个任务,并且这个任务执行完之后才能执行下一个任务。


并行:当系统有一个以上CPU时,则线程的操做有可能非并发.当一个CPU执行一个线程时,另外一个CPU能够执行另外一个线程,两个线程互不抢占CPU资源,能够同时进行,这种方式称为并行。


注:进程也可以并行执行。


并发:并发是一种现象,同时运行多个程序或多个任务需要被处理的现象。这些任务可能是并行执行的,也可能是串行执行的,和CPU核心数无关,是操作系统进程调度和CPU上下文切换达到的结果。、


注:当有多个线程在运行时,若是系统只有一个CPU,则它根本不可能真正同时进行一个以上的线程,它只能把CPU运行时间划分成若干个时间片段,再将时间片段分配给各个线程执行,由于CPU切换特别快对我们来说就像是并行执行,实际是线程之间在交替执行。


3、总结


进程和线程的区别


1、线程是进程的一个实体,是指“进程代码段”的一次顺序执行流程,一个进程由一个或者多个线程组成,一个进程中至少有一个线程。

2、线程是CPU的最小调度单位,进程是操作系统的最小调度单位

3、线程是出于高并发的调度诉求从进程中演变出来的。线程的出现充分发挥了多核CPU的优势,也弥补了进程调度过于笨重的问题。

4、进程之间是相互独立的,但进程内的线程之间并不独立,各个线程之间共享进程的方法区内存、堆内存、系统资源(文件句柄、系统信号等)。

5、切换速度不同,线程上下文切换比进程要快的多,因此线程也称为轻量级进程。

并发和并行的区别


并发和并行是两个相似但又有区别的概念。它们都可以表示两个或者多个任务一起执行,但是侧重点有所不同。并发偏向于多个任务交替执行,而多个任务之间还可以是串行的。而并行就是指同时执行。


三、创建线程


1、继承Thread类


package com.xiaojie.juc.thread.mytest;
/**
 * @author xiaojie
 * @version 1.0
 * @description: 继承的方式创建线程
 * @date 2021/12/11 22:34
 */
public class ExtendsDemo{
    public static void main(String[] args) {
        System.out.println("主线程执行,线程名称是:" + Thread.currentThread().getName());
        new MyThread().start();
    }
    static class MyThread extends Thread {
        @Override
        public void run() {
            System.out.println("子线程执行,线程名称是:" + Thread.currentThread().getName());
        }
    }
}


2、实现Runnable接口


/**
 * @author xiaojie
 * @version 1.0
 * @description: 实现 Runnable
 * @date 2021/12/11 22:39
 */
public class RunableDemo {
    public static void main(String[] args) {
        System.out.println("主线程执行,线程名称是:" + Thread.currentThread().getName());
//        MyThread myThread = new MyThread();
//        Thread thread=new Thread(myThread);
//        thread.start();
        //或者 下面这种写法
        Thread thread1 = new Thread(() -> {
            System.out.println("子线程执行,线程名称是:" + Thread.currentThread().getName());
        });
        thread1.start();
    }
    static class MyThread implements Runnable {
        @Override
        public void run() {
            System.out.println("子线程执行,线程名称是:" + Thread.currentThread().getName());
        }
    }
}


3、实现Callable接口


package com.xiaojie.juc.thread.mytest;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;
/**
 * @author xiaojie
 * @version 1.0
 * @description: 实现Callable接口
 * @date 2021/12/11 22:48
 */
public class CallableDemo  {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        MyThread myThread = new MyThread();
        FutureTask<String> futureTask = new FutureTask<String>(myThread);
        new Thread(futureTask).start();
        //获取执行结果
        System.out.println(futureTask.get());
    }
   static class MyThread implements Callable<String > {
        //这种方式是获取现场执行结果,有返回值
        @Override
        public String call() throws Exception {
            return "实现Callable接口的方式创建线程。。。。。。";
        }
    }
}


4、线程池创建线程


/**
 * @author xiaojie
 * @version 1.0
 * @description: 基于线程池创建线程
 *  有4中线程池,以后总结。
 * @date 2021/12/11 22:55
 */
public class ExecutorsDemo {
    public static void main(String[] args) {
        ExecutorService executorService = Executors.newCachedThreadPool();
        executorService.execute(() -> System.out.println("基于线程池创建线程,线程名称为"+Thread.currentThread().getName()));
    }
}


四、线程状态


public enum State {
    NEW, //新建
    RUNNABLE,//运行状态 包含正在运行和就绪俩种状态
    BLOCKED,//阻塞状态
    WAITING,//等待状态
    TIMED_WAITING,//限时等待
    TERMINATED;//终止状态
}


333.png


1、在调用start()方法之后进入就绪状态,等待线程获取CPU时间片。


2、当线程获取到CPU的时间片时开始执行。


3、当CPU时间片用完之后就会再次进入就绪状态。


4、如果线程执行遇到synchronized关键字或者调用wait()、sleep()、join()等方法就会进入阻塞状态。


5、wait()方法遇到notify()或者notifuAll();join()等目标现成执行完毕等就会重新进入就绪状态。


6、当线程执行完毕,或者线程的run()方法发生异常,都会使线程进入终止状态


注意:


在调用线程实例的start()方法之后,线程可能不会马上执行而是等待CPU分配时间片给当前线程。

RUNNABLE状态包含了就绪和运行两个状态。

一旦线程新建之后,线程就不能再回到新建状态。同样线程进入终止状态之后,也不能再返回运行状态。



五、守护线程和线程优先级


守护线程


守护线程是进程运行时提供的某种后台服务的线程,比如GC线程。当用户线程都执行结束时,守护线程也会自动退出。守护线程需要在线程启动之前设置为守护线程


thread.setDaemon(true);

使用守护线程注意点:


1、守护线程必须在线程启动前设置。


2、守护线程存在被虚拟机强制终止的风险,所以守护线程中尽量不去访问系统资源,如文件句柄、数据库库连接。


3、守护线程创建的线程也是守护线程,如果显示调用thread.setDaemon(false),则新的线程可以调整为用户线程。


线程优先级


线程的调度方式是基于CPU的时间片方式进行线程调度。线程的调度模型主要有两种


分时调度模型:系统平均分配CPU的时间片,所有的线程轮流占用CPU。

抢占式调度模型:系统按照线程的优先级分配CPU时间片,优先级高的线程更容易获取到时间片而执行。


public static void main(String[] args) {
        System.out.println("主线程执行,线程名称是:" + Thread.currentThread().getName());
        MyThread myThread1 = new MyThread();
        myThread1.setName("thread1----");
        myThread1.setPriority(10);
        MyThread myThread2 = new MyThread();
        myThread2.setName("thread2----");
        myThread2.setPriority(3);
        MyThread myThread3 = new MyThread();
        myThread3.setName("thread3----");
        myThread3.setPriority(1);
        myThread3.start();
        myThread2.start();
        myThread1.start();
    }


运行结果如下图


333.png


注意:


1、线程的优先级最大是10,最小是1,超过这个范围就会报java.lang.IllegalArgumentException异常。


2、由图可见线程3执行优先于线程2。并不是说线程优先级高就一定先执行,而是优先级的高的线程更容易获取到CPU的时间片,先执行的机会就越多。


参考:


https://www.pdai.tech/md/java/thread/java-thread-x-theorty.html

《JAVA高并发核心编程(卷2):多线程、锁、JMM、JUC、高并发设计》-尼恩编著

《JAVA高并发程序设计》-葛一鸣著

相关文章
|
2月前
|
安全 数据处理 开发者
Python中的多线程编程:从入门到精通
本文将深入探讨Python中的多线程编程,包括其基本原理、应用场景、实现方法以及常见问题和解决方案。通过本文的学习,读者将对Python多线程编程有一个全面的认识,能够在实际项目中灵活运用。
|
23天前
|
并行计算 数据处理 调度
Python中的并发编程:探索多线程与多进程的奥秘####
本文深入探讨了Python中并发编程的两种主要方式——多线程与多进程,通过对比分析它们的工作原理、适用场景及性能差异,揭示了在不同应用需求下如何合理选择并发模型。文章首先简述了并发编程的基本概念,随后详细阐述了Python中多线程与多进程的实现机制,包括GIL(全局解释器锁)对多线程的影响以及多进程的独立内存空间特性。最后,通过实例演示了如何在Python项目中有效利用多线程和多进程提升程序性能。 ####
|
22天前
|
监控 安全 Java
Java中的多线程编程:从入门到实践####
本文将深入浅出地探讨Java多线程编程的核心概念、应用场景及实践技巧。不同于传统的摘要形式,本文将以一个简短的代码示例作为开篇,直接展示多线程的魅力,随后再详细解析其背后的原理与实现方式,旨在帮助读者快速理解并掌握Java多线程编程的基本技能。 ```java // 简单的多线程示例:创建两个线程,分别打印不同的消息 public class SimpleMultithreading { public static void main(String[] args) { Thread thread1 = new Thread(() -> System.out.prin
|
1月前
|
安全 Java 调度
Java中的多线程编程入门
【10月更文挑战第29天】在Java的世界中,多线程就像是一场精心编排的交响乐。每个线程都是乐团中的一个乐手,他们各自演奏着自己的部分,却又和谐地共同完成整场演出。本文将带你走进Java多线程的世界,让你从零基础到能够编写基本的多线程程序。
34 1
|
2月前
|
算法 NoSQL Java
Springboot3新特性:GraalVM Native Image Support和虚拟线程(从入门到精通)
这篇文章介绍了Spring Boot 3中GraalVM Native Image Support的新特性,提供了将Spring Boot Web项目转换为可执行文件的步骤,并探讨了虚拟线程在Spring Boot中的使用,包括如何配置和启动虚拟线程支持。
103 9
Springboot3新特性:GraalVM Native Image Support和虚拟线程(从入门到精通)
|
1月前
|
Java 数据处理 开发者
Java多线程编程的艺术:从入门到精通####
【10月更文挑战第21天】 本文将深入探讨Java多线程编程的核心概念,通过生动实例和实用技巧,引导读者从基础认知迈向高效并发编程的殿堂。我们将一起揭开线程管理的神秘面纱,掌握同步机制的精髓,并学习如何在实际项目中灵活运用这些知识,以提升应用性能与响应速度。 ####
46 3
|
2月前
|
Java
Java中的多线程编程:从入门到精通
本文将带你深入了解Java中的多线程编程。我们将从基础概念开始,逐步深入探讨线程的创建、启动、同步和通信等关键知识点。通过阅读本文,你将能够掌握Java多线程编程的基本技能,为进一步学习和应用打下坚实的基础。
|
2月前
|
数据挖掘 程序员 调度
探索Python的并发编程:线程与进程的实战应用
【10月更文挑战第4天】 本文深入探讨了Python中实现并发编程的两种主要方式——线程和进程,通过对比分析它们的特点、适用场景以及在实际编程中的应用,为读者提供清晰的指导。同时,文章还介绍了一些高级并发模型如协程,并给出了性能优化的建议。
34 3
|
2月前
|
Java C++
【多线程】JUC的常见类,Callable接口,ReentranLock,Semaphore,CountDownLatch
【多线程】JUC的常见类,Callable接口,ReentranLock,Semaphore,CountDownLatch
34 0
|
3月前
|
负载均衡 Java 调度
探索Python的并发编程:线程与进程的比较与应用
本文旨在深入探讨Python中的并发编程,重点比较线程与进程的异同、适用场景及实现方法。通过分析GIL对线程并发的影响,以及进程间通信的成本,我们将揭示何时选择线程或进程更为合理。同时,文章将提供实用的代码示例,帮助读者更好地理解并运用这些概念,以提升多任务处理的效率和性能。
62 3