JUC系列(一)| 什么是JUC?

12,197 阅读8分钟

多线程一直Java开发中的难点,也是面试中的常客,趁着还有时间,打算巩固一下JUC方面知识,我想机会随处可见,但始终都是留给有准备的人的,希望我们都能加油!!!

沉下去,再浮上来,我想我们会变的不一样的。

先看张图,舒缓下心情,再继续吧

JUC系列

一、JUC简介

JUC实际上就是我们对于jdk中java.util .concurrent 工具包的简称。这个包下都是Java处理线程相关的类,自jdk1.5后出现。

在这里插入图片描述

二、进程与线程

2.1、进程

概述:

进程(Process) 是计算机中的程序关于某数据集合上的一次运行活动,是系统进行资源分配和调度的基本单位,是操作系统结构的基础。 在当代面向线程设计的计算机结构中,进程是线程的容器程序是指令、数据及其组织形式的 描述进程是程序的实体

定义:

狭义定义:进程是正在运行的程序的实例(an instance of a computer program that is being executed)。

广义定义:进程是一个具有一定独立功能的程序关于某个数据集合的一次运行活动。它是操作系统动态执行的基本单元,在传统的操作系统中,进程既是基本的分配单元,也是基本的执行单元。

2.2、线程

线程(thread) 是操作系统能够进行运算调度的最小单位。它被包含在进程之 中,是进程中的实际运作单位。一条线程指的是进程中一个单一顺序的控制流, 一个进程中可以并发多个线程,每条线程并行执行不同的任务

1、线程是独立调度和分派的基本单位。2、同一进程中的多条线程将共享该进程中的全部系统资源。3、一个进程可以有很多线程,每条线程并行执行不同的任务。可并发执行。

当然,我们Java都知道的线程的同步是Java多线程编程的难点,因为要给哪些地方是共享资源(竞争资源),什么时候要考虑同步等,都是编程中的难点。😭所以才有了那么多滴锁,看到烦人。

2.3、创建线程的三种常见方式

  1. 通过实现Runnable接口来创建Thread线程
public class TheardCreateDemo {

    public static void main(String[] args) {
        Runnable runnable = new SomeRunnable();
        Thread thread1 = new Thread(runnable);
        thread1.start();
        //lamda表达式方式
        Thread thread2 = new Thread(() -> {
            System.out.println("使用lamda表达式方式");
        });
        thread2.start();
    }
}
class SomeRunnable implements Runnable
{
    @Override
    public void run()
    {
        System.out.println(Thread.currentThread().getName()+":: 通过实现Runnable接口来创建Thread线程");
    }
}

2.通过继承Thread类来创建一个线程

public class TheardCreateDemo {

    public static void main(String[] args) {
        SomeThread thread = new SomeThread();
        thread.start();
    }
}

class SomeThread extends Thread{
    @Override
    public void run() {
        System.out.println("通过继承Thread类来创建一个线程");
    }
}

3.通过实现Callable接口来创建Thread线程

public class TheardCreateDemo {

    public static void main(String[] args) throws ExecutionException, InterruptedException {
        
        FutureTask<Object> futureTask = new FutureTask<Object>(new SomeCallable<Object>());
        Thread oneThread = new Thread(futureTask);
        
        oneThread.start();
        // 这里可以在运行之后获得 返回值
        System.out.println(futureTask.get());
    }
}

class SomeCallable<Object> implements Callable<Object> {
    @Override
    public Object call() throws Exception {
        System.out.println("通过实现Callable接口来创建Thread线程");
        // 这个是可以返回数据的 这里就随便返回个 1024 哈
        return (Object)" 这个是可以返回数据的 这里就随便返回个哈";
    }
}

这里之后有篇文章还会讲到这个点的。

这里稍微讲下步骤:

} 步骤1:创建实现Callable接口的类SomeCallable

步骤2:创建一个类对象:Callable oneCallable = new SomeCallable();

步骤3:由Callable创建一个FutureTask对象:

FutureTask futureTask= new FutureTask(oneCallable);

注释:FutureTask是一个包装器,它通过接受Callable来创建,它同时实现了Future和Runnable接口。 步骤4:由FutureTask创建一个Thread对象:Thread oneThread = new Thread(futureTask);

