java多线程-Thead线程学习记录笔记

Posted YX_blog

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了java多线程-Thead线程学习记录笔记相关的知识,希望对你有一定的参考价值。

介绍的内容:

主要介绍多线程的基本概念到后面的基本使用。以及一些稍微的原理猜测。个人笔记,不喜勿喷 

目录

目录

一、线程使用

1.1 概念

1.2 线程生命周期

1.3 java中实现的方式

  1.3.1 继承线程 Thread

1.3.2 实现接口Runnable (无返回值)

1.3.3 实现接口Callable (带返回值)

1.4线程执行的流程

二、多线程带来的安全性问题

三、如何解决安全性问题

3.1 基本概念

3.2 如何自己实现上锁操作

四、线程相关使用工具

4.1相关同步API(了解)

4.2线程池相关的知识点(掌握)

 4.2.1 ThreadPoolExecutor相关参数

 4.2.2 线程池提供静态创建

4.2.3猜想一下线程池的实现原理

4.2.4其他


一、线程使用

线程:主要是异步+并行,为了合理的利用cpu资源

 一般多线程都会和并发、并行联系在一起。

1.1 概念

 并发:单个时间内,能支持的吞吐量。一定需要多线程的参与, 常见的评判指标有:QPS,TPS来进行评判标准

 并行:同一时刻能够运行多个任务

1.2 线程生命周期

图:

            

 

  1. 新建NEW、
  2. 运行RUNNABLE(就绪-->通过cpu调用算法)
  3. 阻塞BLOCKED、
  4. 等待WAITING、
  5. 超时等待TIMED_WAITING、
  6. 终止 TERMINATED

1.3 java中实现的方式

  1.3.1 继承线程 Thread

 // 方式一 继承线程 Thread
    public class ThreadDemoOne extends  Thread
        @Override
        public void run() 
            System.out.println("继承 Thread ==> 相关业务逻辑");
            super.run();
        
    

1.3.2 实现接口Runnable (无返回值)

   // 方式二 实现接口Runnable (无返回值)
    public class ThreadDemoTwo implements  Runnable
        @Override
        public void run() 
            System.out.println("实现接口Runnable ==>相关业务逻辑");
        
    

1.3.3 实现接口Callable (带返回值)

// 方式三 实现接口Callable (带返回值)
    public class ThreadDemoThree implements Callable

        @Override
        public Object call() throws Exception 
            System.out.println("实现接口Callable ==>相关业务逻辑");
            return null;
        
    

1.4线程执行的流程

从程序发出指令之后cpu执行的简短流程

图形:

 

二、多线程带来的安全性问题

 特性:原子性、有序性、可见性

原子性:多线程的不确定性,对资源的抢夺是不确定的,比如:多线程count++中

有序性:这个就涉及到指令重排序。涉及到cpu的高速缓存L1 L2、代码的顺序和执行的顺序不一定是一样的。针对一个变量。在cpu中如果需要,可能会优先运行。new Person -->指令分为三步

可见性:a理论上,所有的资源应该是需要从内存中读取,但是由于cpu高速缓存,导致修改之后的数据没有回写给内存。b:指令优化等 volatile 可以处理这种问题。 true  -->另开线程 false 

 

三、如何解决安全性问题

3.1 基本概念

针对带来的安全性问题,我们可以采用相关的策略。更多API可以查看 第四点

  1. 原子性:automicXXX,synchronized,lock
  2. 有序性: volatile 、lock、BlockingQueue
  3. 可见性: volatile ,共享变量、final、synchronized

可见性涉及到cpu内部高速缓存,导致内存数据未及时更新。多线程之间数据的不可见性。

volatitle:解决可见性,防止cpu缓存,让缓存失效。

3.2 如何自己实现上锁操作

1.怎么实现一个锁?需要考虑什么问题呢?如下:

1.条件互斥,共享资源(cas)
cas实现
2.等待队列(抢夺资源的线程存放)
BlockingQueue,链表
3.阻塞
sleep/wait/join/park
4.唤醒
notify/notifyall/unpark

四、线程相关使用工具

4.1相关同步API(了解)

线程同步类 CountDownLatch   Semaphore  CyclicBarrier  
并发集合类 ConcurrentHashMap .ConcurrentSkipListMap , CopyOnWriteArrayList ,BlockingQueue 
线程池: Executors  
锁:StampedLock(1.8引入)  ReentrantLock 
原子操作:LongAdder (1.8引入,比AtomicLong 好)
  // 实现runnable,或者继承Thread ,返回异常给调用方捕捉。如果实现(Callable接口可以返回参数就不用了)
         Thread.currentThread().setUncaughtExceptionHandler();

