多线程学习笔记

JUC概述

1. 什么是JUC ?
        
java.util.concurrent工具包的简称,从jdk1.5开始出现 

2. 线程和进程的概念     

1.什么是进程什么是线程? 

        1.进程:指的是系统中正在运行的一个应用程序;  ------资源分配的最小单位
        2.线程:系统再分配处理器时间资源的基本单位;  ------程序执行的最小单位

 2. 线程的状态

        1.新建状态/就绪状态 ——创建还未启动
        2.运行状态 ——使用start()方法调用
        3.阻塞状态 ——没有抢到被锁住的资源,被阻塞
        4.等待状态 ——使用wait()方法进入阻塞等待状态
        5.计时等待状态/超时等待 —— sleep()网上有叫计时也有叫超时,其实意思就是在等待一定时间之后自动唤醒
        6.终止

3.wait和sleep的区别

        1.sleep是Thread的静态方法,wait是Object的方法, 任何实例对象都可以直接调用
        2.sleep不会释放锁,wait会释放锁。
        3.使用范围:wait,notify和notifyAll只能在同步代码块里面使用,而sleep可以在任何地方使用
        4.sleep必须要捕获异常,wait不用

4.并发和并行 

        1. 并行:多项工作一起执行,最后汇总
        2. 并发:多个线程同时抢占同一份资源

5.管程—Monitor监视器 

        1.是一种同步机制,保证在同一时刻只有一个线程访问被保护数据的代码
        2.JVM同步基于进入和退出,使用管程对象实现——现持有管程对象去访问,别的进程没有管程对象就不能访问,要等这个线程释放管程

6.用户线程和守护线程

        1.用户线程:自定义线程
        2.守护线程:特殊线程运行在后台,比如垃圾回收,会一直等待到用户的主线程结束比如main

        3.可以同setDaemon设置用户线程和守护线程, 默认false用户线程

3.创建线程的几种方法

1.继承Thread方法:
        1.重写run方法
2.实现Runable接口
        1.实现run方法
3.实现Callable接口
        1. FutureTask 未来任务 原理:
                1. 需要使用FutureTask实现类接收参数,然后使用Thread启动线程
                2. 什么是未来任务?
                        1. Callable会返回一个结果,下次同一个线程再去执行相同任务的时候不需要等待,之间获得结果
                        2. 他会等所有Callable创建的线程完成在做一个汇总输出
4. Runnable接口和Callable接口的区别
        1. Runnable接口实现的是Run()方法,Callable规定的方法是call()。
        2. Callable的任务执行后可返回值,而Runnable的任务是不能返回值得
        3. call方法可以抛出异常,run方法不可以

5.线程池(在下面)

        1.创建线程池的方式 

                 1.通过Executors工厂方法创建

//:创建一个固定大小的线程池,可控制并发的线程数,超出的线程会在队列中等待;       
       Executors.newFixedThreadPool(2);
//:创建一个可缓存的线程池,若线程数超过处理所需,缓存一段时间后会回收,若线程数不够,则新建线程
       Executors.newCachedThreadPool();
//:创建单个线程数的线程池,它可以保证先进先出的执行顺序;
       Executors.newSingleThreadExecutor();
//:创建一个可以执行延迟任务的线程池;
       Executors.newScheduledThreadPool(2);
//:创建一个单线程的可以执行延迟任务的线程池;
       Executors.newSingleThreadScheduledExecutor();
//:创建一个抢占式执行的线程池(任务执行顺序不确定)【JDK 1.8 添加】。
       Executors.newWorkStealingPool();


                2.通过ThreadPoolExecutor new一个原始线程池
        2.线程池的七个参数
     
           (1)corePoolSize:线程池中的常驻核心线程数。
                (2)maximumPoolSize:线程池能够容纳同时执行的最大线程数,此值大于等于1。
                (3)keepAliveTime:多余的空闲线程存活时间,当空间时间达到keepAliveTime值时,多余的线程会被销毁直到只剩下corePoolSize个线程为止。
                (4)unit:keepAliveTime的单位。
                (5)workQueue:任务队列,被提交但尚未被执行的任务。
                (6)threadFactory:表示生成线程池中工作线程的线程工厂,用户创建新线程,一般用默认即可。
                (7)handler:拒绝策略,表示当线程队列满了并且工作线程大于等于线程池的最大显示数(maxnumPoolSize)时如何来拒绝请求执行的runnable的策略。
        3.线程池的流转方式:
             
 
