Java多线程基础知识

212 阅读8分钟

多线程:

1、进程

I.运行时的程序,称为进程。

II.单核CPU在任一时间点上,只能运行一个进程。

III.宏观并行、微观串行

IV. cpu get NumberOfCores 获得核心数

2、线程

I.轻量级进程

II.程序中的一个顺序控制流程,也是CPU的基本调度单位。

III.进程可以由单个或多个线程组成,彼此间完成不同的工作,交替执行,称为多线程

IV.JVM虚拟机是一个进程,默认包含主线程(Main函数),可以通过代码创建多个独立线程,与Main线程并发执行。

3、线程的组成:

  I.CPU时间片

II. 运行数据:

(1).堆空间:存储线程需要使用的对象,多个线程可以共享堆中的对象

(2).栈空间:存储线程需要使用的局部变量,每个线程都拥有独立的栈

4、线程的创建

I.继承Thread类,自定义类变成线程类

II.实现Runnable接口,赋予自定义类线程任务的能力。

III.实现Runnable接口,不影响类继承,更灵活。

IV.线程创建后,需要调用start();方法,来启动线程,由JVM调用run()方法。直接调用run()方法并不是线程的启动。

5、线程的状态(基本)

在这里插入图片描述

6、线程常见方法

I.休眠 sleep(long millis);

(1).当前线程主动休眠 millis毫秒,进入有限期等待!

II. 放弃 yield();

(1).当前线程主动放弃时间片,回到就绪状态,竞争下一次时间片

III. 结合 join();

(1).允许其他线程加入到当前线程中,当前线程进入无限期等待!

7、线程的状态(等待)

在这里插入图片描述

8、线程安全的问题

I. 当多线程并发访问临界资源时,如果破坏了原子操作,可能会导致数据不一致。

II. 临界资源:共享资源(同一对象、堆空间),一次仅允许一个线程使用,才可保证其正确性

III.原子操作:不可分割的多步操作,被视作为一个整体,其顺序和步骤不能打乱或缺省。

9、synchronized 同步锁

I.每个对象都有一个互斥锁标记,用来分配给线程。

II.只有持有对象互斥锁标记的线程,才能进入对该对象加锁的同步操作中(同步方法、同步代码块)。

III.只有线程退出同步操作时,才会释放相应的锁标记

10、同步方式

I.同步代码块

(1). synchronized(临界资源对象){

//原子操作

}

II.同步方法

(1). synchronized 返回值类型 方法名成(参数列表){

//原子操作

}

11、线程的状态(阻塞)

在这里插入图片描述

12、同步规则

I.只有在调用包含同步代码块的方法或者是同步方法时,才需要对象的锁标记

II.如果调用的是不包含同步代码块的方法或普通方法时,则不需要锁标记,直接调用即可。

III.已知线程安全的内容:StringBuffer、Vector、Hashtable

13、线程通信

I.等待

(1)wait();

(2) 必须在对obj(对象)加锁的同步代码块(或同步方法)中,在一个线程执行期间,调用了obj.wait(),该线程会释放所拥有的锁标记。同时,进入到obj的等待队列中。等待唤醒

II. 通知(唤醒)

(1).notify();、notifyAll();

(2).必须在对obj加锁的同步代码块(或同步方法)中,从obj的Waiting(等待队列)中随机释放一个或全部线程。对自身线程无影响。

1、高级多线程

现有问题:

线程是宝贵的内存资源、单个线程约占1MB空间,过多分配容易造成内存溢出。 频繁的创建及销毁线程会增加虚拟机回收频率、资源开销、造成程序性能下降。 线程池:

线程容器,可设定线程分配数量上限。

将预先创建的线程对象存入池中,并重用线程池中的线程对象。

避免频繁的创建和消费。

3、线程池原理

在这里插入图片描述

将任务提交给线程池,由线程池分配线程、运行任务,并在当前任务结束后复用线程。

获取线程池:

常用的线程池接口和类(所在包java.util.concurrent):

Executor: 线程池的顶级接口。

ExecutorService: 线程池借口,可通过submit(Runnable task)提交任务代码。

Excutors工厂类:通过此类可以过得一个线程池。

通过newFixedThreadPool(int nThreads)获取固定数量的线程池。参数:指定线程池中线程的数量。

通过newCachedThreadPool()获得动态数量的线程池,如不够则创建新的,没有上线

Callable接口:

 public  interface  Callable<V>{
       public V call() throws Exception;
  }
 JDK5加入,与Runnable 接口类似,实现之后代表一个线程任务。
 Callable 具有泛型返回值、可以声明异常。

Future接口:

  概念:异步接收ExcuptorService.submit(); 所返回的状态结果,当中包含了call()的返回值。
  
  方法:V get() 以阻塞形式等待Future中的异步处理结果(call()的返回值)

线程的同步:

在这里插入图片描述

线程的异步:

在这里插入图片描述

Lock接口:

 JDK5加入,与synchronized比较,显示定义,结构更灵活。
 
 提供更多实用性方法,功能更强大、性能更优越。

常用方法:

  void lock();//获取锁,如锁被占用,则等待。
  
  boolean  tryLock();//尝试获取锁 (成功返回true。失败返回false,不阻塞)
  
  void   unlock(); //释放锁

