Java并发编程
文章平均质量分 88
风度玉门
人得先清心寡欲,方能高枕无忧。
展开
-
面试-锁相关
7 死锁死锁是两个或更多线程阻塞着等待其它处于死锁状态的线程所持有的锁。死锁通常发生在多个线程同时但以不同的顺序请求同一组锁的时候。例如,如果线程1锁住了A,然后尝试对B进行加锁,同时线程2已经锁住了B,接着尝试对A进行加锁,这时死锁就发生了。线程1永远得不到B,线程2也永远得不到A,并且它们永远也不会知道发生了这样的事情。为了得到彼此的对象(A和B),它们将永远阻塞下去。这种情况就是一原创 2015-07-21 10:50:15 · 646 阅读 · 0 评论 -
线程池:ThreadPool
在多线程的开发当中,如果我们大量的使用new Thread()的方式去创建线程的话,则会使系统内存被过度消耗(JDK1.5以后,创建一个线程的开销是1M),从而使系统崩溃。 除此之外,线程创建和销毁的过程中也是很消耗资源的。很有可能出现,线程本身创建和销毁的开销比业务的开销还要大。因此,线程池是一个不二的选择,并且JUC提供了一套完善的线程池的类库,下面介绍一下。原创 2017-08-13 15:51:24 · 518 阅读 · 0 评论 -
ConcurrentHashMap的JDK1.8实现
今天我们介绍一下ConcurrentHashMap在JDK1.8中的实现。基本结构 ConcurrentHashMap在1.8中的实现,相比于1.7的版本基本上全部都变掉了。首先,取消了Segment分段锁的数据结构,取而代之的是数组+链表(红黑树)的结构。而对于锁的粒度,调整为对每个数组元素加锁(Node)。然后是定位节点的hash算法被简化了,这样带来的弊端是Hash冲突原创 2017-09-03 11:31:12 · 11331 阅读 · 8 评论 -
CopyOnWriteArrayList的实现
CopyOnWriteArrayList是自JDK5开始提供的支持并发读写的List。从名字可以大致的看出来其内部的设计思想:在写入前copy一份数据以提供修改。下面看一下具体的实现:基本结构 final transient ReentrantLock lock = new ReentrantLock(); private transient volatile Object[原创 2017-09-03 11:49:11 · 303 阅读 · 0 评论 -
ConcurrentLinkedQueue的实现
今天我们介绍一下ConcurrentLinkedQueue的内部实现。从名字就可以看出来,其内部使用链表实现。下面介绍一下它的结构:基本结构 单单从类图看来,结构不算复杂,有两个重要的属性就是head和tail。方法操作CAS操作 Node节点里面有几个CAS的操作,介绍一下: // CAS设置Node的值,cmp原创 2017-09-03 12:01:31 · 454 阅读 · 0 评论 -
Java锁优化
我们在多线程编程的过程中,对于锁的使用有一些可以优化的地方。下面介绍一下,自己在写业务代码中需要注意的问题和JVM对锁的优化操作。业务代码中的注意事项减少锁的持有时间:意思就是在同步快内部的代码,对于不需要同步控制的代码片段,尽量不要写在同步快内部。减小锁粒度:这个可以用JDK中的ConcurrentHashMap作为类比,将原先的对整个Map加锁,调整为对每个片段加锁。读写分离替原创 2017-09-03 12:12:32 · 811 阅读 · 0 评论 -
LongAdder | LongAccumulator简介
说到LongAdder,不得不提的就是AtomicLong。AtomicLong是JDK1.5开始出现的,里面主要使用了一个long类型的value作为成员变量。然后使用循环的CAS操作去操作value的值。优化思想 LongAdder是JDK1.8开始出现的,所提供的API基本上可以替换掉原先的AtomicLong。LongAdder所使用的思想就是热点分离,这一点可以类比原创 2017-09-03 20:18:23 · 5916 阅读 · 0 评论 -
StampedLock的简单用法
StampedLock是JDK1.8新引入的锁机制,可以简单的理解为读写锁的改进版本。我们知道读写锁可以让读和读之间完全并发,但是读和写之间是有阻塞的。StampedLock使用了一种乐观锁的读策略,这种机制类似于无锁的概念,使得获取锁的过程中不会阻塞写线程。简单例子 下面给出一个JDK官方给的Demo:public class Point { private d原创 2017-09-03 20:08:58 · 1795 阅读 · 0 评论 -
CompletableFuture用法
CompletableFuture是JDK1.8新增的工具类。它继承了Future和CompletionStage接口,里面提供了大量的链式调用的API。下面简单的介绍一下其用法:异步执行任务 CompletableFuture对接口的封装,使得调用者很容易实现Future模式的异步调用。如下Demo:public class CalcTest { public原创 2017-09-03 19:59:44 · 1562 阅读 · 0 评论 -
Future模式
Future模式是一种让调用线程可以异步处理任务的设计模式,它可以让调用线程利用异步处理的时间去执行其他任务,从而使硬件资源达到一个更好的利用率。参考以下时序图: 下面介绍一下各个成员的作用:MainTest:主线程入口,调用Client发出请求。Client:开启异步线程,并提交任务,返回FutureData。FutureData:同步返回的对象,对Re原创 2017-09-03 19:52:21 · 331 阅读 · 0 评论 -
CPU Cache的优化:解决伪共享问题
伪共享问题(false sharing) 对于解释伪共享问题,就需要了解一下缓存行的相关概念。缓存行是主存复制到高速缓存的最小单位,一般情况下缓存行的大小为32~128字节(通常为64字节)。 在多线程程序执行的过程中,有可能将2个或多个需要频繁修改的变量存储在同一个缓存行当中。这样以来,会频繁的造成缓存头失效的问题。如下图所示: ...原创 2017-09-03 19:42:04 · 1722 阅读 · 0 评论 -
线程分组 | 守护线程 | 线程优先级
线程分组 线程分组可以理解为一些线程的分类。在多线程的程序当中,在调试或者打印日志的时候,众多的线程一般很难区分开来。但是通过线程的groupName和threadName可以清晰的看出是哪个线程。但是首先你需要给线程或线程组命一个清晰明了的名字。 下面上一个demo:public class ThreadGroupName implements Runna原创 2017-08-05 20:29:13 · 369 阅读 · 0 评论 -
Java内存模型与volatile
Java内存模型(JMM) 在介绍volatile之前,先介绍一下java内存模型(JMM)。如下图所示: 每个Java线程在运行的过程中,都有一个与之对应的工作内存,这个内存空间是线程私有的。 当多个线程同时修改同一个对象时,线程会首先从主内存里面取出对象到工作内存当中去。然后更改完工作内存之后,再更新到主内存当中去。因此,对于普通的原创 2017-08-05 20:18:30 · 258 阅读 · 0 评论 -
面试-阻塞队列及线程池
16 阻塞队列 阻塞队列与普通队列的区别在于:当队列是空的时,从队列中获取元素的操作将会被阻塞,或者当队列是满时,往队列里添加元素的操作会被阻塞。http://ifeve.com/blocking-queues/17 线程池线程池(ThreadPool)对于限制应用程序中同一时刻运行的线程数很有用。因为每启动一个新线程都会有相应的性能开销,每个线程都需要给栈分配一些内存等等。原创 2015-07-21 10:52:41 · 1548 阅读 · 0 评论 -
面试-并发概述
1 多线程的优点 资源利用率更好 程序设计在某些情况下更简单 程序响应更快http://ifeve.com/benefits/2 多线程的代价 设计更复杂 上下文切换的开销 增加资源消耗http://ifeve.com/costs-of-multithreading/3 创建运行Thread的方式 创建Thread的子类原创 2015-07-21 10:47:40 · 769 阅读 · 0 评论 -
ConcurrentHashMap的JDK1.7实现
本篇文章主要介绍一下JDK1.7中的ConcurrentHashMap的一些代码结构。ConcurrentHashMap顾名思义就是线程安全的HashMap,相对于HashMap来说,可以保证线程安全的问题;对于HashTable来说可以提高存取的效率。基本结构 从上述类图可以看出,ConcurrentHashMap除了Map通用的get()、put()等方法外,还有原创 2017-08-18 22:41:34 · 1186 阅读 · 0 评论 -
Fork/Join框架
Fork/Join框架是Java7提供的用户并行任务执行的框架。原理是将一个大任务分割成多个小任务,最终再将小任务的结果汇聚,从而得到大任务的结果。工作窃取算法 工作窃取算法(work-stealing)是指某个线程从其他任务队列窃取任务来执行。如下图所示: 当我们处理一个大任务时,可以把它拆分成多个小任务,为了减少线程间的竞争,将其放到不同的队列中原创 2017-08-18 23:16:45 · 430 阅读 · 0 评论 -
重入锁ReentrantLock
线程的同步控制,最基础的就是synchronized关键字。但是在使用的过程中有一些限制,并没有那么的灵活。可重入 所以下面介绍一下ReentrantLock的使用和相关特性,一个简单的demo如下:public class ReenterLock implements Runnable { public static ReentrantLock lock = new原创 2017-08-13 14:57:39 · 270 阅读 · 0 评论 -
线程间通信:Condition | 信号量:Semaphore
线程间通信:Condition 对于线程间通信,大家最熟悉的莫过于Object.wait()、Object.notify()了。Condition的使用与前面的方法类似。 void await() throws InterruptedException; void awaitUninterruptibly(); long awaitNanos(long原创 2017-08-13 15:11:19 · 1488 阅读 · 0 评论 -
读写锁:ReadWriteLock
当我们在做加锁处理共享资源时,对于读远大于写的场景,可以选择ReadWriteLock作为同步控制的工具。下面是读写锁的互斥关系:读写锁的互斥关系 读写读非阻塞阻塞写阻塞阻塞 从上面互斥关系可以看出,只有当多线程同时加读锁时,线程可以不用进入阻塞状态。public class Re原创 2017-08-13 15:18:58 · 368 阅读 · 0 评论 -
倒计数器:CountDownLatch | 循环栅栏:CyclicBarrier
倒计数器:CountDownLatch CountDownLatch从名字就可以看出其作用:初始化一个计数,然后每次递减,直至为0,然后触发一个动作。只有一个带参构造器:public CountDownLatch(int count); 主要提供了以下方法:// 等待计数器为0public void await() throws InterruptedEx原创 2017-08-13 15:25:40 · 532 阅读 · 0 评论 -
线程阻塞工具:LockSupport
LockSupport是一个实用的线程阻塞工具,提供了线程阻塞和唤醒的静态方法。 LockSupport使用类似信号量的机制。当调用LockSupport.unpark()时,给当前线程提供了一个调用LockSupport.park()的许可。和信号量不同的是,这个许可不可以累加(即连续的unpark()和1次效果一样)。提供的主要方法如下:// 阻塞当前线程,只有调用unp原创 2017-08-13 15:33:26 · 456 阅读 · 0 评论 -
Java线程基本概念及操作
进程和线程 进程是计算机中的程序关于某数据集合上的一次运行活动,是系统进行资源分配和调度的基本单位,是操作系统结构的基础。线程可以理解为轻量级进程,是程序执行的最小单位。一个进程可以包含一个或者多个线程。 线程的生命周期如下所示:新建线程 方式一:继承Thread类,然后重写里面的run()方法。然后new一个对象,调用start(原创 2017-08-05 20:06:45 · 487 阅读 · 0 评论 -
生产者消费者模式
单生产者单消费者模式 生产者消费者模型是一个经典的问题,其主要有3部分构成:生产者、阻塞队列和消费者。如下图所示: 生产者消费者模式,可以在一定程度上面做到程序之间的模块解耦。但是还有一个主要的特性,就是它可以通过阻塞队列来协调生产者和消费者的处理速率不同的问题。下面给出一个简单的Demo:PCData:用于传输的数据public fin原创 2017-09-03 12:20:12 · 454 阅读 · 0 评论