1.  一个池里面有两个线程常驻(corePoolSize:常驻核心线程数量),如果来了两个任务需要处理,就由这两个常驻线程进行处理
                2. 如果这个时候又来了两个任务,就把任务丢进
任务队列(workQueue)里面进行等待。
                3.如果来了很多任务导致任务队列爆满,那么就由
线程工厂(threadFactory)创建新的线程开始处理任务。
                4.此时创建的线程达到了线程池能够容纳同时执行的
最大线程数(maximumPoolSize)此时开始执行拒绝策略(handler),拒绝多出来无法处理的任务。
                5.所有任务都处理完了,在等待一定时间之后,关闭多余的线程,以免浪费资源
(keepAliveTime:存活时间+unit:keepAliveTime的单位)

线程池:

使用线程池完成异步

1. 设置线程参数创建线程池:

    private static final int CORE_POOL_SIZE = 5;
    private static final int MAX_POOL_SIZE = 10;
    private static final int QUEUE_CAPACITY = 100;
    private static final Long KEEP_ALIVE_TIME = 1L;

    public static ThreadPoolExecutor executor = new ThreadPoolExecutor(
            CORE_POOL_SIZE,
            MAX_POOL_SIZE,
            KEEP_ALIVE_TIME,
            TimeUnit.SECONDS,
            new ArrayBlockingQueue<>(QUEUE_CAPACITY),
            new ThreadPoolExecutor.CallerRunsPolicy());;

2.创建任务

     
    private static void interfaceOne() {
        try {
            System.out.println("执行方法One");
            Thread.sleep(5000);
            System.out.println("执行完毕方法One");
        } catch (Exception e) {
            System.out.println("执行异常");
        }
    }

    private static void interfaceTwo(){...}

    private static void interfaceThree(){...}


3.测试

     public static void main(String[] args) {
        long a = new Date().getTime();
        executor.execute(ThreedTest::interfaceOne);
        executor.execute(ThreedTest::interfaceTwo);
        executor.execute(ThreedTest::interfaceThree);
        long b = new Date().getTime();
        interfaceOne();
        interfaceTwo();
        interfaceThree();
        long c = new Date().getTime();
        System.out.println("异步执行耗时" + (b - a));
        System.out.println("执行耗时" + (c - b));
        System.out.println("总耗时" + (c - a));

    }

4.结果输出

 

 为什么不是20秒?主线程 5 + 5 + 5 ,然后开了三个线程另外来执行,所以主线程在执行的时候其他线程已经在跑了

1.Synchronized

1.同步实现

        1.Synchronized实现同步的基础:Java中的每一个对象都可以作为锁,具体表现以下3种形式。
        2.对于普通同步方法,锁是当前实例对象
        3.对于静态同步方法,锁是当前类的Class对象
        4.对于同步方法块,锁的是Synchronized括号里配置的对象

2.锁的升级--------Synchronized只能升级不能降级

        1.偏向锁:

锁对象的对象头里面有个Threadld字段,如果这个字段为空,那么在线程第一次获取锁的时候就会把自己的id写入到ThreadId当中,下次在获取所得时候直接检查threadId中的id是否和自己的ID一致,如果一致就表示以及获取了锁,不用重新获取锁,略过了加锁阶段

        2.轻量级锁:

轻量级锁,偏向锁是单线程情况下锁的优化,当有多个线程竞争同一临界资源,这个时候偏向锁就会被撤销,升级成轻量级锁,

        3.锁自旋:

         4.重量级锁:  

2.Lock

3.Lock锁和Synchronized的区别

        1. Lock是一个接口,Synchronized是Java的一个关键字,Synchronized是由内置语言实现的。
        2. synchronized在发生异常的时候,会自动释放线程占有的锁,Lock如果没用通过unLock()去释放锁,是不会自动释放,很容易导致死锁的产生。
        3. 性能问题,由于Synchronized锁的多次优化升级,比如锁的升级,导致使用Sunchronized锁性能是由于lock锁的
        4. Lock锁有读锁和写锁,Synchronized没有。
        5. Lock可以实现公平锁和非公平锁,实现公平锁在构造函数中传入true就可以实现

4.可重入锁/递归锁

                自己也还没搞明白,搞明白再写

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值