重入锁:

   ReentrantLock:  Lock 接口的实现类,与synchronized一样具有互斥锁功能。
  ![在这里插入图片描述](https://p1-jj.byteimg.com/tos-cn-i-t2oaga2asx/gold-user-assets/2020/3/22/171022bde44c4bd6~tplv-t2oaga2asx-image.image)

读写锁: ReentrantReadWriteLock:

     一种支持一写多读的同步锁,读写分离,可分别分配读锁、写锁。
     
     支持多次分配读锁,使多个读操作可以并发执行。

互斥规则:

写-写:互斥、阻塞;
读-写: 互斥,不阻塞、写阻塞读。
读-读:  不互斥、不阻塞。
在读操作远远高于写操作的环境中,可在保障线程安全的情况下,提高运行效率。

线程安全的集合:

  Collection体系集合下,除Vector以外的线程安全集合。
![在这里插入图片描述](https://p1-jj.byteimg.com/tos-cn-i-t2oaga2asx/gold-user-assets/2020/3/22/171022bde64cf821~tplv-t2oaga2asx-image.image)

Collections中的工具方法:

   Collections工具类中提供了多个可以获得线程安全集合的方法。
    public static<T> Collection<T>synchronizedCollection(Collection<T>c);
    Public static<T> List<T>synchronizedList(List<T>list);
    pulic static<T>Set<T>synchronizedSet(Set<T>s);
    public static <K,V>Map<K,V>synchronizedMap(Map<k,V>m);
    public static <T>SortedSet<T>synchronizedSortedSet(SortedSet<T>s);
    pulic  static <K,V>SortedMap<K,V>synchronizedSortedMap(SortedMap<K,V>m):
    JDK1.2提供,接口统一,维护性高,但性能没有提升,均以synchonized实现。

CopyOnWriteArrayList:

     线程安全的ArrayList,加强版读写分离。
     写有锁,读无锁,读写之间不阻塞,优于读写锁。
     写入时,先copy一个容器副本、在添加新元素,最后替换引用。
     使用方式与ArrayList无异。
    ![在这里插入图片描述](https://p1-jj.byteimg.com/tos-cn-i-t2oaga2asx/gold-user-assets/2020/3/22/171022bde96975bc~tplv-t2oaga2asx-image.image)

CopyOnWriteArraySet

    线程安全的Set,底层使用CopyOnWriteArrayList实现。
    唯一不同在于,使用addIfAbsen()添加元素,会遍历数组。
    如存在元素,则不添加,(扔掉副本)。
   ![在这里插入图片描述](https://p1-jj.byteimg.com/tos-cn-i-t2oaga2asx/gold-user-assets/2020/3/22/171022bdec4a3314~tplv-t2oaga2asx-image.image)

ConcurrentHashMap

    初始容量默认为16段(Segment),使用分段锁设计。
    不对整个Map加锁,而是每个Segment加锁。
    当多个对象存入同一个Segment时,才需要互斥。
    最理想状态为16个对象分别存入16个Segment,并行数量16。
    使用方式与HashMap无异。

在这里插入图片描述

Queue接口(队列)

  Collection的子接口,表示队列FIFO(First  In First  Out)

常用方法:

   抛出异常: 
       boolean  add(E e) ;  //顺序添加一个元素(到达上限后,再添加则会抛出异常)
       E remove() ;      //获得第一个元素并移除(如果队列元素时,则抛异常)
       E element();    //获得第一个元素但不移除(如果队列没有元素时,则抛出异常)
    返回特殊值: 推荐使用
       boolean  offer(E e); //顺序添加一个元素(到达上限后,再添加则会返回false)
       E poll();  // 获得第一个元素并移除(如果队列没有元素时,则返回null)
       E keep();  //获得第一个元素但不移除(如果队列没有元素时,则返回null)

ConcurrentLinkedQueue 线程安全、可高效读写的队列,高并发下性能最好的队列。

       无锁、CAS比较交换算法,修改的方法包含三个核心参数(V,E,N)
       
       V: 要更新的变量、 E:预期值、 N: 新值。
       
       只有当V ==E 时,V =N;否则表示已被表示更新过,则取消当前操作。

在这里插入图片描述

BlockingQueue接口(阻塞队列)

    Queue的子接口,阻塞的队列,增加了两个线程状态为无限期等待的方法。
 方法:
     void put(E e); //将指定元素插入此队列中,如果没有可用空间,则等待。
     E  take();// 获取并移除队列头部元素,如果没有可用元素,则等待。
     可用于解决产生、消费者问题。

阻塞队列:

 ArrayBlockingQueue:
     数组结构实现,有界队列。(手工固定上限)
     ![在这里插入图片描述](https://p1-jj.byteimg.com/tos-cn-i-t2oaga2asx/gold-user-assets/2020/3/22/171022be19a24e59~tplv-t2oaga2asx-image.image)

LinkedBlockingQueue: 链表结构实现,无界队列。(默认上限Integer.MAX_VALUE)

在这里插入图片描述

ExecutorService线程池接口、Executors工厂。

Callable线程任务、Future异步返回值。

Lock、ReentrantLock重入锁、ReentrantReadWriteLock读写锁。

CopyOnWriteArrayList线程安全的ArrayList。

CopyOnWriteArraySet线程是安全的Set。

ConcurrentHashMap线程是安全的HashMap。

ConcurrentLinkedQueue线程安全的Queue。

ArrayBlockingQueue线程安全的阻塞Queue。(生产者、消费者)