Java 多线程知识点

Java 多线程

知识点归类github地址 https://github.com/bage2014/interview
多线程代码demo实现github地址https://github.com/bage2014/study/tree/master/study-java/src/main/java/com/bage/study/java/multhread

to be continue! 提纲已经就绪,会不断进行完善!!!

多线程理论基础

什么是多线程

线程和进程

线程安全

重入锁

死锁的四个条件

  • 互斥
  • 请求与保持
  • 不剥夺
  • 循环等待

预防死锁

检查死锁

  • Jconsole查看死锁
  • Jstack查看死锁

volatile

  • JMM
  • 内存可见性
  • 防止指令重排序
  • 不能保证原子性

synchronized

  • 作用

    1. 互斥访问
    2. 内存可见性
    3. 防止指令重排序
  • 用法

    1. 修饰普通方法
    2. 修饰静态方法
    3. 修饰代码块
  • 注意点

    1. 当一个线程在访问对象的 synchronized 方法,因为对象只有一把锁,其他线程无法获取该对象的锁,所以无法访问该对象的其他synchronized实例方法,但是其他线程还是可以访问该实例对象的其他非synchronized方法
    2. 实现原理为,对象监视器,Monitor

volatile vs synchronized vs lock

  • 来源差异

volatile、synchronized为Java关键字;
lock是Java类

  • 代价开销

volatile不是锁,代价最小;
lock是一般基于AQS,相对比synchronized代价小;
synchronized代价最大

  • 简单性

volatile、synchronized为Java关键字,JVM全权帮忙维护,只要我们能正确使用,不需要我们太多关心维护;
lock是Java类,有很多方法可以调用,灵活性最好,但是需要自己控制锁的获取、释放

进程间通信

  • 管道pipe
  • 命名管道FIFO
  • 消息队列MessageQueue
  • 共享存储SharedMemory
  • 信号量Semaphore
  • 套接字Socket
  • 信号 ( sinal )

并发和并行

创建线程

  • 集成Thread类
  • 实现Runable接口
  • 实现Callable接口

Object的wait、notify方法

sleep、 await方法差别###

多线程常用类

参考地址 http://www.importnew.com/21889.html

Thread

CAS

  • ABA问题

原子操作类

CountDownLatch

  • 某线程需要等待多个线程执行完毕,再执行。
  • 实现多个线程共同等待,同时开始执行任务,不可重用。(此类似于CyclicBarrier,可重用)

CyclicBarrier

  • CyclicBarrier允许一组线程互相等待,直到到达某个公共屏障点。
  • 与CountDownLatch不同的是该barrier在释放等待线程后可以重用,所以称它为循环(Cyclic)的屏障

CountDownLatch、CyclicBarrier差别

  • CountDownLatch和CyclicBarrier都能够实现线程之间的等待,只不过它们侧重点不同:
    CountDownLatch一般用于某个线程A等待若干个其他线程执行完任务之后,它才执行;
    而CyclicBarrier一般用于一组线程互相等待至某个状态,然后这一组线程再同时执行;
  • CountDownLatch是不能够重用的,而CyclicBarrier是可以重用的。

Semaphore

  • Semaphore是一个计数信号量,它的本质是一个”共享锁”。
  • 信号量维护了一个信号量许可集。线程可以通过调用acquire()来获取信号量的许可;当信号量中有可用的许可时,线程能获取该许可;否则线程必须等待,直到有可用的许可为止。 线程可以通过release()来释放它所持有的信号量许可。
  • Semaphore其实和锁有点类似,它一般用于控制对某组资源的访问权限。

Exchanger

  • 用于进行线程间的数据交换。
  • 两个线程通过exchange方法交换数据
  • 该工具类的线程对象是成对的

ThreadLocal

