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

目录

一、为什么要有多线程

二、名词解释

1、进程和线程

2、并发和并行

3、总结

三、创建线程

 1、继承Thread类

 2、实现Runnable接口

 3、实现Callable接口

 4、线程池创建线程 

四、线程状态

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


一、为什么要有多线程

          随着计算机的发展,单核的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 futureTask = new FutureTask(myThread);
        new Thread(futureTask).start();
        //获取执行结果
        System.out.println(futureTask.get());
    }

   static class MyThread implements Callable {
        //这种方式是获取现场执行结果,有返回值
        @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;//终止状态
}

JUC并发编程——多线程入门_第1张图片

 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();
    }

运行结果如下图

JUC并发编程——多线程入门_第2张图片

 注意:

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高并发程序设计》-葛一鸣著

你可能感兴趣的:(JUC,JUC,并发编程,多线程)