步骤5:启动线程:oneThread.start();

这里为什么Thread(FutureTask futureTask)可以勒?

Thread的构造函数:

public Thread(Runnable target) {
    this(null, target, "Thread-" + nextThreadNum(), 0);
}

原因:因为套娃套到一起啦。😁

public class FutureTask<V> implements RunnableFuture<V> 
    
public interface RunnableFuture<V> extends Runnable, Future<V> 

第四种还有线程池的创建方式,之后会讲到,就不再这里增加篇幅啦。

三、并发和并行

在了解并发和并行之前,让我们先来看一看串行是什么样的吧。

1)串行模式:

串行模式:即表示所有任务都是按先后顺序进行。串行是一次只能取的一个任务,并执行这个任务。

举个生活中的小例子:就是在火车站买票,今天只开放这一个窗口卖票,那么我们只有等到前面的人都买了,才能轮到我们去买。即按先后顺序买到票。

2)并行模式:

概述:一组程序按独立异步的速度执行,无论从微观还是宏观,程序都是一起执行的。对比地,并发是指:在同一个时间段内,两个或多个程序执行,有时间上的重叠(宏观上是同时,微观上仍是顺序执行)。

并行模式:并行意味着可以同时取得多个任务,并同时去执行所取得的这些任务。

我们还是用上面那个例子:还是在买票,以前是只有一个窗口卖票,但是近几年发展起来了,现在有五个窗口卖票啦,大大缩短了人们买票的时间。

并行模式相当于将长长的一条队列,划分成了多条短队列,所以并行缩短了任务队列的长度。不过并行的效率,一方面受多进程/线程编码的好坏的影响,另一方面也受硬件角度上的CPU的影响。

3)并发:

并发并发指的是多个程序可以同时运行的一种现象,并发的重点在于它是一种现象,并发描述的是多进程同时运行的现象。但真正意义上,一个单核心CPU任一时刻都只能运行一个线程。所以此处的"同时运行"表示的不是真的同一时刻有多个线程运行的现象(这是并行的概念),而是提供了一种功能让用户看来多个程序同时运行起来了,但实际上这些程序中的进程不是一直霸占 CPU 的,而是根据CPU的调度,执行一会儿停一会儿。

4)小小的总结一下:

并发:即同一时刻多个线程在访问同一个资源,多个线程对一个点

  • 例子:秒杀活动、12306抢回家的票啦、抢演唱会的票...

并行:多个任务一起执行,之后再汇总

  • 例子:电饭煲煮饭、用锅炒菜,两个事情一起进行,(最后我们一起干饭啦干饭啦😁)

四、用户线程和守护线程

用户线程:指不需要内核支持而在用户程序中实现的线程,其不依赖于操作系统核心,应用进程利用线程库提供创建、同步、调度和管理线程的函数来控制用户线程。

守护线程是指在程序运行的时候在后台提供一种通用服务的线程,用来服务于用户线程;不需要上层逻辑介入,当然我们也可以手动创建一个守护线程。(用白话来说:就是守护着用户线程,当用户线程死亡,守护线程也会随之死亡)

比如垃圾回收线程就是一个很称职的守护者,并且这种线程并不属于程序中不可或缺的部分。因此,当所有的非守护线程结束时,程序也就终止了,同时会杀死进程中的所有守护线程。反过来说,只要任何非守护线程还在运行,程序就不会终止。

用一个简单代码来模拟一下:

未设置为守护线程时:主线程执行完成了,但是我们自己创建的线程仍然未结束。

在这里插入图片描述

设置为守护线程后:明显可以看到,当主线程执行完成后,我们设置为守护线程的那个线程也被强制结束了。

在这里插入图片描述

setDaemon就是设置为是否为守护线程。

五、自言自语

最近又开始了JUC的学习,感觉Java内容真的很多,但是为了能够走的更远,还是觉得应该需要打牢一下基础。

最近在持续更新中,如果你觉得对你有所帮助,也感兴趣的话,关注我吧,让我们一起学习,一起讨论吧。

你好,我是博主宁在春,Java学习路上的一颗小小的种子,也希望有一天能扎根长成苍天大树。

希望与君共勉😁

待我们,别时相见时,都已有所成