一线码农对java多线程、Thead线程的独到见解

码农公社  210.net.cn   210= 1024  10月24日一个重要的节日--码农(程序员)节

主要介绍多线程的概念和实际使用、及一对相关原理的理解,请耐心读到最后!一定有收获哟!


一、线程使用 

线程:主要是异步+并行,为了合理的利用cpu资源   一般多线程都会和并发、并行联系在一起。  

1.1 概念  

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

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

1.2 线程生命周期

aHR0cHM6Ly9zczAuYmRzdGF0aWMuY29tLzcwY0Z2SFNoX1ExWW54R2twb1dLMUhGNmhoeS9pdC91PTMzMjc0NDkzMTAsMTQwNjM3ODM2OCZmbT0yNiZncD0wLmpwZw.jpg

新建NEW、 运行RUNNABLE(就绪-->通过cpu调用算法)、 阻塞BLOCKED、 等待WAITING、 超时等待TIMED_WAITING、 终止 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执行的简短流程

20200122095522445.jpg

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

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


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


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


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


三、如何解决安全性问题

3.1 基本概念

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


原子性:automicXXX,synchronized,lock

有序性: volatile 、lock、BlockingQueue

可见性: volatile ,共享变量、final、synchronized

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


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


3.2 如何自己实现上锁操作

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

  1. 1.条件互斥,共享资源(cas)

  2. cas实现

  3. 2.等待队列(抢夺资源的线程存放)

  4. BlockingQueue,链表

  5. 3.阻塞

  6. sleep/wait/join/park

  7. 4.唤醒

  8. 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 属于对象。


评论