JUC介绍 以及JUC中的锁框架
Java JUC 简介
在Java 5.0 提供了java.util.concurrent(简称JUC )包,在此包中增加了在并发编程中很常用的实用工具类。
用于定义类似于线程的自定义子系统,包括线程池、异步IO 和轻量级任务框架。提供可调的、灵活的线程池。还提供了设计用于多线程上下文中的Collection 实现(JUC集合)
锁结构:
下面这个图更方便理解:
在本篇文章之前,我们已经对并发机制中出现的很多的常见知识点进行了总结,今天我们来回顾一下有哪些是JUC包中的。
1.volatile关键字:
当多个线程共享某一个变量的时候,可以实现内存可见性(内存中的数据可见),相较于synchronized是一个轻量级的同步机制。但是volatile不保证原子性和有序性还有一个互斥性。
2.JUC中提供的原子变量类:
包括AtomicInteger等Atomic家族类。查看源码即可知道,其中的变量都是用volatile修饰保证内存可见性,通过CAS算法保证数据的原子性。
CAS:三个参数:V( 内存中的值) A 预估值 B更新值 当且仅当V==A时,将B更新值赋给V,否则不做任何操作。
3.1ConcurrentHashMap锁分段机制
a. java5.0在JUC包中提供了多种并发容器来改进同步容器的性能;、
b. concurrentHashMap同步容器是java5增加的一个线程安全的哈希表,对多线程的操作介于HashMap和HashTable之间,内部采用了“锁分段”机制代替了HashTable的独占锁,进而提高性能。
c. 此包还提供了设计用于多线程上下文中的Collection实现:ConcurrentHashMap、ConcurrentSkipListMap、ConcurrentSkipListSet、CopyOnWriteArrayList和CopyOnWriteArraySet。当期望许多线程访问一个给定collection时, ConcurrentHashMap通常优于同步的HashMap,ConcurrentSkipListMap通常优于同步的TreeMap.当期望的读数和遍历远远大于列表的更新数时,CopyOnWriteArrList优于同步的ArrayList。
3.2CountDownLatch闭锁操作
CountDownLatch:闭锁,在完成某些运算时,只有其他所有的线程的运算全部完成,当前运算才算执行。
CountDownLatch 一个同步辅助类,在完成一组正在其他线程中执行的操作之前,它允许一个或多个线程一直等待。
闭锁可以延迟线程的进度直到其达到终止状态,闭锁可以用来确保某些活动直到其他活动都完成才继续执行:
确保某个计算在其需要的所有资源都被初始化之后才继续执行
确保某个服务在其依赖的所有其他服务都已经启动之后才启动
等待直到某个操作所有参与者都准备就绪在继续执行
以下代码是用通过闭锁计算10线程执行的时间
4.线程池
线程池:提供了一个线程队列,队列中保存着所有等待状态的线程,避免了创建与销毁额外开销,提高了响应的速度。
Java.util.concurrent.Executor: 负责线程的使用与调度的根接口;
线程池的体系结构:
- java.util.concurrent.Executor : 负责线程的使用与调度的根接口
-
|--**ExecutorService 子接口: 线程池的主要接口
-
|--ThreadPoolExecutor 线程池的实现类
-
|--ScheduledExecutorService 子接口:负责线程的调度
-
|--ScheduledThreadPoolExecutor :继承 ThreadPoolExecutor, 实现 ScheduledExecutorService
工具类 : Executors
- ExecutorService newFixedThreadPool() : 创建固定大小的线程池
- ExecutorService newCachedThreadPool() : 缓存线程池,线程池的数量不固定,可以根据需求自动的更改数量。
- ExecutorService newSingleThreadExecutor() : 创建单个线程池。线程池中只有一个线程
- ScheduledExecutorService newScheduledThreadPool() : 创建固定大小的线程,可以延迟或定时的执行任务。
*/
5.Lock:Lock同步锁
用于解决多线程安全问题的方式。
synchronized:隐式锁,同步代码块、同步方法。
Jdk1.5后:同步锁Lock,是一种显式锁 ,需要通过lock()方式上锁,必须通过unlock方法进行释放锁;
Lock实现线程等待唤醒机制
6.实现Callable接口:
创建执行线程的方式三:实现Callble接口。相较于实现Runnable接口的方式,方法可以有返回值,并且可以抛出异常
public class TestCallable {
public static void main(String[] args) {
ThreadDemo td = new ThreadDemo();
// 1.执行Callable方式,需要FutureTask实现类的支持,用于接收运算结果。
FutureTask<Integer> result = new FutureTask<Integer>(td);
new Thread(result).start();
// 2.接收线程运算后的结果
try {
Integer sum = result.get();
System.out.println(sum);
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
}
}
class ThreadDemo implements Callable<Integer>{
@Override
public Integer call() throws Exception {
int sum = 0;
for(int i = 0; i<=100;i++){
System.out.println(i);
sum+=i;
}
return sum;
}
}
7.Condition 控制线程通信
Condition 接口描述了可能会与锁有关联的条件变量。
这些变量在用法上与使用Object.wait 访问的隐式监视器类似,但提供了更强大的功能。
需要特别指出的是,单个Lock 可能与多个Condition 对象关联。
为了避免兼容性问题,Condition 方法的名称与对应的Object 版本中的不同。
在Condition 对象中,与wait、notify 和notifyAll 方法对应的分别是await、signal 和signalAll。
Condition 实例实质上被绑定到一个锁上。要为特定Lock 实例获得Condition 实例,请使用其newCondition() 方法。
/*
* 生产者消费者案例:
*/
public class TestProductorAndConsumerForLock {
public static void main(String[] args) {
Clerk clerk = new Clerk();
Productor pro = new Productor(clerk);
Consumer con = new Consumer(clerk);
new Thread(pro, "生产者 A").start();
new Thread(con, "消费者 B").start();
// new Thread(pro, "生产者 C").start();
// new Thread(con, "消费者 D").start();
}
}
class Clerk {
private int product = 0;
private Lock lock = new ReentrantLock();
private Condition condition = lock.newCondition();
// 进货
public void get() {
lock.lock();
try {
if (product >= 1) { // 为了避免虚假唤醒,应该总是使用在循环中。
System.out.println("产品已满!");
try {
condition.await();
} catch (InterruptedException e) {
}
}
System.out.println(Thread.currentThread().getName() + " : "
+ ++product);
condition.signalAll();
} finally {
lock.unlock();
}
}
// 卖货
public void sale() {
lock.lock();
try {
if (product <= 0) {
System.out.println("缺货!");
try {
condition.await();
} catch (InterruptedException e) {
}
}
System.out.println(Thread.currentThread().getName() + " : "
+ --product);
condition.signalAll();
} finally {
lock.unlock();
}
}
}
// 生产者
class Productor implements Runnable {
private Clerk clerk;
public Productor(Clerk clerk) {
this.clerk = clerk;
}
@Override
public void run() {
for (int i = 0; i < 20; i++) {
try {
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
clerk.get();
}
}
}
// 消费者
class Consumer implements Runnable {
private Clerk clerk;
public Consumer(Clerk clerk) {
this.clerk = clerk;
}
@Override
public void run() {
for (int i = 0; i < 20; i++) {
clerk.sale();
}
}
}
读写锁
线程按序交替
ForkJoinPool分支/合并框架工作窃取
Fork Join与线程池的区别
JUC篇具体介绍:https://blog.****.net/cx8122389/article/details/70049425
java多线程各个点的详细介绍 有43篇呢
http://www.cnblogs.com/skywang12345/p/java_threads_category.html