参考链接 https://blog.csdn.net/u012088516/article/details/84067841https://blog.csdn.net/bntx2jsqfehy7/article/details/78315161

  • 每个Thread 维护一个 ThreadLocalMap 映射表,这个映射表的 key 是 ThreadLocal实例本身,value 是真正需要存储的 Object。
  • ThreadLocal 本身并不存储值,它只是作为一个 key 来让线程从 ThreadLocalMap 获取 value。值得注意的是图中的虚线,表示 ThreadLocalMap 是使用 ThreadLocal 的弱引用作为 Key 的,弱引用的对象在 GC 时会被回收。
  • ThreadLocalMap使用ThreadLocal的弱引用作为key,如果一个ThreadLocal没有外部强引用来引用它,那么系统 GC 的时候,这个ThreadLocal势必会被回收,这样一来,ThreadLocalMap中就会出现key为null的Entry,就没有办法访问这些key为null的Entry的value,如果当前线程再迟迟不结束的话,这些key为null的Entry的value就会一直存在一条强引用链:Thread Ref -> Thread -> ThreaLocalMap -> Entry -> value永远无法回收,造成内存泄漏。
  • ThreadLocal里面使用了一个存在弱引用的map, map的类型是ThreadLocal.ThreadLocalMap. Map中的key为一个threadlocal实例。这个Map的确使用了弱引用,不过弱引用只是针对key。每个key都弱引用指向threadlocal。 当把threadlocal实例置为null以后,没有任何强引用指向threadlocal实例,所以threadlocal将会被gc回收。
    但是,我们的value却不能回收,而这块value永远不会被访问到了,所以存在着内存泄露。因为存在一条从current thread连接过来的强引用。只有当前thread结束以后,current thread就不会存在栈中,强引用断开,Current Thread、Map value将全部被GC回收。最好的做法是将调用threadlocal的remove方法,这也是等会后边要说的。
  • 使用static的ThreadLocal,延长了ThreadLocal的生命周期,可能导致内存泄漏。
  • 分配使用了ThreadLocal又不再调用get(),set(),remove()方法,那么就会导致内存泄漏,因为这块内存一直存在。

线程池

参考链接 https://www.cnblogs.com/dongguacai/p/6030187.htmlhttps://www.cnblogs.com/szwh/p/7761171.htmlhttps://blog.csdn.net/shahuhubao/article/details/80311992https://justsee.iteye.com/blog/999189

构造函数参数
	int corePoolSize = 2; // 核心线程池大小
	int maximumPoolSize = 5; // 最大线程池大小
	long keepAliveTime = 10; // 线程池中超过corePoolSize数目的空闲线程最大存活时间;可以allowCoreThreadTimeOut(true)使得核心线程有效时间
	TimeUnit unit = TimeUnit.SECONDS; // keepAliveTime时间单位
	BlockingQueue<Runnable> workQueue; // 阻塞任务队列
	ThreadFactory threadFactory; // 新建线程工厂
	RejectedExecutionHandler handler; //  当提交任务数超过maxmumPoolSize+workQueue之和时,任务会交给RejectedExecutionHandler来处理
BlockingQueue阻塞队列
  • ArrayBlockingQueue

基于数组实现的一个阻塞队列,在创建ArrayBlockingQueue对象时必须制定容量大小。并且可以指定公平性与非公平性,默认情况下为非公平的,即不保证等待时间最长的队列最优先能够访问队列。

  • LinkedBlockingQueue

基于链表实现的一个阻塞队列,在创建LinkedBlockingQueue对象时如果不指定容量大小,则默认大小为Integer.MAX_VALUE。

  • PriorityBlockingQueue[praɪˈɒrəti]

以上2种队列都是先进先出队列,而PriorityBlockingQueue却不是,它会按照元素的优先级对元素进行排序,按照优先级顺序出队,每次出队的元素都是优先级最高的元素。注意,此阻塞队列为无界阻塞队列,即
容量没有上限(通过源码就可以知道,它没有容器满的信号标志),前面2种都是有界队列。

  • DelayQueue

基于PriorityQueue,一种延时阻塞队列,DelayQueue中的元素只有当其指定的延迟时间到了,才能够从队列中获取到该元素。DelayQueue也是一个无界队列,因此往队列中插入数据的操作(生产者)永远不会
被阻塞,而只有获取数据的操作(消费者)才会被阻塞。

RejectedExecutionHandler拒绝策略

