多线程之基础篇(一)

一、Thread类

1、线程的创建

大家都熟知创建单个线程的三种方式,通过继承Thread类创建线程 并重写该类的run()方法;通过实现Runnable接口创建线程 一样要重写run()方法;以上的两个run()方法都是线程的执行体;第三,使用Callable和Future来创建线程 Callable接口提供了一个call()方法来作为线程的执行体,call()方法比run()方法功能要更加强大,call()方法可以有返回值 ,call()方法可以声明抛出异常 (前两种如果要抛异常只能通过try,catch来实现);下面详细介绍一下Callable和Future

我们知道创建线程最终要由操作系统支持,java中只有调掉Thread对象,最后执行一个叫private native void start0();的本地方法,而该方法的最终实现在C++和C层面,要阅读JVM源码才能更深一步,暂时咱们知道最后是由操作系统给我们另起了一个线程取执行 "执行体"里面的业务。我们看Thread类的构造方法里面没有直接传Callable接口的构造方法,那为啥我们还能运行下面这段代码呢?

java 复制代码
    /**
     * Callable需要配合FutureTask使用
     * FutureTask的get()可以返回任务的执行结果,方便在2个线程之间,把一个线程的结果传给另一个线程
     *
     * @throws InterruptedException
     * @throws ExecutionException
     */
    private static void testFutureAndCallable() throws InterruptedException, ExecutionException {
        FutureTask<Integer> future = new FutureTask<>(() -> {
            log.debug("running.....");
            Thread.sleep(1000);
            return 100;
        });

        Thread t3 = new Thread(future, "t3");
        t3.start();

        // 当主线程运行到get方法时,主线程会阻塞住,一直等待 t1线程 结果返回
        log.debug("main接收future的返回值:{}",future.get());
        log.debug("main线程继续运行....");
    }

我们可以看到,直接把future对象传入给了Thread构造方法,其实我们可以从FureTask出发, FutureTask实现了RunnableFuture接口,而RunnableFuture又同时实现了Runnable和Future接口,所以在直接构造Thread时可以直接传入构造参数执行。

java 复制代码
public class FutureTask<V> implements RunnableFuture<V> {}

public interface RunnableFuture<V> extends Runnable, Future<V> {}

尽管FureTask+Callable有返回值,可以抛异常,可以查看线程执行任务的情况,但是有时候为获取它的返回值主线程而陷入阻塞 等待,另一个isDone()方法轮询获取值容易耗CPU资源.........

针对以上2个痛点JDK做了扩展CompletableFuture类同时实现了Future和CompletionStage接口,在CompletionStage上做了极大的扩展

Runnable------没有输入参数,也没有返回值

Function------功能型函数接口,有一个参数,有一个返回参数

Consumer------消费型函数接口,有一个输入参数,没有返回参数

---Consumer延申BiConsumer,有两个输入参数,没有返回参数

Supplier------供给型函数接口,没有输入参数,有返回值

|------------|--------|------|------|
| 数式接口名称 | 方法名称 | 参数 | 返回值 |
| Runnable | run | 无参数 | 无返回值 |
| Function | apply | 1个参数 | 有返回值 |
| Consumer | accept | 1个参数 | 无返回值 |
| Supplier | get | 无参数 | 有返回值 |
| BiConsumer | accept | 2个参数 | 无返回值 |

2、interrupt()、interrupted()、isinterrupted()

|-------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| public void interrupt() | 实例方法 ,Just to set the interrupt flag 实例方法interrupt()仅仅是设置线程的中断状态为true,发起一个协商而不会立刻停止线程。 如果线程处于被阻塞状态(例如处于sleep,wait,join等状态),在别的线程中调用当前线程对象的interrupt方法,那么线程将立即退出被阻塞状态,并抛出一个InterruptException异常。 |
| public static boolean interrupted() | 静态方法,Thread.interrupted(); 判断线程是否被中断并清除当前中断状态。 这个方法做了两件事 1.返回到当前线程的中断状态,测试当前线程是否已被中断。 2.将当前线程的中断状态清零并重新设为false,清除线程的中断状态 |
| public boolean isInterrupted() | 实例方法 判断当前线程是否被中断(通过检查中断标志位) |

二、Java的锁

