如何创建一个线程 按 Java 语言规范中的说法,创建线程只有一种方式,就是创建一个 Thread 对象。 而从 HotSpot 虚拟机的角度看,创建一个虚拟机线程 有两种方式,一种是创建 Thread 对象,另一种是创建 一个本地线程,加入到虚拟机线程中。 如果从 Java 语法的角度。有两种方法。 Executors JDK 的 java.util.concurrent.Executors 类提供了几个静态的方法,用于创建不同类型的线程池。 原理 JDK 中的线程池通过 HashSet 存储工作者线程,通过 BlockingQueue 来存储待处理任务。 另外,如果待处理队列中没有任务要处理,并且工作者线程数目超过了核心工作者数目,那么,需要减少工作者线程数目。
一、Java中锁分类 1.1 偏向锁/轻量级锁/重量级锁 这三种锁指的是synchronized锁的状态,Java1.6之前是基于重量级锁,Java1.6之后对synchronized进行了优化,为了减少获取和释放锁带来的性能消耗 java中的Synchronized内置锁和ReentrantLock显式锁都是独占锁。 2、共享锁 共享锁就是在同一时刻允许多个线程持有的锁。 在并发环境中,每个线程在获取锁时会先查看此锁维护的等待队列,如果为空,或者当前线程是等待队列的第一个,就占有锁,否则就会加入到等待队列中,以后会按照FIFO(先进先出)的规则从队列中取到自己。 3、例子 在Java的并发包中,ReentrantLock的创建可以指定构造函数的boolean类型来得到公平锁或非公平锁,默认是非公平锁。 二、synchronized锁 详细见Java并发——synchronized锁 三、Lock锁 Java并发——Lock锁 四、synchronized 和 Lock 对比 相同点: 1、synchronized
这是java高并发系列第2篇文章,一个月,咱们一起啃下java高并发,欢迎留言打卡,一起坚持一个月,拿下java高并发。 由于临界区的存在,多线程之间的并发必须受到控制。 根据控制并发的策略,我们可以把并发的级别分为阻塞、无饥饿、无障碍、无锁、无等待几种。 阻塞 一个线程是阻塞的,那么在其他线程释放资源之前,当前线程无法继续执行。 ,此时版本号为w_v 2.打开事务 3.做一些业务操作 4.update t set version = version+1 where id = 记录id and version = w_v;//此行会返回影响的行数 在无锁的情况下,所有的线程都能尝试对临界区进行访问,但不同的是,无锁的并发保证必然有一个线程能够在有限步内完成操作离开临界区。 在无锁的调用中,一个典型的特点是可能会包含一个无穷循环。 java高并发系列目录: 1.java高并发系列-第1天:必须知道的几个概念 希望您能把这篇文章分享给更多的朋友,让它帮助更多的人。帮助他人,快乐自己,最后,感谢您的阅读。微信扫码入群一起交流。
Java并发学习2【面试+工作】 三.synchronized&volatile synchronized 关键字synchronized的作用是实现进程间的同步。 volatile与synchronized volatile本质是在告诉jvm当前变量在寄存器中的值是不确定的,需要从主存中读取,synchronized则是锁定当前变量,只有当前线程可以访问该变量,其他线程被阻塞住 总结:volatile本质是在告诉JVM当前变量在寄存器中的值是不确定的,需要从主存中读取。 重入锁使用java.util.concurrent.locks.ReentrantLock类来实现。它的几个重要方法如下: ? signal() 方法用于唤醒一个在等待中的线程,这和obj.notify方法很类似。 简单示例 ? 代码中,听过lock生成一个与之绑定的Condition对象。
New(新创建) 2. Runnable(可运行) 3. Blocked(被阻塞) 4. Waiting(等待) 5. Timed Waiting(计时等待) 6. 当一个线程处于新创建状态,程序还没有开始运行线程中的代码,可以在之前进行一些基础工作 1.3 可运行线程 一旦调用start方法,线程处于runnable状态。 当线程等待另一个线程通知调度器一个条件时,它自己进入等待状态 在调用�Object.wait() Thread.join()或者是等待concurrent库中的Lock和Condition会出现 4. 因为run方法正常退出而死亡 2. Thread.State getState() //获得指定的线程状态 void stop() //停止该线程 2.1 线程属性 线程的属性包括:线程优先级、守护线程、线程组以及处理未捕获异常的处理器 Java
2.线程的并发工具类 2.1 Fork-Join JDK 7中引入了fork-join框架,专门来解决计算密集型的任务。 具体使用中,需要向ForkJoinPool线程池提交一个ForkJoinTask任务。 > t2) { int s1, s2; t2.fork(); if (((s1 = t1.doInvoke()) & ABNORMAL) ! CountDownLatch的用法如下, 初始化count down的次数; 在初始化线程中调用countDown对计数器进行减1; 工作线程中调用await进行等待,当计时器为0时,工作线程开始工作。 下面的例子中,在两个线程中分别填入数据,然后交换数据,最后打印从对方线程交换得来的数据。
[Java] Java 并发包中并发原理剖析之ConcurrentLinkedQueue ConcurrentLinkedQueue是线程安全的无界非阻塞队列,其底层数据结构使用单向链表实现,对于入队和出队操作使用 队列中包含的元素是从head访问的非空节点。通过CAS将节点的引用指向null,自动的会将它从队列中移除。 队列的头部 是队列中时间最长的元素。队列的尾部 是队列中时间最短的元素。新的元素插入到队列的尾部,队列获取操作从队列头部获得元素。 内存一致性效果:当存在其他并发 collection 时,将对象放入 ConcurrentLinkedQueue 之前的线程中的操作 happen-before 随后通过另一线程从 ConcurrentLinkedQueue REFERENCES Java并发编程之美 JDK-API-DOCS
如果使用多进程,那么并发数相对来说不会很高。而线程是更细小的调度单元,更加轻量级,所以线程会较为广泛的用于并发设计。 在Java当中线程的概念和操作系统级别线程的概念是类似的。 事实上,Jvm将会把Java中的线程映射到操作系统的线程区。 2. 线程的基本操作 2.1 线程状态图 ? 上图是Java中线程的基本操作。 当new出一个线程时,其实线程并没有工作。 如果不了解Java的中断机制,这样的一种解释极容易造成误解,认为调用了线程的interrupt方法就一定会中断线程。 其实,Java的中断是一种协作机制。 对于非阻塞中的线程, 只是改变了中断状态, 即Thread.isInterrupted()将返回true,并不会使程序停止; 1 2 3 4 5 6 public void run(){//线程t1 当一个Java应用内,所有非守护进程都结束时,Java虚拟机就会自然退出。 此前有写过一篇python中如何实现,查看这里。 而Java中变成守护进程就相对简单了。
转载请以链接形式标明出处: 本文出自:103style的博客 Java并发编程的艺术笔记 并发编程的挑战 Java并发机制的底层实现原理 Java内存模型 Java并发编程基础 Java中的锁的使用和实现介绍 Java并发容器和框架 Java中的12个原子操作类介绍 Java中的并发工具类 Java中的线程池 Executor框架 ---- 简介 在JDK的并发包里提供了几个非常有用的并发工具类。 ,所以必须等代码中的 第一个线程 和 线程A 都执行完之后,才会继续执行 主线程,然后输出 2,所以代码执行后的输出如下: 3 1 2 ---- CyclicBarrier的应用场景 CyclicBarrier 假如有一个需求,要读取几万个文件的数据,因为都是IO密集型任务,我们可以启动几十个线程并发地读取,但是如果读到内存后,还需要存储到数据库中,而数据库的连接数只有10个,这时我们必须控制只有10个线程同时获取数据库连接保存数据 虽然有20个线程在执行,但是只允许5个并发执行。
Java 中的并发(Concurrency) 指多个任务在同一时间段内交替执行(宏观上同时进行,微观上可能是 CPU 快速切换调度),目的是提高程序效率,充分利用系统资源(如 CPU、内存、I/O 等) 2. 并行(Parallelism)与并发(Concurrency)的区别并发:多个任务“交替执行”(CPU 切换速度快,看起来同时进行),适用于单 CPU 或多 CPU。 undefined例如:4 核 CPU 同时运行 4 个线程是并行,1 核 CPU 快速切换 4 个线程是并发。三、Java 实现并发的方式Java 提供了多种并发编程工具,核心是通过线程实现:1. 示例:两个线程同时对变量 count 做 ++ 操作,预期结果为 2,实际可能为 1(因 ++ 是多步操作,可能被打断)。2. 理解并发是 Java 进阶的关键,尤其在高并发场景(如分布式系统、高流量服务器)中,合理设计并发模型能显著提升系统性能。
java并发中ExecutorService的使用 ExecutorService是java中的一个异步执行的框架,通过使用ExecutorService可以方便的创建多线程执行环境。 第一种方式是使用Executors中的工厂类方法,例如: ExecutorService executor = Executors.newFixedThreadPool(10); 除了newFixedThreadPool Future<String>> futures = executorService.invokeAll(callableTasks); 关闭ExecutorService 如果ExecutorService中的任务运行完毕之后 ExecutorService和 Fork/Join java 7 引入了Fork/Join框架。那么两者的区别是什么呢? 本文的代码请参考https://github.com/ddean2009/learn-java-concurrency/tree/master/ExecutorService
java并发中CountDownLatch的使用 在java并发中,控制共享变量的访问非常重要,有时候我们也想控制并发线程的执行顺序,比如:等待所有线程都执行完毕之后再执行另外的线程,或者等所有线程都准备好了才开始所有线程的执行等 最后在主线程中调用await()方法来等待子线程结束执行。 我们是主线程等待子线程,那么在这个例子中,我们将会看看怎么子线程一起等待到准备好的状态,再一起执行。 思路也很简单,在子线程开始之后,将等待的子线程计数器减一,在主线程中await该计数器,等计数器归零之后,主线程再通知子线程运行。 sync.tryAcquireSharedNanos(1, unit.toNanos(timeout)); } 本文的例子可以参考https://github.com/ddean2009/learn-java-concurrency
9.2 改进的读写锁:StampedLock StampedLock是JDK 8中引入的新的锁机制,可以认为是读写锁的一个改进版本,读写锁虽然分离了读和写,使得读与读之间可以完全并发,但是读和写之间仍然是冲突的 简单粗暴的分散了高并发下的竞争压力。 答案就在LongAdder的java doc中,从我们翻译的那段可以看出,LongAdder适合的场景是统计求和计数的场景,而且LongAdder基本只提供了add方法,而AtomicLong还具有cas 从java doc中可以看出,其适用于统计计数的场景,例如计算qps这种场景。在高并发场景下,qps这个值会被多个线程频繁更新的,所以LongAdder很适合。 ---- 参考: https://www.jianshu.com/p/22d38d5c8c2a 《实战Java高并发程序设计》
Java 并发 线程状态转换 新建(New) 创建后尚未启动。 可运行(Runnable) 可能正在运行,也可能正在等待 CPU 时间片。 時雨:在 《Java 并发核心知识体系精讲》中,参考 Oracle 官方文档,标注实现多线程方式只有两种:实现 Runnable 接口和继承 Thread 类。 $Worker.run(ThreadPoolExecutor.java:617) at java.lang.Thread.run(Thread.java:745) 如果只想中断 Executor 中的一个线程 它是 JUC 并发包中的核心基础组件。 CountDownLatch 用来控制一个或者多个线程等待多个线程。 在 Java 内存模型中,允许编译器和处理器对指令进行重排序,重排序过程不会影响到单线程程序的执行,却会影响到多线程并发执行的正确性。
Java内存模型是围绕着原子性,有序性,可见性展开的。 volatile 能保证数据的可见性 volatile 能保证一定的有序性 volatile 不能保证操作的原子性。 2. volatile 不保证原子性 任意单个volatile变量的读/写具有原子性,但是类似于++这种复合操作不具有原子性 Java内存模型的原子性可以由synchronized和Lock实现。 例如: // x,y为非volatile变量 // flag为volatile变量 x = 2; //语句1 y = 0; //语句2 flag = true; //语句 并且volatile关键字能保证,执行到语句3时,语句1和语句2必定是执行完毕了的,且语句1和语句2的执行结果对语句3、语句4、语句5是可见的。 4. volatile原理 通过在指令序列中插入内存屏障实现来禁止指定类型的处理器重排序
上文说到synchronized,JAVA并发编程synchronized全能王的原理,虽然被评为并发全能王,不过用起来也是格外注意,不能搞大力出奇迹那一套,容易出现性能问题。 volatile是轻量级的并发解决方案,不会阻塞线程,是一种简单的同步机制。JAVA对volatile的定义是:volatile修饰的变量,在多线程并发读写场景下,可以保证变量的可见性和有序性。 在多线程并发到这一段代码语句123,CPU处理器就是按顺序执行。不好出现132顺序。 2.如何保证可见性 保证此变量的修改,能被所有线程及时看到。 java编译器会根据内存屏障的规则禁止重排序。 在对volatile变量写操作前,编译器会在写操作之后-》增加一个store屏障指令,让线程本地内存变量值能刷新到主内存中。 5.volatile怎么用更科学 像4的示例,volatile修饰的count并发++2w次,结果出现原子性问题。
synchronized和volatile都具有有序性,Java允许编译器和处理器对指令进行重排,但是指令重排并不会影响单线程的顺序,它影响的是多线程并发执行的顺序性。 但是换一个对象访问这个临界区就不会阻塞 2)synchronized修饰静态方法 由于静态成员不专属于任何一个实例对象,是类成员,因此通过class对象锁可以控制静态成员的并发操作。 Synchronized是Java并发编程中最常用的用于保证线程安全的方式,其使用相对也比较简单。 3)数组长度:如果对象是一个Java数组,那在对象头中还必须有一块用于记录数组长度的数据,因为虚拟机可以通过普通Java对象的元数据信息确定Java对象的大小,但是从数组的元数据中无法确定数组的大小。 2、轻量级锁 JVM的开发者发现在很多情况下,在Java程序运行时,同步块中的代码都是不存在竞争的,不同的线程交替的执行同步块中的代码。这种情况下,用重量级锁是没必要的。
java高并发系列目录 1.java高并发系列-第1天:必须知道的几个概念 2.java高并发系列-第2天:并发级别 3.java高并发系列-第3天:有关并行的两个重要定律 4.java高并发系列 - - 第11天:线程中断的几种方式 12.java高并发系列 - 第12天JUC:ReentrantLock重入锁 13.java高并发系列 - 第13天:JUC中的Condition对象 14.java 高并发系列 - 第14天:JUC中的LockSupport工具类 15.java高并发系列 - 第15天:JUC中的Semaphore(信号量) 16.java高并发系列 - 第16天:JUC中等待多线程完成的工具类 CountDownLatch 17.java高并发系列 - 第17天:JUC中的循环栅栏CyclicBarrier的6种使用场景 18.java高并发系列 - 第18天:JAVA线程池,这一篇就够了 19.java高并发系列 - 第19天:JUC中的Executor框架详解1
今天使用到了volatile关键字,之前了解到这个关键字,但是不知道他的具体作用是什么,下面就来详细解释一下他的作用: 在java线程并发处理中,有一个关键字volatile的使用目前存在很大的混淆,以为使用这个关键字 Java语言是支持多线程的,为了解决线程并发的问题,在语言内部引入了 同步块(synchronized) 和 volatile 关键字机制。 在Java内存模型中,有main memory,每个线程也有自己的memory (例如寄存器)。为了性能,一个线程会在自己的memory中保持要访问的变量的副本。 换句话说,另一个线程可能已经改变了它线程内的i1值,而这个值可以和当前线程中的i1值不相同。事实上,Java有个思想叫“主”内存区域,这里存放了变量目前的“准确值”。 线程请求获得监视this对象的对象锁(假设未被锁,否则线程等待直到锁释放) 2. 线程内存的数据被消除,从“主”内存区域中读入(Java虚拟机能优化此步。。。
而线程二则是先尝试获取lock2锁,成功获取lock2锁后再继续尝试获取lock1锁。 最终两个线程处于互相无线等待状态,获得lock1的线程一在等lock2,而获得lock2的锁却在等lock1。 ? ? 02 死锁的处理 由于死锁的检测涉及到很多复杂的场景,而且它还是运行时才会产生的,所以编程语言编译器一般也不会提供死锁的检测功能,包括Java也不提供死锁检测功能。 这其实就叫做鸵鸟算法,对于某件事如果我们没有很好的处理方法,那么就学鸵鸟一样把头埋入沙中假装什么都看不见。 死锁的处理主要包括锁的顺序化、资源合并、避免锁嵌套等事前预防措施和超时机制、抢占资源机制、撤销线程机制等事中的处理措施 - END -