Java并发、同步总结
来源:互联网 发布:尚学堂java ppt 编辑:程序博客网 时间:2024/06/09 13:50
Java中提供并发控制的两种方式:1、同步关键字 2、锁
Java 5.0之前使用的是同步关键字Synchronized和volatile,他们是jvm中的隐式锁
Synchronized和volatile的实现是基于jvm指令的同步代码块实现的。
添加同步关键字后,会在jvm代码块指令前后添加monitorexit和monitorenter两个同步指令。
但是Synchronized修饰的方法却不一样,同步方法的实现是JVM定义了方法的访问标志ACC_SYNCHRONIZED在方法表中,JVM将同步方法前面设置这个标志。
同步代码块和同步方法的字节码指令(javap -c classname)
public void syncBlockImpl(); Code: 0: aload_0 1: dup 2: astore_1 3: monitorenter 4: getstatic #15 // Field java/lang/System.out:Ljava/io/PrintStream; 7: ldc #23 // String hello world 9: invokevirtual #22 // Method java/io/PrintStream.println:(Ljava/lang/String;)V 12: aload_1 13: monitorexit 14: goto 22 17: astore_2 18: aload_1 19: monitorexit 20: aload_2 21: athrow 22: return Exception table: from to target type 4 14 17 any 17 20 17 any public synchronized void syncMethodImpl(); Code: 0: getstatic #15 // Field java/lang/System.out:Ljava/io/PrintStream; 3: ldc #23 // String hello world 5: invokevirtual #22 // Method java/io/PrintStream.println:(Ljava/lang/String;)V 8: return
Java 5.0之后,JDK添加了多线程并发库java.util.concurrent,提供了线程池(Executors)、信号量(Semaphore)、重入锁(ReentrantLock)、读写锁(ReentrantWriteReadLock)和一些线程安全的集合类(ConcurrentHashMap/ConcurrentLinkedQueue)、CountDownLatch
无论是Semaphore还是ReentrantLock都是基于AbstractQueueSynchronized实现的,AQS实现了自己的算法来实现共享资源的合理控制。AQS中核心的变量替换方法(原子性)是借助CPU硬件指令集compareAndSweep实现的,等待队列是由虚拟无界双向链表CLH实现的。每一个等待线程对一个Node,Node中存储了当前线程引用、等待状态、前一个或后一个等待节点。
CLH锁,是一种基于链表的可扩展、高性能、公平的自旋锁。
锁分为公平锁和协商锁(非公平锁),其中公平锁的开销要大。
一、信号量Semaphore的用法
ExecutorService executorService = Executors.newCachedThreadPool();
final Semaphore semaphore = new Semaphore(5, false);//非公平信号量
//开启20个线程
for (int i = 0; i < 20; i++) {
final int Num = i;
Runnable runnable = new Runnable() {
@Override
public void run() {
try {
//线程开始时,获取许可
semaphore.acquire();
System.out.println(“Thread ” + Num + ” accessing”);
Thread.sleep((long) (Math.random() * 10000));
//访问完后释放
semaphore.release();
System.out.println(“available permit = ” + semaphore.availablePermits());
} catch (InterruptedException e) {
e.printStackTrace();
}
}};
executorService.submit(runnable);
}
Semaphore本质为共享锁,用来限制多个线程对有限资源访问进行控制。
二、ReentrantLock 可重入锁 独占锁
比Synchronized要高效,因为在高度争用情况下,处理器把大部分时间都花在任务处理上,而不是线程调度上
特性:时间等候锁,可中断锁,多个条件变量,锁投票,无块结构锁
一般用法:
try {
reentrantLock.lock();
System.out.println(str + ” 获取锁 “);
doSomeThing(str);
Thread.sleep((long) (Math.random() * 10000));
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
System.out.println(str + ” 释放锁 “);
reentrantLock.unlock();
}
时间等候锁,在一定时间内获取不到锁,则返回false
boolean captured = false;
try {
//在2秒内尝试获取锁
captured = reentrantLock.tryLock(10, TimeUnit.SECONDS);
} catch (InterruptedException e) {
e.printStackTrace();
}
try {
System.out.println(“tryLock(10, TimeUnit.SECONDS) ” + captured);
} finally {
if (captured) {
reentrantLock.unlock();
}
}
可中断锁,获取锁的线程可被自己或其他线程中断
try {
reentrantLock.lockInterruptibly();
System.out.println(Thread.currentThread().getName() + ” 获取锁 “);
try {
Thread.sleep(5 * 1000);
reentrantLockTest.plusMethod();
} catch (InterruptedException e) {
e.printStackTrace();
System.out.println(Thread.currentThread().getName() + “被中断 “);
}
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
reentrantLock.unlock();
System.out.println(Thread.currentThread().getName() + ” 释放锁 “);
}
多条件变/
* 生产者-消费者
* 多条件锁
*
* Created by zczhang on 16/9/27.
*/
public class ProductQueue {
private T[] items;
private final Lock lock = new ReentrantLock();
private Condition notFull = lock.newCondition();
private Condition notEmpty = lock.newCondition();
private int head, tail, count;public ProductQueue() { this(10);}public ProductQueue(int maxSize) { items = (T[]) new Object[maxSize];}public void put(T t) throws InterruptedException { lock.lock(); try { while (count == getCapacity()) { System.out.println("---------队列满,生产者等待------"); notFull.await(); } items[tail] = t; if (++tail == getCapacity()) { tail = 0; } ++count; notEmpty.signalAll(); } finally { lock.unlock(); }}public T take() throws InterruptedException { lock.lock(); try { while (count == 0) { System.out.println("---------队列空,消费者" + Thread.currentThread().getName() + "等待------"); notEmpty.await(); } T item = items[head]; items[head] = null; if (++head == getCapacity()) { head = 0; } --count; notFull.signalAll(); return item; } finally { lock.unlock(); }}public int size() { lock.lock(); try { return count; } finally { lock.unlock(); }}public int getCapacity() { return items.length;}
}eentrantReadWriteLock读写锁
读锁为共享锁,写锁为独占锁。读读不互斥,读写互斥,写写互斥
写锁支持Condition,读锁不支持Condition。
支持锁降级,即写锁可以获取读锁,然后释放写锁,这样写锁就变成了读锁
不支持锁升级,即读锁不能直接获取写锁。需将读锁释放后再获取写锁。
支持锁中断
读写锁的最大数量只能是65535(包括重入数)
private ReentrantReadWriteLock reentrantReadWriteLock = new ReentrantReadWriteLock();private ReentrantReadWriteLock.ReadLock readLock = reentrantReadWriteLock.readLock();private ReentrantReadWriteLock.WriteLock writeLock = reentrantReadWriteLock.writeLock();private int resource = 0;public int read() { try { try { Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); } readLock.lock(); System.out.println(Thread.currentThread().getName() + "获取读锁,尝试读取"); return resource; } finally { readLock.unlock(); System.out.println(Thread.currentThread().getName() + "释放读锁"); }}public void write(int newValue) { try { try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } writeLock.lock(); System.out.println(Thread.currentThread().getName() + "获取写锁,尝试写入"); this.resource = newValue; System.out.println(Thread.currentThread().getName() + "写入值 " + newValue); } finally { writeLock.unlock(); System.out.println(Thread.currentThread().getName() + "释放写锁"); }}
四、CountDownLatch
/** * CountDownLatch 同步辅助类 * 指定数字count初始化,然后调用await方法的线程会一直阻塞,直到count变为0 * <p> * 一个同步辅助类,允许一个或多个线程等待,一直到其他线程完成任务 * 使用场景:1.主线程开启多个子线程去执行分解的任务,当所有子线程都完成后,主线程再继续执行。 * 2.主线程先运行,子线程再运行,主线程等待子线程完成再运行 * <p> * Created by zczhang on 16/9/28. */public class CountDownLatchTest { public static void main(String[] args) throws InterruptedException { int childTaskNum = 3; CountDownLatch startLatch = new CountDownLatch(1); CountDownLatch childEndLatch = new CountDownLatch(childTaskNum); for (int i = 0; i < childTaskNum; i++) { Thread child = new Thread(new ChildTaskRun(startLatch,childEndLatch)); child.start(); } System.out.println("主线程处理一些任务..."); Thread.sleep(2*1000); System.out.println("主线程处理一些任务完成"); startLatch.countDown(); System.out.println("主线程等待子线程处理任务完成..."); childEndLatch.await(); System.out.println("主线程处理任务结果"); } private static class ChildTaskRun implements Runnable { private CountDownLatch mainStartLatch; private CountDownLatch childEndLatch; public ChildTaskRun(CountDownLatch mainStartLatch, CountDownLatch childEndLatch) { this.mainStartLatch = mainStartLatch; this.childEndLatch = childEndLatch; } @Override public void run() { try { mainStartLatch.await(); doSomeThing(); System.out.println("子线程 " + Thread.currentThread().getName() + "处理任务完成"); childEndLatch.countDown(); } catch (InterruptedException e) { e.printStackTrace(); } } private void doSomeThing() { System.out.println("子线程 " + Thread.currentThread().getName() + "处理任务中"); try { Thread.sleep(2 * 1000); } catch (InterruptedException e) { e.printStackTrace(); } } }}
五、CyclicBarrier
/** * 回环栅栏 同步辅助类 * 可实现让一组线程等待至某个状态之后再全部同时执行,这个状态就叫做一个栅栏. * 当所有线程都被释放后,该栅栏可以重用 * <p/> * 使用场景:开启多个子线程处理同步任务,当所有子任务都处理完成后,各个子线程再处理其他任务. * 同时主线程可插入任务,当最后一个子线程完成同步任务后,执行主线程插入任务,然后子线程再处理其他任务 * <p/> * Created by zczhang on 16/9/28. */public class CyclicBarrierTest { public static void main(String[] args) { int childTaskNum = 3; CyclicBarrier cyclicBarrier = new CyclicBarrier(childTaskNum, new Runnable() { @Override public void run() { System.out.println("当最后一个线程 " + Thread.currentThread().getName() + "完成同步任务时,做一些事情..."); } }); for (int i = 0; i < childTaskNum; i++) { Thread thread = new Thread(new ChildTaskRun(cyclicBarrier)); thread.start(); } } public static class ChildTaskRun implements Runnable { private CyclicBarrier cyclicBarrier; public ChildTaskRun(CyclicBarrier cyclicBarrier) { this.cyclicBarrier = cyclicBarrier; } @Override public void run() { System.out.println("子线程" + Thread.currentThread().getName() + "开始处理任务..."); try { Thread.sleep(3 * 1000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("子线程" + Thread.currentThread().getName() + "处理任务完成,等待其他线程..."); try { cyclicBarrier.await(); } catch (InterruptedException e) { e.printStackTrace(); } catch (BrokenBarrierException e) { e.printStackTrace(); } System.out.println("所有子线程完成同步任务, 继续执行其他任务..."); } }}
- Java并发、同步总结
- Java 并发 同步方案总结
- Java并发总结(二):同步与原子性
- Java并发总结(二):同步与原子性
- Java线程总结(六):并发包------线程同步Lock
- Java并发与同步
- java并发代码同步
- java-并发-同步容器
- Java并发同步
- Java中关于同步,异步,多线程,多线程同步,并发,并行的一些总结
- JAVA并发-同步容器和并发容器
- java并发:同步容器&并发容器
- Java并发编程(6)-同步
- Java中同步与并发
- Java并发编程2-同步
- Java线程同步于并发
- Java并发编程 同步容器
- Java并发编程:同步容器
- LOG4J日志级别详解
- STM8不用手动复位进入自带Bootloader方法(串口下载)
- 第一篇 破壳 实现人物简单的控制的代码和注释 笔记向
- Node.js REPL(交互式解释器)
- java中集合的运用,实现一个简单的购物程序
- Java并发、同步总结
- untiy 3d ShaderLab_第9章_1_平面阴影(一)
- 谈谈期刊论文
- js字符串常用判断方法
- Linux环境配置Lua出现的小问题
- angularjs常见页面查询功能
- 【通知栏】notification
- Node.js 回调函数
- Python3 Scrapy 安装方法 (一脸辛酸泪)