乐观锁:

1、版本号version

2、CAS(compareAndSwap)算法,java原子类(Atomic打头的类)中的递增操作就是通过CAS自旋实现的。------Unsafe类

使用场景:适合读操作多的场景,不加锁的特点能够使其读取操作的性能大幅提升。乐观锁则直接去操作同步资源,是一种无锁算法

悲观锁: 同一时间点有且仅有一个线程占有锁

1、synchronized

2、ReentrantLock

使用场景:适合写操作多的场景,先加锁可以保证写操作时数据正确,现实的锁定之后在操作同步资源

1、synchronized

为什么任何一个对象都可以成为一把锁?

因为Java中所有的对象都默认继承了超类Object,而Object在JVM源码(C++)层面关联了一个对象ObjectMonitor,所以每个对象"天生"都带着一个对象监视器,也就是每一个被锁住的对象都会和Monitor关联起来。

ObjectMonitor中有几个关键属性

|-------------|------------------------|
| _owner | 指向持有ObjectMonitor对象的线程 |
| _WaitSet | 存放于wait状态的线程队列 |
| _EntryList | 存放处于等待锁block状态的线程队列 |
| _recursious | 锁的重入次数 |
| _count | 用来记录该线程获取锁的次数 |

管程:Monitors,也叫监视器

是一种程序结构,结构内的多个子程序(对象或模块)形成的多个工作线程互斥访问共享资源。这些共享资源一般是硬件设备或一群变量。对共享变量能够进行的所有操作集中在一个模块中。(把信号量及其操作原语"封装"在一个对象内部)管程实现了在一个时间点,最多只有一个线程在执行管程的某个子程序。管程提供了一种机制,管程可以看作一个软件模块,它是将共享的变量和对于这些共享变量的操作封装起来,形成一个具有一定接口的功能模块,进程可以调用管程来实现进程级别的并发控制。

注:synchronized和 static synchronized前者是对象锁,后者是类锁,属于不同的锁,a线程加对象锁,b线程加类锁,加锁不同,a、b线程不会产生竟态条件。

2、自旋锁

没有成员变量的类一般都是线程安全的

cynchronized

并发压测工具

复制代码

装饰器模式:Collections以synchronized打头的的实现集合的那些方法就是采用了装饰器模式。

1、AQS

AQS全称是AbstractQueuedSynchronizer,是阻塞式锁和相关的同步器工具的框架

特点:

1、用state属性来表示资源的状态(分独占模式和共享模式),子类需要定义如何维护这个状态,控制如何获取锁和释放锁

---getState-获取state状态

---setState-设置state状态

---compareAndSetState-cas机制设置state状态compare式保证state的原子性

2、提供了基于FIFO的等待队列,类似于Monitor的EntryList

3、条件变量来实现等待、唤醒机制,支持多个条件变量,类似于Montor的WaitSet

相关推荐
Chester_199914 小时前
CSP202203C.计算资源调度器
开发语言·数据结构·c++·蓝桥杯
EXI-小洲15 小时前
Java 操作 Word:字符串替换、图片插入、动态生成表格与API接口下载
java·开发语言·spring boot·word
会周易的程序员16 小时前
给 PLC 写一个字节码虚拟机:STVM 虚拟机架构设计
开发语言·c++·虚拟机·软plc·iec61131·stvm
机器视觉知识推荐、就业指导16 小时前
为什么老说“纯 Qt 没啥就业市场”?
开发语言·qt
telepan16 小时前
Qt 开发避坑与性能指南:如何优雅、安全地定义全局常量字符串?
开发语言·qt
m0_6952514916 小时前
Qt MaintenanceTool 使用国内镜像加速下载(超详细教程)
开发语言·qt
2601_9619017016 小时前
SpringBoot使用Nacos进行application.yml配置管理
java·spring boot·spring
何以解忧,唯有..17 小时前
LangChain 工具调用(Tool Calling)实战指南
java·前端·langchain
大衛說17 小时前
《Python 从入门到精通》系列总览与学习路线
开发语言·python·学习
QQ_216962909618 小时前
【源码编号:project79475】SpringBoot校内二手交易平台:商品发布、分类检索、留言交流、订单管理全流程实战
java·spring boot·后端