并发编程之线程

一、创建线程的方式

1.继承Thread类,重写该类的run()方法。

2.实现Runnable接口,并重写该接口的run()方法,该run()方法同样是线程执行体,创建Runnable实现类的实例,并以此实例作为Thread类的target来创建Thread对象,该Thread对象才是真正的线程对象。接口 静态代理

3.使用Callable和Future接口创建线程。具体是创建Callable接口的实现类,并实现call()方法。并使用FutureTask类来包装Callable实现类的对象,且以此FutureTask对象作为Thread对象的target来创建线程。有返回值和异常。JUC包

4.通过线程池来创建线程

5.java线程中start()和run()的区别:start()可以启动线程,而run()只是一个普通方法。

 二、线程的生命周期及五种基本状态

  2.1 线程生命周期:新建(new)、就绪(start/yield等)、运行(CPU run)、阻塞(sleep/join等)、死亡(stop/加flag)。

 2.2 Thread类和Object的方法

sleep暂停/yield礼让属于类方法;join插队属于对象方法。

  2.3 守护线程

    用户线程和守护线程(Daemon)。jvm的退出不用等待守护线程执行完成。例:垃圾回收线程。应用场景:jvm退出时,线程能够自动退出。

    设置守护线程:t.setDaemon(true)  验证守护线程:t.isDaemon.

  2.4 Hook(钩子)线程 

    jvm退出的时候,钩子线程就会被启动执行。

   Hook(钩子)可以执行一些资源释放的操作,比如关闭数据库连接,Socket 连接等。

   Runtime.getRuntime().addShutdownHook(new CleanWorkThread());

  2.5 常用方法

  isAlive()、setName()、getName()、currentThread()类方法。

三、高并发

 3.1 并发:同一个对象多个线程同时操作。抢占资源

 3.2 线程同步(线程安全):队列与锁 Synchronized

 锁机制:锁住对象或Class对象。

 3.3 Synchronized方法和Synchronized块 线程安全

     synchronized 方法:public synchronized void test(){}。

  synchronized 块:synchronized (obj){}。 obj:同步监视器。粒度更小

       性能优化:临界值、double checking。

  3.4 并发容器

      concurrent包:CopyOnWriteArrayList

  3.5 死锁

      3.5.1 产生死锁的条件

   1)互斥使用:即当资源被一个线程使用(占有)时,别的线程不能使用。

   2)不可抢占:资源请求者不能强制从资源占有者手中夺取资源,资源只能由资源占有者主动释放。

   3)请求和保持:即当资源请求者在请求其他的资源的同时保持对原有资源的占有。

  

   4)循环等待:即存在一个等待队列:P1占有P2的资源,P2占有P3的资源,P3占有P1的资源。这样 就形成了一个等待环路。
  3.5.2 解决死锁

  1)资源一次性分配:一次性分配所有资源,这样就不会再有请求了:(破坏请求条件)
  2)只要有一个资源得不到分配,也不给这个进程分配其他的资源:(破坏请保持条件)
  3)可剥夺资源:即当某进程获得了部分资源,但得不到其它资源,则释放已占有的资源(破坏不可剥夺条件)
  4)资源有序分配法:系统给每类资源赋予一个编号,每一个进程按编号递增的顺序请求资源,释放则相反(破坏环路等待条件)

  3.5.3 死锁的根本原因

    1)是多个线程涉及到多个锁,这些锁存在着交叉,所以可能会导致了一个锁依赖的闭环;
         2)默认的锁申请操作是阻塞的。

              多个线程都拥有锁,且都需要等待对方释放资源。   

 3.6 并发协作 线程协作

      3.6.1生产者消费者模式

       1) 线程通信

      3.6.2协作模型之管程法

        1)生产者 消费者 缓冲区

        2)Object类  wait() 释放锁,notify()/notifyall() 成对出现

      3.6.3协作模型之信号灯法

       1)生产者 消费者 标志位

 3.6 高级主题-定时调度

  java.util.Timer

       java.util.TimerTask

 3.7 任务定时调度框架-quartz

     Scheduler:调度器,控制所有的调度

     Trigger:触发条件,采用DSL模式

     JobDetail:需要处理的任务

   Job:执行逻辑

 3.8 重排序:Happen-Before

    重排序:编译器和CPU会尝试重排指令使得代码更快地运行。目的:提高性能

    数据依赖:编译器和处理器在重排序时,会遵守数据依赖性,编译器和处理器不会改变存在数据依赖关系的两个操作的执行顺序。

3.9 volatile修饰

    当一个共享变量被volatile修饰时,它会保证修改的值会立即被更新到主存,

    当有其他线程需要读取时,它会去内存中读取新值(JMM通过将工作内存的设为失效,以让其直接去读内存)。

    volatile变量的内存可见性是基于内存屏障实现的。

   1) java内存模型中的可见性、原子性和有序性。

       可见性:是指线程之间的可见性,一个线程修改的状态对另一个线程是可见的。Java中volatile、synchronized和final实现可见性。

  原子性:synchronized、lock、unlock保证原子性。

       有序性:volatile、synchronized保证线程之间操作的有序性。

   2) volatile保证线程间变量的可见性。不能保证原子性。

   3) 原理:内存屏障。通过内存屏障可以禁止特定类型处理器的重排序。

  4) volatile规则:如果一个线程对volatile变量读,另一个线程对该变量写,那么写操作一定发生在读操作之前。

3.10 dcl单例模式

    volatile保证线程间变量的可见性。不能保证原子性。

    1)构造器私有化-->避免外部new构造器

    2)提供私有的静态属性-->存储对象的地址

    3)提供公共的静态方法-->获取属性

3.11 ThreadLocal 线程的本地存储区域

    get/set/initialValue初始化方法。

    private static修饰

    InheritableThreadLocal:继承上下文环境的数据,拷贝一份给子线程。

3.12 可重入锁实现原理

    锁可以延续使用+计数器

    可重入锁:公平锁和不公平锁。公平锁:等待时间长的先获得锁。

    JUC包:ReentrantLock.

3.13 CAS原子操作(Compare and swap 比较并交换)。

   悲观锁:synchronized关键字、lock接口。

   乐观锁:CAS:属于硬件级的操作(利用CPU的CAS指令),是乐观锁的一种实现。效率高

  JUC(java.util.concurrent)中AtomicXxx类:AtomicInteger.decrementAndGet()。

四、多线程小结

 4.1 推导lambda简化线程    拉姆达

 4.2 在并发编程中,比较常用的是使用synchronized关键字和Lock接口同步,或者volatile关键字,来确保多线程下的有序性。

   

 

posted @ 2019-05-31 16:22  遇见神龙  阅读(175)  评论(0编辑  收藏  举报