Java 并发 线程状态转换 新建(New) 创建后尚未启动。 可运行(Runnable) 可能正在运行,也可能正在等待 CPU 时间片。 時雨:在 《Java 并发核心知识体系精讲》中,参考 Oracle 官方文档,标注实现多线程方式只有两种:实现 Runnable 接口和继承 Thread 类。 4 5 5 6 6 7 7 8 8 9 9 2. 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 4. 在 Java 内存模型中,允许编译器和处理器对指令进行重排序,重排序过程不会影响到单线程程序的执行,却会影响到多线程并发执行的正确性。
所以使用起来相对会有些不便,而Java在这方面提供了相应的同步容器,我们可以在多线程情况下可以结合实际场景考虑使用这些同步容器。 delIndex = v1.indexOf(integer); } } v1.remove(delIndex); } 最方便的方式就是使用jdk1.8提供的函数式编程接口 本小节将简单介绍一下并发容器,并发容器也称为J.U.C,即是其包名:java.util.concurrent。 ---- 4.HashMap对应的ConcurrentHashMap HashMap的并发安全版本是ConcurrentHashMap,但ConcurrentHashMap不允许 null 值。 的4倍左右。
转载:http://www.cnblogs.com/dolphin0520/p/3932921.html 一. java中的ThreadPoolExecutor类 java.util.concurrent.ThreadPoolExecutor 类时线程池中最核心的一个类,因此如果要透彻的了解java中线程池,必须先了解这个类。 ;然后就将任务也分配给这4个临时工人做;如果说这14个工人做任务的速度还是不够,此时工厂主管可能就要考虑不再接收新的任务或者抛弃前面的一些任务了。 这个例子中的corePoolSize就是10,而maximumPoolSize就是14(10+4)。 不过在java doc中,并不提倡我们直接使用ThreadPoolExecutor,而是使用Executors类中提供的几个静态方法来创建线程池: Executors.newCachedThreadPool
上篇文章 并发编程3:线程池的使用与执行流程 中我们了解到,线程池中需要使用阻塞队列来保存待执行的任务。这篇文章我们来详细了解下 Java 中的阻塞队列究竟是什么。 七种阻塞队列的前三种 Java 中提供了 7 种 BlockingQueue 的实现,在看线程池之前我根本搞不清楚究竟选择哪个,直到完整地对比总结以后,发现其实也没什么复杂。 notFull; //... } 可以看到,ArrayBlockingQueue 使用可重入锁 ReentrantLock 实现的访问公平性,两个 Condition 保证了添加和获取元素的并发控制 ,这样在添加、获取时通过判断数组元素个数可以感知到并发的获取/添加操作 ;此外就是链表比数组的优势了。 不了解 Comparator 和 Comparable 可以看这篇 Java 解惑:Comparable 和 Comparator 的区别。
Java中创建线程的三种方法以及区别 Java使用Thread类代表线程,所有的线程对象都必须是Thread类或其子类的实例。 Java可以用三种方式来创建线程,如下所示: 继承Thread类创建线程 实现Runnable接口创建线程 使用Callable和Future创建线程 线程池创建线程 下面让我们分别来看看这三种创建线程的方法 call()方法可以有返回值 call()方法可以声明抛出异常 Java5提供了Future接口来代表Callable接口里call()方法的返回值,并且为Future接口提供了一个实现类FutureTask 2); ThreadChi tc=new ThreadChi(); //参数1:目标对象 参数2:隔多长时间开始执行线程, 参数3:执行周期 参数4: 但是编程稍微复杂,如果需要访问当前线程,必须调用Thread.currentThread()方法。 继承Thread类的线程类不能再继承其他父类(Java单继承决定)。
以均衡与内存的速度差异 2、操作系统增加了进程、线程,以分时复用cpu,进而均衡cpu与io设备的速度差异 3、编译程序优化指令执行次序,使得cpu缓存能够得到更加合理利用 2)带来的问题(引出三大特性) 现实中的并发问题往往是三种问题的综合症 在解锁的时候,JVM需要强制刷新缓存,使得当前线程所修改的内存对其他线程可见 (3) final 当一个对象包含final修饰的实例字段时,其他线程能够看到已经初始化的final实例字段,这是安全的 二、java 内存模型(两大核心之一) 1)Java内存模型定义了线程和内存的交互方式 在JMM抽象模型中,分为主内存、工作内存。 把从执行引擎接收到的的值赋值给工作内存变量 Store(存储):把工作内存的变量值传递给主内存,以便后续的write使用 Write(写入):用于主内存变量,把store获得的变量的值放入主内存变量 3)内存模型解决并发问题主要采用两种方式 ,原来是happens-before第一条规则限制住了编译器的优化 2)volatile 变量规则 写先于读指的是不会因为cpu缓存,导致a线程已经写了,但是b线程没读到的情况 3) 管程中锁的规则 4)
并发与并行 并发指的是同时应对多个事件的能力,并行指的是同时做多件事的能力。 位级并行:32位计算机能够同时处理32位数运算,而8位计算机却要进行多次运算。 通过扩展cpu数量可以提高这种架构性能,但是SMP服务器cpu利用率最好是2-4个cpu。 java内存模型 java内存类似于SMP,但是其屏蔽了底层硬件环境的差异,给java提供了统一的内存访问模型。 java中所有线程共享主内存,对于每个线程都有自己的工作区,包括寄存器,栈,写换冲区,缓存,硬件,编译优化等。 多线程并发 并发问题也就带来来线程安全访问的问题。多线程执行时需要考虑进行额外的协调。 不可变性:可变数据是引起不安全的主要原因,如果一个数据不可变,则不会存在数据安全问题。
并行与并发 单核CPU下,线程实际还是 串行执行 的。 一般会将这种 线程轮流使用 CPU 的做法称为并发(concurrent)。 引用 Rob Pike 的一段描述: 并发(concurrent)是同一时间对应(dealing with)多件事情的能力 并行(parallel)是同一时间动手做(doing)多件事情的能力 生活例子 : 家庭主妇做饭、打扫卫生、洗衣服,她一个人轮流交替做多件事,这时就是并发 家庭主妇雇了一个保姆,她们一起做这些事,这时既有并发、也有并行(这时会产生竞争,例如洗衣机只有一台,一个人用洗衣机时,另一个人只能等待 $main$0(Test10.java:19) at java.lang.Thread.run(Thread.java:748) 08:32:04.201 [main] DEBUG io.ray -
Java并发编程的核心挑战 线程安全与数据竞争 线程安全的概念及其重要性 数据竞争的产生原因及常见场景 如何通过同步机制(如锁、原子类)避免数据竞争 // 示例:使用synchronized关键字实现线程安全 wait(); } buffer.remove(0); notify(); } } } 性能与可伸缩性 并发编程对性能的影响 (4); for (int i = 0; i < 10; i++) { executor.submit(() -> { // 任务逻辑 }); } executor.shutdown (); 并发工具类的使用 Java并发工具类(如CountDownLatch、CyclicBarrier、Semaphore)的应用场景 如何选择合适的工具类解决并发问题 工具类的使用注意事项 // 示例 并发编程的新特性(如CompletableFuture、Flow API) 多核处理器与并发编程的关系 如何应对分布式环境下的并发挑战 // 示例:使用CompletableFuture实现异步编程 CompletableFuture.supplyAsync
1.Java容器 1.1.同步容器 Vector ArrayList是最常用的List实现类,内部是通过数组实现的,它允许对元素进行快速随机访问。 public V put(K key, V value) { 2 3 return putVal(hash(key), key, value, false, true); 4 这个机制允许任意数量的读线程可以并发访问Map,读者和写者也可以并发访问Map,并且有限数量的写进程还可以并发修改Map,结果是为并发访问带来更高的吞吐量,同时几乎没有损失单个线程访问的性能。 并发队列 2.1.ConcurrentLinkedQueue ConcurrentLinkedQueue:是一个适用于高并发场景下的队列,通过无所的方式,实现了高并发状态下的高性能,通常ConcurrentLinkedQueue 在Java中,BlockingQueue的接口位于java.util.concurrent 包中(在Java5版本开始提供),由上面介绍的阻塞队列的特性可知,阻塞队列是线程安全的。
本章将着重介绍Java并发编程的基础知识,从启动一个线程到线程间不同的通信方式,最后通过简单的线程池示例以及应用(简单的Web服务器)来串联本章所介绍的内容。 [4] Signal Dispatcher // 分发处理发送给JVM信号的线程 [3] Finalizer // 调用对象finalize方法的线程 [2] Reference (3)更好的编程模型 Java为多线程编程提供了良好、考究并且一致的编程模型,使开发人员能够更加专注于问题的解决,即为所遇到的问题建立合适的模型,而不是绞尽脑汁地考虑如何将其多线程化。 一旦开发人员建立好了模型,稍做修改总是能够方便地映射到Java提供的多线程编程模型上。 线程的状态 Java线程在运行的生命周期中可能处于表4-1所示的6种不同的状态,在给定的一个时刻,线程只能处于其中的一个状态。 Java线程的状态  ? ?
1.并发编程基础 1.1 基本概念 CPU核心与线程数关系 Java中通过多线程的手段来实现并发,对于单处理器机器上来讲,宏观上的多线程并行执行是通过CPU的调度来实现的,微观上CPU在某个时刻只会运行一个线程 但对于一些要求同时进行并且又要共享某些变量的并发操作,只能用线程,不能用进程。 优缺点:线程和进程在使用上各有优缺点:线程执行开销小,但不利于资源的管理和保护;而进程正相反。 并行和并发 并行:同一时刻,可以同时处理事情的能力。 并发:与单位时间相关,在单位时间内可以处理事情的能力。 高并发编程的意义和注意事项 意义和好处:充分利用cpu的资源、加快用户响应的时间,程序模块化,异步化 问题。 修饰一个静态的方法 Synchronized也可修饰一个静态方法,用法如下: public synchronized static void method() { // todo } 4.
4 显示锁和AQS 4.1 Lock接口 核心方法 Java在java.util.concurrent.locks包中提供了一系列的显示锁类,其中最基础的就是Lock接口,该接口提供了几个常见的锁相关的操作 4. 公平锁 ReentrantLock内部利用AQS的线程队列,可以实现公平锁,但是性能相比非公平锁会差一点。 4.不管是ReadLock还是WriteLock都支持Interrupt,语义与ReentrantLock一致。 它是构建锁或者其他同步组件的基础框架(如ReentrantLock、ReentrantReadWriteLock、Semaphore等),是JUC并发包中的核心基础组件。 JUC并发包的作者(Doug Lea)期望它能够成为实现大部分同步需求的基础。 AQS解决了实现同步器时涉及到的大量细节问题,例如获取同步状态、FIFO同步队列。
---- Java内存模型 以上我们简单介绍了在多核并发的环境下CPU进行乱序执行优化时所带来的线程安全问题,为了保证线程安全,我们需要采取一些额外的手段去防止这种问题的发生。 不过在介绍如何采用实际手段解决这种问题之前,我们先来看看Java虚拟机是如何解决这种问题的:为了屏蔽各种硬件和操作系统内存的访问差异,以实现让Java程序在各种平台下都能达到一致的并发效果,所以Java 这意味着,如果你的Java程序是多线程的,在你的Java程序中每个CPU上一个线程可能同时(并发)执行。 这种情况下,我们就需要采取一些同步的手段,确保在并发环境下,程序处理结果的准确性。 ---- 并发的风险与优势 ?
5 并发容器 5.1 Hashtable、HashMap、TreeMap、HashSet、LinkedHashMap 在介绍并发容器之前,先分析下普通的容器,以及相应的实现,方便后续的对比。 1、K key 2、V value 3、Entry<K, V> next 4、int hash 5、Entry<K, V> before 6、Entry<K, V> after 1-4是从HashMap.Entry 1: // Transfer method in java.util.HashMap - 2: // called to resize the hashmap 3: 4: for (int j concurrencyLevel 并发度:默认16。 在高并发下的情况下如何保证取得的元素是最新的?
CAS 是什么 CAS 的全称 Compare-And-Swap,它是一条 CPU 并发。 它的功能是判断内存某一个位置的值是否为预期,如果是则更改这个值,这个过程就是原子的。 CAS 并发原体现在 JAVA 语言中就是 sun.misc.Unsafe 类中的各个方法。调用 UnSafe 类中的 CAS 方法,JVM 会帮我们实现出 CAS 汇编指令。 Unsafe类 unsafe类是CAS的核心类,由于java无法直接访问底层系统,需要通过本地(native)方法来访问,基于unsafe类可直接操作特定内存的数据unsafe类存在于sun.mics包中 因为 Java 中 CAS 操作执行依赖于 Unsafe 类。 变量 vauleOffset,表示该变量值在内存中的偏移量,因为 Unsafe 就是根据内存偏移量来获取数据的。 public class AtomicInteger extends Number implements java.io.Serializable { private static final
——摘自http://www.infoq.com/cn/articles/java-threadPool。 类图 ? 当新任务在方法 execute(java.lang.Runnable) 中提交时,如果运行的线程少于 corePoolSize,则创建新线程来处理请求,即使其他辅助线程是空闲的。 如果将 maximumPoolSize 设置为基本的无界值(如 Integer.MAX_VALUE),则允许池适应任意数量的并发任务。 throw new NullPointerException(); 10 return new RunnableAdapter<Object>(task, null); 11 } 参考资料 深入理解Java 线程池:ThreadPoolExecutor 聊聊并发(三)——JAVA线程池的分析和使用 FutureTask源码解析 FutureTask中的waiters为什么这么设计?
synchronized 是 Java 中最基本的同步机制,Lock 则是一种更灵活的同步机制,可以控制锁的获取和释放。
线程B .4. 线程B .5. 线程B .6. 线程B .7. 线程B .8. 线程B .9. 线程B .10. 线程A .4. 线程B .4. 线程B .5. 线程A .5. 线程A .6. 线程B .6. 线程B .7. 线程A .7. 线程B .8. 线程A .8. 线程A .9. 线程B .4. 线程B .5. 线程B .6. 线程B .7. 线程B .8. 线程B .9. 线程B .10. (AbstractQueue.java:98) at java.util.concurrent.ArrayBlockingQueue.add(ArrayBlockingQueue.java:312) ; import java.util.concurrent.ArrayBlockingQueue; import java.util.concurrent.BlockingQueue; import java.util.concurrent.SynchronousQueue
21、CycliBarriar 和 CountdownLatch 都是 Java 并发编程中的线程同步工具。 在写并发应用时,使用不可变对象可以提高性能和避免出现数据竞争的问题。因为如果一个对象被多个线程同时修改,那么就可能会发生数据错乱的情况。因此,使用不可变对象可以保证线程安全。 Java 中的线程调度算法有多种,包括先来先服务、最短作业优先、轮转调度等。具体使用哪种算法取决于系统的硬件资源和应用程序的需求。 在 Java 中,我们可以使用 ThreadGroup 类来创建和管理线程组。 t2); group.addThread(t3); for (Thread thread : group.getThreads()) { thread.start(); } 25、线程组是 Java