超出线程范围和队列容量的任务的处理程序

  • ThreadPoolExecutor.AbortPolicy:丢弃任务并抛出(默认)RejectedExecutionException异常。
  • ThreadPoolExecutor.DiscardPolicy:也是丢弃任务,但是不抛出异常。
  • ThreadPoolExecutor.DiscardOldestPolicy:丢弃队列最前面的任务,然后重新尝试执行任务(重复此过程)
  • ThreadPoolExecutor.CallerRunsPolicy:由调用线程处理该任务
提交过程
1.	校验当前执行的线程数,是否小于 corePoolSize ,小于corePoolSize,则创建一个线程执行任务
2.	否则,尝试添加到阻塞队列中,如果能够添加,则提交过程结束
3.	否则,如果阻塞队列已经无法添加,则校验当前线程数是否达到maximumPoolSize个数,如果没有达到,创建新线程执行任务
4.	否则,如果已经达到maximumPoolSize个数,此时采取RejectedExecutionHandler拒绝策略进行拒绝操作
  • 源代码

      public void execute(Runnable command) {
      if (command == null)
          throw new NullPointerException();
      int c = ctl.get();
      if (workerCountOf(c) < corePoolSize) {
          if (addWorker(command, true))
              return;
          c = ctl.get();
      }
      if (isRunning(c) && workQueue.offer(command)) {
          int recheck = ctl.get();
          if (! isRunning(recheck) && remove(command))
              reject(command);
          else if (workerCountOf(recheck) == 0)
              addWorker(null, false);
      }
      else if (!addWorker(command, false))
          reject(command);
    

    }

excute vs submit
  • excute 无返回值,submit有返回值
  • submit里面还是调用了excute方法
shutdown vs shutdownnow
  • shutDown()

    1. 线程池的状态编程SHUTDOWN
    2. 不能再往线程池中添加任何任务,否则将会抛出RejectedExecutionException异常。
    3. 此时线程池不会立刻退出,会把线程池中的任务都已经处理完成,才会退出。
  • shutdownNow()

    1. 线程池的状态立刻变成STOP状态,并通过调用Thread.interrupt()方法试图停止所有正在执行的线程,不再处理还在池队列中等待的任务
    2. 它会返回那些未执行的任务。
    3. ShutdownNow()并不代表线程池就一定立即就能退出,它可能必须要等待所有正在执行的任务都执行完成了才能退出。
线程池状态

Running、ShutDown、Stop、Tidying、Terminated

线程池默认4个实现类

AQS

偏向锁、轻量级锁、重量级锁、自旋锁

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Java 多线程是指在一个程序中可以同时执行多个线程,并且每个线程独立运行,互不干扰。Java 多线程Java 平台提供的重要特性,常用于提高程序并发性能和解决复杂问题。 Java 多线程的重点知识点包括以下几个方面: 1. 线程的创建和启动:可以通过继承 Thread 类或实现 Runnable 接口来创建线程,并使用 start() 方法启动线程。 2. 线程的生命周期:线程的生命周期包括新建、就绪、运行、阻塞和死亡等阶段,可以通过调用 sleep()、wait()、join() 等方法来控制线程的状态转换。 3. 线程同步:多个线程之间可能会共享资源,为了保证数据的一致性和避免冲突,需要使用同步机制,如 synchronized 关键字、Lock 接口等。 4. 线程间通信:多个线程之间可以通过共享内存或者消息传递的方式进行通信,如使用 wait()、notify()、notifyAll() 等方法。 5. 线程池:线程池可以管理和复用线程,减少线程的创建和销毁开销,提高系统的性能。 6. 线程安全性:在多线程环境下,存在资源竞争和线程安全性问题,需要采取相应的措施来保证线程的安全性,如使用 synchronized 关键字、volatile 关键字等。 7. 线程调度:Java 提供了线程调度器来控制线程的执行顺序和优先级,可以使用 yield()、join()、setPriority() 等方法进行调度。 8. 线程异常处理:在多线程环境下,线程的异常需要处理和捕获,可以使用 try-catch 块来捕获异常。 综上所述,Java 多线程Java 平台提供的重要特性,掌握多线程的概念和相关知识点可以帮助开发者提高程序的并发性能和解决复杂问题。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值