文章目录

  • 并发编程三要素
  • 并发编程内存模型
  • 多线程
  • 创建线程的三种方式
  • volatile
  • synchronized
  • 线程池
  • ThreadPoolExcutor![在这里插入图片描述](https://img-blog.csdnimg.cn/714b156cd3bd441d94a10396580ab638.png)
  • 阻塞队列
  • 线程中断
  • 基础线程机制
  • 线程之间的协作
  • 线程的状态

并发编程三要素

  • 原子性:和数据库事务特性的原子性类似,一组操作要么全部失败,要么全部成功
  • 可见性:某个线程对变量的修改,对其它线程是可见的
  • 有序性:java编译器为了提升效率,会改变代码顺序,这违背了有序性,通过happen-before先行发送原则可以保证有序性

并发编程内存模型

多线程

多线程的目的是为了提高cpu的利用率,我们在学设计模式的时候单例模式的完美方案是要考虑多线程的,在学多线程我们需要了解操作系统讲的进程和线程、死锁,了解jvm的知识

创建线程的三种方式

  • 继承Thread,一般不推荐,因为java是单继承的
  • 实现Runnable接口,重写run方法
  • 实现Callable接口,Callable 可以有返回值,返回值通过 FutureTask 进行封装,

volatile

参考: https://jenkov.com/tutorials/java-concurrency/volatile.html

  • 不满足原子性
  • 满足可见性
  • 可以通过内存屏障防止指令重排保证有序性

场景

参考: https://blog.csdn.net/xichenguan/article/details/119425408

  • 当一个变量依赖其他变量或变量的新值依赖旧值时,不能用volatile

  • 适用场合:多个线程读,一个线程写的场合

  • 使用场景:通常被 作为标识完成、中断、状态的标记,值变化应具有原子性

这里简单说一说状态标志:比如有个boolean变量,如果多个线程都要访问它,作为判断标志,这时就可以使用volatile修饰该变量,可以保证多线程对变量的可见性

synchronized

Java 提供了两种锁机制来控制多个线程对共享资源的互斥访问,第一个是 JVM 实现的 synchronized,而另一个是 JDK 实现的 ReentrantLock

思考:volatile怎么保证可见性、volatile怎么保证有序性

参考博客:https://blog.csdn.net/weixin_37990128/article/details/110955558

四个窗口卖500张票:加sychronized

package interview.sort.test;public class SellTicket implements Runnable {int ticket = 500;public void sellTicket() throws InterruptedException {synchronized (this) {while (ticket > 0) {Thread.sleep(10);System.out.println(Thread.currentThread().getName() + "卖出第" + ticket-- + "张票");}}}@Overridepublic void run() {try {sellTicket();} catch (InterruptedException e) {e.printStackTrace();}}
}class Test {public static void main(String[] args) {SellTicket sellTicket = new SellTicket();Thread t1 = new Thread(sellTicket);Thread t2 = new Thread(sellTicket);Thread t3 = new Thread(sellTicket);Thread t4 = new Thread(sellTicket);t1.setName("窗口1");t2.setName("窗口2");t3.setName("窗口3");t4.setName("窗口4");t1.start();t2.start();t3.start();t4.start();}
}

四个窗口卖500张票:加可重入锁ReentrantLock

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;class SellTickets implements Runnable {int ticket = 500;private Lock lock = new ReentrantLock();public void sellTicket() throws InterruptedException {lock.lock();while (ticket > 0) {Thread.sleep(10);System.out.println(Thread.currentThread().getName() + "卖出第" + ticket-- + "张票");}lock.unlock();}@Overridepublic void run() {try {sellTicket();} catch (InterruptedException e) {e.printStackTrace();}}
}class Test {public static void main(String[] args) {SellTickets sellTicket = new SellTickets();Thread t1 = new Thread(sellTicket);Thread t2 = new Thread(sellTicket);Thread t3 = new Thread(sellTicket);Thread t4 = new Thread(sellTicket);t1.setName("窗口1");t2.setName("窗口2");t3.setName("窗口3");t4.setName("窗口4");t1.start();t2.start();t3.start();t4.start();}
}

四个窗口卖500张票:使用AtomicInteger实现用的是底层是CAS算法

import java.util.concurrent.atomic.AtomicInteger;public class SellTicket implements Runnable {AtomicInteger ticket = new AtomicInteger(500);public void sellTicket() throws InterruptedException {while (ticket.get() > 0) {Thread.sleep(10);System.out.println(Thread.currentThread().getName() + "卖出第" + ticket.decrementAndGet() + "张票");}}@Overridepublic void run() {try {sellTicket();} catch (InterruptedException e) {e.printStackTrace();}}
}class Test {public static void main(String[] args) {SellTicket sellTicket = new SellTicket();Thread t1 = new Thread(sellTicket);Thread t2 = new Thread(sellTicket);Thread t3 = new Thread(sellTicket);Thread t4 = new Thread(sellTicket);t1.setName("窗口1");t2.setName("窗口2");t3.setName("窗口3");t4.setName("窗口4");t1.start();t2.start();t3.start();t4.start();}
}

区别

  • 实现:synchronized是jvm实现的,ReentrantLock是jdk实现的
  • 性能:sychronized做了优化,速度和ReentrantLock差不多,前者的锁,jvm会帮我们自动释放,后者需要手动释放锁
  • 公平:公平锁是指多个线程在等待同一个锁时,必须按照申请锁的时间顺序来依次获得锁,synchronized是公平锁,ReetrantLock是非公平锁
  • 中断:当持有锁的线程长期不释放锁的时候,正在等待的线程可以选择放弃等待,改为处理其他事情,ReentrantLock 可中断,而 synchronized 不行
  • 绑定多对象:ReentrantLock可以绑定多个Condition对象

线程池

建立线程池好处:

  • 降低资源消耗。通过重复利用已创建的线程降低线程创建和销毁造成的消- 耗。
  • 提高响应速度。当任务到达时,任务可以不需要的等到线程创建就能立即执行。
  • 提高线程的可管理性。线程是稀缺资源,如果无限制的创建,不仅会消耗系统资源,还会降低系统的稳定性,使用线程池可以进行统一的分配,调优和监控

常见线程池

  • CachedThreadPool:一个任务创建一个线程;
  • FixedThreadPool:所有任务只能使用固定大小的线程;
  • SingleThreadExecutor:相当于大小为 1 的 FixedThreadPool;
  • newScheduledThreadPool:创建一个可以执行延迟任务的线程池;
  • newSingleThreadScheduledExecutor:创建一个单线程的可以执行延迟任务的线程池
  • newWorkSteadingThreadPool: 创建一个抢占式执行的线程池(任务执行顺序不确定),此方法是 JDK 1.8 版本新增的

ExecutorService executorService1 = Executors.newScheduledThreadPool(12);

ThreadPoolExecutor是Executor的实现类,可以通过它的execute()方法创建线程,构造方法里面需要设置很多参数

ThreadPoolExcutor

  • corePoolSize: 核心线程数
  • maximumPoolSize:最大线程数
  • keepAliveTime:空闲线程存活时间,当线程数量大于核心线程数小于最大线程数时,存在空闲的线程它们的存活时间就是这个,当存活的线程数小于等于核心线程数就失效了
  • unit:空闲线程的存活时间的单位
  • workQueue:工作队列,当线程数大于最大线程数时会将线程放到工作队列中,这里用的是阻塞队列BlockingQueue
  • threadFactory:线程工厂,用于创建工作线程
  • handler: 当工作队列达到上限的时候采取拒绝策略,常见的拒绝策略就是抛出异常

阻塞队列

  • ArrayBlockingQueue:先进先出,阻塞队列,插入元素时队列满了会一直等待。新元素插入到队列的尾部,队列获取操作则是从队列头部开始获得元素。

  • SynchronousQueue:数组结构的阻塞队列。队列满了以后,任何一次插入操作的元素都要等待相对的删除/读取操作。

  • LinkedBlockingQueue:先进先出,链表结构非阻塞队列,适合插入和删除较多的操作。

  • DelayedWorkQueue:延迟队列,延迟的时间还未到的获取的任务会返回空。

  • PriorityBlockingQueue:优先级队列,元素必须实现Comparable接口,优先级最高的始终在队列的头部,任务会优先执行。

线程中断

  • 在捕获异常中抛出中断异常
  • 将interrupted()方法当作标志状态,做判断,没中断在执行后面的语句
  • Executor可以通过shutdown()中断所有线程,如果要中断某一个线程就是用submit()方法会返回一个Future对象,该对象调用cancel(true)方法中断线程

基础线程机制

  • 线程睡眠sleep():单位是毫秒,是个本地方法,执行此方法会让线程睡眠

  • yield():是个本地方法,执行此方法代表当前线程已经执行差不都了,可以切换另外的线程;该方法只是向线程调度器提供一个建议,建议具有相同优先级的其它线程可以运行

  • 守护线程setDaemon():这时final修饰的方法,执行此方法是将线程设置为守护线程(后台线程),守护线程是指在后台执行的一种服务线程,这种线程不是必须的,当所有的非守护线程执行完毕后,程序停止,会杀死所有的守护线程

  • 线程优先级setPriority():final修饰的方法,为线程设置优先级,可以是1-10级,10级最高,目的是为了告诉线程调度器优先执行优先级高的线程

线程之间的协作

join(): final()修饰的方法,执行此方法当前线程将会挂起,执行目标线程后,在执行当前线程

wait()、notify()、notifyAll()

  • 调用wait()方法会使当前线程挂起,会释放锁,其它线程执行满足这个条件时其它线程会调用 notify() 或者 notifyAll() 来唤醒挂起的线程
  • final修饰,是本地方法,并且是Object里面的方法
  • 只能在同步方法或者同步控制块中使用

sleep()和wait()的区别

  • sleep()是Thread类的方法,wait()是Object的方法
  • 执行wait()时会释放锁,sleep()则不会释放锁
  • wait()只能在synchronized中使用

await()、signal()、signalAll()

  • juc包下Condition的方法,一个Lock可以创建多个Condition,synchronized块搭配wait()、notify()、notifyAll(),相当于仅有一个Condition,所有线程的调度通信都是由这个Condition完成的,不够灵活
  • await()对用wait(),signal()对用notify(),signalAll()对用notifyAll()

线程的状态

参考:https://www.cnblogs.com/IUbanana/p/7110297.html

  • 新建状态(New):新线程对象已经创建,还没有在其上调用start()方法

  • 就绪状态(Runnable):当前线程调用了start()方法,随时等待CPU调度执行

  • 运行状态(Running):当CPU开始调度处于就绪状态的线程时,此时线程才得以真正执行,即进入到运行状态。注:就绪状态是进入到运行状态的唯一入口,也就是说,线程要想进入运行状态执行,首先必须处于就绪状态中;

  • 阻塞状态(Blocked):处于运行状态中的线程由于某种原因,暂时放弃对CPU的使用权,停止执行,此时进入阻塞状态,直到其进入到就绪状态,才 有机会再次被CPU调用以进入到运行状态。根据阻塞产生的原因不同,阻塞状态又可以分为三种:

    • 等待阻塞 – 运行状态中的线程执行wait()方法,使本线程进入到等待阻塞状态;
    • 同步阻塞 – 线程在获取synchronized同步锁失败(因为锁被其它线程所占用),它会进入同步阻塞状态;
    • 其他阻塞 – 通过调用线程的sleep()或join()或发出了I/O请求时,线程会进入到阻塞状态。当sleep()状态超时、join()等待线程终止或者超时、或者I/O处理完毕时,线程重新转入就绪状态
  • 死亡状态(Dead):线程执行完了或者因异常退出了run()方法,当前线程的任务已处理完毕,释放CPU资源,运行结束的状态;

线程的状态这个人讲的不错:https://blog.csdn.net/weixin_44673534/article/details/121344594

java并发编程:多线程基础相关推荐

  1. Java并发编程-多线程基础

    Java多线程基础 1.多线程概述 实现线程的两种方式 继承Thread类 实现Runnable接口 2.线程生命周期 获取线程的名字和线程对象 3.线程的休眠 sleep方法 终止线程的休眠 强行终 ...

  2. java 并发编程多线程_多线程(一)java并发编程基础知识

    线程的应用 如何应用多线程 在 Java 中,有多种方式来实现多线程.继承 Thread 类.实现 Runnable 接口.使用 ExecutorService.Callable.Future 实现带 ...

  3. 学习笔记:Java 并发编程①_基础知识入门

    若文章内容或图片失效,请留言反馈. 部分素材来自网络,若不小心影响到您的利益,请联系博主删除. 视频链接:https://www.bilibili.com/video/av81461839 视频下载: ...

  4. Java并发编程实战基础概要

    文章目录 Java并发编程实战基础概要 开篇 多线程问题有啥难点呢? 为啥要学习并发编程? 并发问题的根源是什么? CPU切换线程执导致的原子性问题是如何发生的? 缓存导致的可见性问题是如何发生的? ...

  5. 并发编程-多线程基础

    1.引言 推荐书籍 深入理解Java并发编程 Java并发编程 核心知识点 多线程基础知识 同步和异步的概念 线程安全(线程同步)相关 线程通讯 java1.8并发包 线程池原理分析 锁的概念 专题类 ...

  6. JAVA并发编程的基础

    1.线程简介 什么是线程? 操作系统在运行一个程序时,会为其创建一个进程. 线程是操作系统调度的最小单元,也叫轻量级进程. 在一个进程里可以创建多个线程,这些线程拥有各自的计数器.堆栈和局部变量等属性 ...

  7. java并发编程艺术——基础篇

    这篇文章目的是为了总结一下这段时间看<java并发编程艺术>学到的东西,尝试用自己的话说出来对java多线程的理解和使用. 一.什么是多线程,为什么要用多线程,多线程带来的挑战 多线程定义 ...

  8. Java并发编程(多线程)中的相关概念

    众所周知,在Java的知识体系中,并发编程是非常重要的一环,也是面试中必问的题,一个好的Java程序员是必须对并发编程这块有所了解的. 并发必须知道的概念 在深入学习并发编程之前,我们需要了解几个基本 ...

  9. JAVA并发编程JUC基础学习(简介)

    2019独角兽企业重金招聘Python工程师标准>>> 之前写过一篇并发编程的简单实例应用,Future快速实现并发编程,可以很快的在自己的项目中应用,但并不系统,之前说过总结一篇( ...

最新文章

  1. tensorflow兼容处理 tensorflow.compat.v1 tf.contrib
  2. keras cnn注意力机制_从发展历史视角解析Transformer:从全连接CNN到Transformer
  3. matlab无限长序列卷积,怎样求未知长度序列的卷积
  4. Python 制作微信全家福,你就是朋友圈最亮的仔!
  5. 支持字典_【多测师小课堂】python数据类型之列表、字典、元祖、字符串
  6. linux的ctrl alt f6的作用,Linux(Centous6.4)操作系统中,快捷键Alt+Ctrl+F10是什么作用?...
  7. JavaScript新手入门教程大全~~~
  8. android so导致启动慢,谈谈Android NDK中动态链接库(.so文件)的优化
  9. 使用 CSS 模拟鼠标点击交互
  10. 【经典】MIT人工智能实验室: 如何做研究?
  11. Ubuntu上安装NS3(最详细的图文介绍)
  12. 一张图慢慢转换成下一张图_给一张照片做一个视频 如何把一张图片制作成几分钟的视频|图片做成视频软件...
  13. 快递查询单号查询追踪,一键查询全部物流
  14. 时间格式化 几分钟前 几小时前 几天前
  15. 比例谐振(PR)控制
  16. python通信达数据_Python读取通达信数据
  17. html/css做一个简单的个人简历
  18. 传统特征点检测器的检测特征点和匹配流程
  19. 角色转移服务器维护怎么回事,梦幻西游角色转移热点问题解答
  20. Excel取消合并自动填充

热门文章

  1. win10系统笔记本电脑如何通过USB连接iphone热点
  2. 我的勇者服务器显示关闭,我的勇者退出工会流程一览
  3. mysql数据库性别字段你选用什么数据类型呢
  4. 图片放大不清晰怎么办?无损放大的好方法
  5. UE4 安卓开发 如何接入PicoVR设备,
  6. oculus quest+alvr vr设备quest系统升级后黑屏问题
  7. IOS开发者账号如何续费-Appstore
  8. scrapy中间件详解
  9. python加中文注释_Python使用中文注释和输出中文(原创)
  10. Jaccard 相似度