Java 并发编程实践基础 读书笔记: 第三章 使用 JDK 并发包构建程序
一,JDK并发包实际上就是指java.util.concurrent包里面的那些类和接口等
主要分为以下几类: 1,原子量;2,并发集合;3,同步器;4,可重入锁;5,线程池
二,原子量
原子变量主要有AtomicInteger,AtomicLong,AtomicBoolean等,
主要实现原理都是底层实现类CAS 即比较并交换,都有get,set,compareAndSet等方法,如++,--等也都是有自带方法实现
这些都是线程安全的,保证了多线程访问时候的可见性
import java.util.concurrent.atomic.AtomicLong; /**
* StudySjms
* <p>
* Created by haozb on 2018/3/2.
*/
public class AtomicAccount { AtomicLong account; public AtomicAccount(long money) {
this.account = new AtomicLong(money);
} public void withDraw(long money,int sleepTime){
long oldValue = account.get();
if(oldValue >= money){
try {
Thread.sleep(sleepTime);
} catch (Exception e) { }
if(account.compareAndSet(oldValue,oldValue-money)){
System.out.println(Thread.currentThread().getName()+"扣钱成功");
}else{
System.out.println(Thread.currentThread().getName()+"扣钱失败");
}
}else{
System.out.println("钱不够了");
}
} public static void main(String[] args) {
final AtomicAccount aa = new AtomicAccount(); for (int i = ; i < ; i++) {
new Thread(new Runnable() {
@Override
public void run() {
aa.withDraw(,);
}
}).start();
}
}
}
上面这个方法就是利用原子量解决多线程中计数不安全的例子;
三,并发集合
这个包里面有一个阻塞队列的接口BlockingQueue,阻塞的概念就是满了就不会再填充了,空了也不允许再取,
所有实现这个接口的队列都是线程安全的。
主要有ArrayBlockingQueue:一个由数组支持的有界队列
LinkedBlockingQueue:一个由链接节点支持的可选有界队列
PriorityBlockingQueue:一个由优先级堆支持的无界优先级队列
DelayQueue :一个由优先级堆支持的、基于时间的调度队列
ConcurrentMap 接口,ConcurrentHashMap, 这个实现类的方法是原子的,源代码里面是采用lock住put那一块代码。
CopyOnWriteArrayList,CopyOnWriteArraySet采用copy-on-write 模式
package copyonwrite; import java.util.ArrayList;
import java.util.Arrays;
import java.util.ConcurrentModificationException;
import java.util.Iterator;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList; public class CopyOnWriteDemo {
@SuppressWarnings("unchecked")
public static void main(String args[]) {
String[] ss = {"aa", "bb", "cc"};
List list1 = new CopyOnWriteArrayList(Arrays.asList(ss));
List list2 = new ArrayList(Arrays.asList(ss));
Iterator itor1 = list1.iterator();
Iterator itor2 = list2.iterator();
list1.add("New");
list2.add("New");
try {
printAll(itor1);
} catch (ConcurrentModificationException e) {
System.err.println("Shouldn't get here");
}
try {
printAll(itor2);
} catch (ConcurrentModificationException e) {
System.err.println("Will gethere.ConcurrentModificationException occurs !");
}
} @SuppressWarnings("unchecked")
private static void printAll(Iterator itor) {
while (itor.hasNext()) {
System.out.println(itor.next());
}
}
}
运行结果如下:
Will get here.ConcurrentModificationException occurs!
aa
bb
cc
这个例子很好地说明了。
四,同步器
主要有CyclicBarrier:
1.它允许在涉及一组固定大小的线程的程序中,这些线程必须不时地互相等待
2.重要的属性就是参与者个数,另外最要方法是 await()。当所有线程都调用了 await()后,就表示这些线程都可以继续执行,否则就会等待
package synchronizer; import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors; public class CyclicBarrierDemo {
/* 徒步需要的时间: Shenzhen, Guangzhou, Chongqing */
private static int[] timeForWalk = { , , };
/* 自驾游 */
private static int[] timeForSelf = { , , };
/* 旅游大巴 */
private static int[] timeForBus = { , , }; static String nowTime() /* 时间格式化 */
{
SimpleDateFormat sdf = new SimpleDateFormat( "HH:mm:ss" );
return(sdf.format( new Date() ) + ": ");
} static class Tour implements Runnable {
private int[] timeForUse;
private CyclicBarrier barrier;
private String tourName; public Tour( CyclicBarrier barrier, String tourName, int[] timeForUse )
{
this.timeForUse = timeForUse;
this.tourName = tourName;
this.barrier = barrier;
} public void run()
{
try {
Thread.sleep( timeForUse[] * );
System.out.println( nowTime() + tourName + " ReachedShenenzh " );
barrier.await(); /* 到达中转站后等待其他旅行团 */
Thread.sleep( timeForUse[] * );
System.out.println( nowTime() + tourName + " ReachedGuangzhou" );
barrier.await(); /* 到达中转站后等待其他旅行团 */
Thread.sleep( timeForUse[] * );
System.out.println( nowTime() + tourName + " ReachedChonin" );
barrier.await(); /* 到达中转站后等待其他旅行团 */
} catch ( InterruptedException e ) {
} catch ( BrokenBarrierException e ) {
}
}
} public static void main( String[] args )
{
/* 三个旅行团都到到达某一个站点后,执行下面的操作,表示都到齐了。 */
Runnable runner = new Runnable()
{
@Override
public void run()
{
System.out.println( "we all are here." );
}
};
CyclicBarrier barrier = new CyclicBarrier( , runner );
/* 使用线程池 */
ExecutorService exec = Executors.newFixedThreadPool( );
exec.submit( new Tour( barrier, "WalkTour", timeForWalk ) );
exec.submit( new Tour( barrier, "SelfTour", timeForSelf ) );
exec.submit( new Tour( barrier, "BusTour", timeForBus ) );
exec.shutdown();
}
}
::: SelfTour Reached Shenzhen
: Bus : : Tour Reached Shenzhen
: WalkTour Reached Shenzhen : :
we all are here.
: SelfTour Reached Guangzhou : :
: BusTour Reached Guangzhou : :
: WalkTour Reached Guangzhou : :
we all are here.
: SelfTour Reached Ch : : ongqing
::: BusTour Reached Chongqing
::: WalkTour Reached Chongqing
we all are here.
五,Future(接口)和FutureTask(实现类)
注:FutureTask实现了Runnable,所以可以通过线程池和Thread执行
使用他们的好处是可以获得线程的结果,和抛出异常,这种好处通过Callable的定义就知道了
public interface Callable<V> {
/**
* Computes a result, or throws an exception if unable to do so.
*
* @return computed result
* @throws Exception if unable to compute a result
*/
V call() throws Exception;
}
可以通过以下几种方式调用
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.FutureTask; public class CallableTest { public static void main(String[] args) {
// //创建线程池
// ExecutorService es = Executors.newSingleThreadExecutor();
// //创建Callable对象任务
// CallableDemo calTask=new CallableDemo();
// //提交任务并获取执行结果
// Future<Integer> future =es.submit(calTask);
// //关闭线程池
// es.shutdown(); //创建线程池
ExecutorService es = Executors.newSingleThreadExecutor();
//创建Callable对象任务
CallableDemo calTask=new CallableDemo();
//创建FutureTask
FutureTask<Integer> futureTask=new FutureTask<Integer>(calTask);
//执行任务
es.submit(futureTask);
//关闭线程池
es.shutdown();
try {
Thread.sleep();
System.out.println("主线程在执行其他任务"); if(futureTask.get()!=null){
//输出获取到的结果
System.out.println("futureTask.get()-->"+futureTask.get());
}else{
//输出获取到的结果
System.out.println("futureTask.get()未获取到结果");
} } catch (Exception e) {
e.printStackTrace();
}
System.out.println("主线程在执行完成");
}
}
六,ReentrantLock显示锁 也叫可重入锁; 必须在finally里面释放锁
实现方式:
Lock lock=new ReentrantLock();
lock.lock();
try{
// 更新对象状态
}
finally{
lock.unlock();
}
new的时候,有个参数,true或者false 决定了是不是公平锁,正常上不公平锁的性能比较好!
线程之间的交互,有一个类叫Condition:Condition 的方法与 wait 、notify 和 notifyAll 方法类似,分别命名为 await 、 signal 和 signalAll
后面会详细的介绍
七,ReadWriteLock
ReadWriteLock 维护了一对相关的锁,一个用于只读操作,另一个用于写入操作。只要没有 writer,读取锁可以由多个 reader 线程同时保持。写入锁是独占的
这个锁比互斥锁的性能上应该好些(读的频率大于写的频率)
import java.util.Calendar;
import java.util.Map;
import java.util.TreeMap;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantReadWriteLock; /**
* StudySjms
* <p>
* Created by haozb on 2018/3/2.
*/
public class ReadWriteLockDemo {
private ReentrantReadWriteLock lock = null;
private Lock readLock = null;// 读锁
private Lock writeLock = null;// 写锁
public int key = ;
public int index = ;
public Map<Integer, String> dataMap = null;// 线程共享数据 public ReadWriteLockDemo() {
lock = new ReentrantReadWriteLock(true);
readLock = lock.readLock();
writeLock = lock.writeLock();
dataMap = new TreeMap<Integer, String>();
} public static void main(String[] args) {
ReadWriteLockDemo tester = new ReadWriteLockDemo();
// 第一次获取锁
tester.writeLock.lock();
System.out
.println(Thread.currentThread().getName() + " get writeLock.");
// 第二次获取锁,应为是可重入锁
tester.writeLock.lock();
System.out
.println(Thread.currentThread().getName() + " get writeLock.");
tester.readLock.lock();
System.out.println(Thread.currentThread().getName() + " get readLock");
tester.readLock.lock();
System.out.println(Thread.currentThread().getName() + " get readLock");
tester.readLock.unlock();
tester.readLock.unlock();
tester.writeLock.unlock();
tester.writeLock.unlock();
tester.test();
} public void test() {
// 读线程比写线程多
for (int i = ; i < ; i++) {
new Thread(new reader(this)).start();
}
for (int i = ; i < ; i++) {
new Thread(new writer(this)).start();
}
} public void read() {
// 获取锁
readLock.lock();
try {
if (dataMap.isEmpty()) {
Calendar now = Calendar.getInstance();
System.out.println(now.getTime().getTime() + " R "
+ Thread.currentThread().getName()
+ " get key, but map is empty.");
}
String value = dataMap.get(index);
Calendar now = Calendar.getInstance();
System.out.println(now.getTime().getTime() + " R "
+ Thread.currentThread().getName() + " key = " + index
+ " value = " + value + " map size = " + dataMap.size());
if (value != null) {
index++;
}
} finally {
// 释放锁
readLock.unlock();
}
try {
Thread.sleep();
} catch (Exception e) { }
} public void write() {
writeLock.lock();
try {
String value = "value" + key;
dataMap.put(new Integer(key), value);
Calendar now = Calendar.getInstance();
System.out.println(now.getTime().getTime() + " W "
+ Thread.currentThread().getName() + " key = " + key
+ " value = " + value + " map size = " + dataMap.size());
key++;
try {
Thread.sleep();
} catch (InterruptedException e) {
e.printStackTrace();
}
} finally {
writeLock.unlock();
}
}
} class reader implements Runnable {
private ReadWriteLockDemo tester = null; public reader(ReadWriteLockDemo tester) {
this.tester = tester;
} @Override
public void run() {
Calendar now = Calendar.getInstance();
System.out.println(now.getTime().getTime() + " R "
+ Thread.currentThread().getName() + " started");
for (int i = ; i < ; i++) {
tester.read();
}
}
} class writer implements Runnable {
private ReadWriteLockDemo tester = null; public writer(ReadWriteLockDemo tester) {
this.tester = tester;
} @Override
public void run() {
Calendar now = Calendar.getInstance();
System.out.println(now.getTime().getTime() + " W "
+ Thread.currentThread().getName() + " started");
for (int i = ; i < ; i++) {
tester.write();
}
}
}
Java 并发编程实践基础 读书笔记: 第三章 使用 JDK 并发包构建程序的更多相关文章
- Java 并发编程实践基础 读书笔记: 第一章 JAVA并发编程实践基础
1.创建线程的方式: /** * StudySjms * <p> * Created by haozb on 2018/2/28. */ public class ThreadDemo e ...
- Java 并发编程实践基础 读书笔记: 第二章 构建线程安全应用程序
1,什么是线程安全性? 简单概括就是一个类在多线程情况下能安全调用就是线程安全 2,Servlet 的线程安全性 默认是非线程安全的,写servlet代码的时候需要注意线程安全,注意同步 3,vo ...
- 《Java并发编程实战》读书笔记-第5章 基础构建模块
同步容器类 同步容器类实现线程安全的方式:将所有状态封装起来,对每个公有方法使用同步,使得每一次只有一个线程可以访问.同步容器类包含:Vector.Hashtable.Collections.sync ...
- 《Java并发编程实战》读书笔记-第4章 对象的组合
设计线程安全的类 三个基本要素: 找出构成对象状态的所有变量 找出约束状态变量的不变性条件 建立对象状态的并发访问管理策略 实例封闭 将数据封装在对象内部,可以将数据的访问限制在对象的方法上,从而更容 ...
- 《Java并发编程实战》读书笔记-第3章 对象的共享
可见性 在没有同步的情况下,编译器.处理器以及运行时都可能做指令重排.执行结果可能会出现错误 volatile变量 编译器与运行时不会进行指令重排,不会进行缓存,使用volatile变量要满足以下条件 ...
- 《Java并发编程实战》读书笔记-第1章 简介
并发简史 在早期的计算机中不包含操作系统,从头至尾都只执行一个程序,并且这个程序能访问计算机所有资源.操作系统的出现使得计算机每次能运行多个程序,并且不同的程序都在单独的进程中运行:操作系统为各个独立 ...
- 《Java并发编程实战》读书笔记-第2章 线程安全性
要编写线程安全的代码,其核心在于要对状态访问操作进行管理,特别是对共享的和可变的状态的访问. 修复多线程问题的方式: 不在线程之间共享该状态变量 将状态变量修改为不可变的变量 在访问状态变量时使用同步 ...
- Java并发编程的艺术读书笔记(2)-并发编程模型
title: Java并发编程的艺术读书笔记(2)-并发编程模型 date: 2017-05-05 23:37:20 tags: ['多线程','并发'] categories: 读书笔记 --- 1 ...
- Java并发编程的艺术读书笔记(1)-并发编程的挑战
title: Java并发编程的艺术读书笔记(1)-并发编程的挑战 date: 2017-05-03 23:28:45 tags: ['多线程','并发'] categories: 读书笔记 --- ...
随机推荐
- sqlserver 以年月日为条件查询记录
今天做一个东西的时候,要查某年,某月的记录,从网上找到了sqlserver中的datepart函数,该函数是用来提取年份,月份,日期的一个函数,带两个参数,第一个为(yy,mm,dd)其中一个,表示年 ...
- 芝麻HTTP: Python爬虫入门之Urllib库的高级用法
1.设置Headers 有些网站不会同意程序直接用上面的方式进行访问,如果识别有问题,那么站点根本不会响应,所以为了完全模拟浏览器的工作,我们需要设置一些Headers 的属性. 首先,打开我们的浏览 ...
- 最新数组方法(包括es6)
整理目前所用过的数组方法,学习了新增的es6方法. 1 arr.push() 从后面添加元素,返回值为添加完后的数组的长度 let arr = [1,2,3,4,5] console.log(arr. ...
- 机器学习相关的tutorial
1. MRF 马尔可夫随机场 http://homepages.inf.ed.ac.uk/rbf/CVonline/LOCAL_COPIES/AV0809/ORCHARD/ 从MRF,讲到Gibbs分 ...
- Android继承AppCompatActivity实现全屏设置
转载 2016年05月25日 13:20:25 标签: android / style / android studio / 继承 4839 Android studio创建新项目后Activity默 ...
- ssm整合快速入门程序(一)
整合基础说明 spring 是一个开放源代码的设计层面框架,他解决的是业务逻辑层和其他各层的松耦合问题,因此它将面向接口的编程思想贯穿整个系统应用.Spring是于2003 年兴起的一个轻量级的Jav ...
- (luogu P1410)子序列 [TPLY]
子序列 题目链接:https://www.luogu.org/problemnew/show/P1410 吐槽: 这道题做得我心累 本来想好好练一练dp 刷刷水题来练练手感 于是乎打开了(普及+/提高 ...
- [Luogu2463][SDOI2008]Sandy的卡片
BZOJ权限题qwq Luogu sol "两个子串长度相同且一个串的全部元素加上一个数就会变成另一个串" 其实就是差分一波以后完全相同 所以对输入的数据进行差分,同时记一下每一个 ...
- 【BZOJ3223】文艺平衡树(Splay)
题面 题目描述 您需要写一种数据结构(可参考题目标题),来维护一个有序数列,其中需要提供以下操作:翻转一个区间,例如原有序序列是5 4 3 2 1,翻转区间是[2,4]的话,结果是5 2 3 4 1 ...
- BSGS算法(大步小步算法)
计算\(y^x ≡ z \ mod\ p\) 中 \(x\) 的解. 这个模板是最小化了\(x\) , 无解输出\(No \ Solution!\) map<ll,ll>data; ll ...