4.2线程池相关的知识点(掌握)

     线程使应用能够更加充分合理地协调利用CPU 、内存、网络、1/0 等系统资源。线程的创建需要开辟虚拟机栈、本地方法枝、程序计数器等线程私有的内存空间。在线程销毁时需要回收这些系统资源。频繁地创建和销毁线程会浪费大量的系统资源,增加并发编程风险。另外,在服务器负载过大的时候,如何让新的线程等待或者友好地拒绝服务?这些都是线程自身无法解决的。所以需要通过线程池协调多个线程, 并实现类似主次线程隔离、定时执行、周期执行等任务。

 4.2.1 ThreadPoolExecutor相关参数

corePoolSize:常驻核心数。不会销毁的线程
maximumPool:线程池能够容纳同时执行的最大线程数
keepAliveTime:线程池中的线程空闲时间,当空闲时间达到keepAliveTime 值时,线程会被销毁
TimeUnit:keepAliveTime的时间单位通常是TimeUnit.SECONDS 。
workQueue:缓存队列。当请求数大于线程池能够容纳同时执行的最大线程数时。进入缓存队列
ThreadFactory:线程工厂,分组名称。方便后期出问题时精确定位
handler:执行拒绝策晤的对象。当缓存队列达到上限时,开始执行拒绝策略 RejectedExecutionHandler

 4.2.2 线程池提供静态创建

//创建持有足够线程的线程池支持给定的并行度, 并通过使用多个队列减少竞争
Executors.newWorkStealingPool

//maximumPoolSize 最大可以至Integer. MAX_VALUE, 是高度可伸缩的线程池,
Executors.newCachedThreadPool

//最大卫Integer.max_value 不回收线程相比Timer , Schedu l edExecutorService 更安全,
功能更强大, 与newCachedThreadPool 的区别是不回收工作线程。
Executors.newScheduledThreadPool

//建个单线程的线程池,相当于单线程串行执行所有任务, 保证接任务的提交顺序依次执行。
Executors.newSingleThreadExecutor

//输入的参数即是固定线程数,既是核心线程
数也是最大线程数, 不存在空闲线程,所以keepAliveTime 等于O
Executors.newFixedThreadPool


4.2.3猜想一下线程池的实现原理

  我们是不是可以自己弄呢?

   1.明确为何需要线程池?

        池化技术,防止高并发时,资源上面的耗尽和重复创建。限流保护机制

   2. 线程池的如何设计呢?

      线程执行时,run方法完成就会结束,如何创建线程不销毁呢?

  •       1.死循环
  •       2 不能无限循环。需要阻塞
  •      3 保证有任务就执行,没有任务就阻塞

    基本上就是按着这个进行的 

   

4.2.4其他

第一题 问:如何实现一个订阅发布呢?

答:阻塞队列,原理:notify/wait

 第二题:怎么算是线程执行完成呢?

 答:run 方法运行完成

第三:线程的状态有几种。两种等待的区别

答:共六种, new ,runabled,waitting ,TIMED_WAITING.block ,terminated.  ==> waitting 需要唤醒,属于被动唤醒,TIMED_WAITING超时等待==>约定时间之后,自己会自动唤醒,属于主动唤醒

第四:什么是可重入锁

答:就是方法调用方法,递归,可以共用同一把锁,不会发生死锁,ReentrantLock 和synchronized 都属于可重入锁

第五:synchronized 和 ReentrantLock 的区别

答:

都属于可重入锁。

  • 特性==> 
  • synchronized  非公共锁 , ReentrantLock  支持fair and no fair 
  • synchronized  被动释放锁 ,ReentrantLock  主动释放锁

    synchronized 属于关键字, ReentrantLock 属于对象。

 

 

 

以上是关于java多线程-Thead线程学习记录笔记的主要内容,如果未能解决你的问题,请参考以下文章

java 多线程学习笔记 -- 计算密集型任务

Java 高级开发技术学习笔记之多线程

Java 高级开发技术学习笔记之多线程

多线程Java多线程学习笔记 | 多线程基础知识

java中多线程执行时,为何调用的是start()方法而不是run()方法

Java基础学习——多线程之线程池