java高并发编程(四)高并发的一些容器
摘抄自马士兵java并发视频课程;
一、需求背景:
有N张火车票,每张票都有一个编号,同时有10个窗口对外售票, 请写一个模拟程序。
分析下面的程序可能会产生哪些问题?重复销售?超量销售?
/**
* 有N张火车票,每张票都有一个编号
* 同时有10个窗口对外售票
* 请写一个模拟程序
*
* 分析下面的程序可能会产生哪些问题?
* 重复销售?超量销售?
*
* @author 马士兵
*/
package yxxy.c_024; import java.util.ArrayList;
import java.util.List; public class TicketSeller1 {
static List<String> tickets = new ArrayList<>(); static {
for(int i=0; i<10000; i++) tickets.add("票编号:" + i);
} public static void main(String[] args) {
for(int i=0; i<10; i++) {
new Thread(()->{
while(tickets.size() > 0) {
System.out.println("销售了--" + tickets.remove(0));
}
}).start();
}
}
}
可能卖重;一张票可能对多个线程同时remove(0),所以可能一张票被卖出去多次;也可能最后一张票的时候都被多个线程remove(),程序会报错,总之,不加锁是不行的。
ArrayList不是同步的,remove、add等各种方法全都不是同步的;一定会出问题;
二、使用Vector
/**
* 使用Vector或者Collections.synchronizedXXX
* 分析一下,这样能解决问题吗?
*
* @author 马士兵
*/
package yxxy.c_024; import java.util.Vector;
import java.util.concurrent.TimeUnit; public class TicketSeller2 {
static Vector<String> tickets = new Vector<>(); static {
for(int i=0; i<1000; i++) tickets.add("票 编号:" + i);
} public static void main(String[] args) { for(int i=0; i<10; i++) {
new Thread(()->{
while(tickets.size() > 0) { try {
TimeUnit.MILLISECONDS.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
} System.out.println("销售了--" + tickets.remove(0));
}
}).start();
}
}
}
三、使用synchronized加锁:
/**
* 就算操作A和B都是同步的,但A和B组成的复合操作也未必是同步的,仍然需要自己进行同步
* 就像这个程序,判断size和进行remove必须是一整个的原子操作
*
* @author 马士兵
*/
package yxxy.c_024; import java.util.LinkedList;
import java.util.List;
import java.util.concurrent.TimeUnit; public class TicketSeller3 {
static List<String> tickets = new LinkedList<>(); static {
for(int i=0; i<1000; i++) tickets.add("票 编号:" + i);
} public static void main(String[] args) { for(int i=0; i<10; i++) {
new Thread(()->{
while(true) {
synchronized(tickets) {
if(tickets.size() <= 0) break; try {
TimeUnit.MILLISECONDS.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
} System.out.println("销售了--" + tickets.remove(0));
}
}
}).start();
}
}
}
/**
* 使用ConcurrentQueue提高并发性
*
* @author 马士兵
*/
package yxxy.c_024; import java.util.Queue;
import java.util.concurrent.ConcurrentLinkedQueue; public class TicketSeller4 {
static Queue<String> tickets = new ConcurrentLinkedQueue<>(); static {
for(int i=0; i<1000; i++) tickets.add("票 编号:" + i);
} public static void main(String[] args) { for(int i=0; i<10; i++) {
new Thread(()->{
while(true) {
String s = tickets.poll();
if(s == null) {
break;
}else {
System.out.println("销售了--" + s);
}
}
}).start();
}
}
}
/**
* http://blog.csdn.net/sunxianghuang/article/details/52221913
* http://www.educity.cn/java/498061.html
* 阅读concurrentskiplistmap
*/
package yxxy.c_025; import java.util.Arrays;
import java.util.Hashtable;
import java.util.Map;
import java.util.Random;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentSkipListMap;
import java.util.concurrent.CountDownLatch; public class T01_ConcurrentMap {
public static void main(String[] args) {
// Map<String, String> map = new ConcurrentHashMap<>();
Map<String, String> map = new ConcurrentSkipListMap<>(); //高并发并且排序 // Map<String, String> map = new Hashtable<>();
//Map<String, String> map = new HashMap<>(); //Collections.synchronizedXXX
//TreeMap
Random r = new Random();
Thread[] ths = new Thread[100];
CountDownLatch latch = new CountDownLatch(ths.length);
long start = System.currentTimeMillis();
for(int i=0; i<ths.length; i++) {
ths[i] = new Thread(()->{
for(int j=0; j<10000; j++) map.put("a" + r.nextInt(100000), "a" + r.nextInt(100000));
latch.countDown();
});
} Arrays.asList(ths).forEach(t->t.start());
try {
latch.await();
} catch (InterruptedException e) {
e.printStackTrace();
} long end = System.currentTimeMillis();
System.out.println(end - start);
}
}
/**
* 写时复制容器 copy on write
* 多线程环境下,写时效率低,读时效率高
* 适合写少读多的环境
* @author 马士兵
*/
package yxxy.c_025; import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Random;
import java.util.Vector;
import java.util.concurrent.CopyOnWriteArrayList; public class T02_CopyOnWriteList {
public static void main(String[] args) {
List<String> lists =
//new ArrayList<>(); //这个会出并发问题!
//new Vector();
new CopyOnWriteArrayList<>();
Random r = new Random();
Thread[] ths = new Thread[100]; for(int i=0; i<ths.length; i++) {
Runnable task = new Runnable() { @Override
public void run() {
for(int i=0; i<1000; i++) lists.add("a" + r.nextInt(10000));
} };
ths[i] = new Thread(task);
} runAndComputeTime(ths); System.out.println(lists.size());
} static void runAndComputeTime(Thread[] ths) {
long s1 = System.currentTimeMillis();
Arrays.asList(ths).forEach(t->t.start());
Arrays.asList(ths).forEach(t->{
try {
t.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
});
long s2 = System.currentTimeMillis();
System.out.println(s2 - s1);
}
}
package yxxy.c_025; import java.util.Queue;
import java.util.concurrent.ConcurrentLinkedQueue; public class T04_ConcurrentQueue {
public static void main(String[] args) {
Queue<String> strs = new ConcurrentLinkedQueue<>(); for(int i=0; i<10; i++) {
strs.offer("a" + i); //add
} System.out.println(strs); System.out.println(strs.size()); System.out.println(strs.poll());
System.out.println(strs.size()); System.out.println(strs.peek());
System.out.println(strs.size()); //双端队列Deque
}
}
console:
[a0, a1, a2, a3, a4, a5, a6, a7, a8, a9]
10
a0
9
a1
9
package yxxy.c_025; import java.util.Random;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.TimeUnit; public class T05_LinkedBlockingQueue { static BlockingQueue<String> strs = new LinkedBlockingQueue<>(); static Random r = new Random(); public static void main(String[] args) {
new Thread(() -> {
for (int i = 0; i < 100; i++) {
try {
strs.put("a" + i); //如果满了,就会等待
TimeUnit.MILLISECONDS.sleep(r.nextInt(1000));
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}, "p1").start(); for (int i = 0; i < 5; i++) {
new Thread(() -> {
for (;;) {
try {
System.out.println(Thread.currentThread().getName() + " take -" + strs.take()); //如果空了,就会等待
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}, "c" + i).start(); }
}
}
package yxxy.c_025; import java.util.Random;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.TimeUnit; public class T06_ArrayBlockingQueue { static BlockingQueue<String> strs = new ArrayBlockingQueue<>(10); //有界队列,最多装10个元素 static Random r = new Random(); public static void main(String[] args) throws InterruptedException {
for (int i = 0; i < 10; i++) {
strs.put("a" + i);
} strs.put("aaa"); //满了就会等待,程序阻塞,无限制的阻塞下去
//strs.add("aaa"); //报异常,Queue full
//strs.offer("aaa"); //不会报异常,但是加不进去;boolean带表是否加成功;这是add和offer的区别
//strs.offer("aaa", 1, TimeUnit.SECONDS); //1s钟之后加不进去就加不进了;按时间段阻塞 System.out.println(strs);
}
}
九、DelayQueue·:
package yxxy.c_025; import java.util.Random;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.DelayQueue;
import java.util.concurrent.Delayed;
import java.util.concurrent.TimeUnit; public class T07_DelayQueue { static BlockingQueue<MyTask> tasks = new DelayQueue<>(); static Random r = new Random(); static class MyTask implements Delayed {
long runningTime; MyTask(long rt) {
this.runningTime = rt;
} @Override
public int compareTo(Delayed o) {
if(this.getDelay(TimeUnit.MILLISECONDS) < o.getDelay(TimeUnit.MILLISECONDS))
return -1;
else if(this.getDelay(TimeUnit.MILLISECONDS) > o.getDelay(TimeUnit.MILLISECONDS))
return 1;
else
return 0;
} @Override
public long getDelay(TimeUnit unit) {
return unit.convert(runningTime - System.currentTimeMillis(), TimeUnit.MILLISECONDS);
} @Override
public String toString() {
return "" + runningTime;
}
} public static void main(String[] args) throws InterruptedException {
long now = System.currentTimeMillis();
MyTask t1 = new MyTask(now + 1000);
MyTask t2 = new MyTask(now + 2000);
MyTask t3 = new MyTask(now + 1500);
MyTask t4 = new MyTask(now + 2500);
MyTask t5 = new MyTask(now + 500); tasks.put(t1);
tasks.put(t2);
tasks.put(t3);
tasks.put(t4);
tasks.put(t5); System.out.println(tasks); for(int i=0; i<5; i++) {
System.out.println(tasks.take());
}
}
}
console:
[1534606492700, 1534606493200, 1534606493700, 1534606494700, 1534606494200]
1534606492700
1534606493200
1534606493700
1534606494200
1534606494700
package yxxy.c_025; import java.util.concurrent.LinkedTransferQueue; public class T08_TransferQueue {
public static void main(String[] args) throws InterruptedException {
LinkedTransferQueue<String> strs = new LinkedTransferQueue<>(); new Thread(() -> {
try {
System.out.println(strs.take());
} catch (InterruptedException e) {
e.printStackTrace();
}
}).start(); strs.transfer("aaa");
}
}
2.如果先起生产者transfer,然后再起消费者take,程序就会阻塞住了:
package yxxy.c_025; import java.util.concurrent.LinkedTransferQueue; public class T08_TransferQueue {
public static void main(String[] args) throws InterruptedException {
LinkedTransferQueue<String> strs = new LinkedTransferQueue<>(); strs.transfer("aaa"); new Thread(() -> {
try {
System.out.println(strs.take());
} catch (InterruptedException e) {
e.printStackTrace();
}
}).start();
}
}
3.如果transfer换成put(或者add、offer),也不会有问题,因为不会阻塞:
package yxxy.c_025; import java.util.concurrent.LinkedTransferQueue; public class T08_TransferQueue {
public static void main(String[] args) throws InterruptedException {
LinkedTransferQueue<String> strs = new LinkedTransferQueue<>(); //strs.transfer("aaa"); strs.put("aaa"); new Thread(() -> {
try {
System.out.println(strs.take());
} catch (InterruptedException e) {
e.printStackTrace();
}
}).start();
}
}
十一、SynchronousQueue
package yxxy.c_025; import java.util.concurrent.BlockingQueue;
import java.util.concurrent.SynchronousQueue; public class T09_SynchronusQueue { //容量为0
public static void main(String[] args) throws InterruptedException {
BlockingQueue<String> strs = new SynchronousQueue<>(); new Thread(()->{
try {
System.out.println(strs.take());
} catch (InterruptedException e) {
e.printStackTrace();
}
}).start(); strs.put("aaa"); //阻塞等待消费者消费
//strs.add("aaa");
System.out.println(strs.size());
}
}
总结:
1:对于map/set的选择使用
HashMap 不需要多线程的情况下使用
TreeMap 不需要多线程的情况下使用
LinkedHashMap 不需要多线程的情况下使用 Hashtable 并发量比较小
Collections.sychronizedXXX 并发量比较小 ConcurrentHashMap 高并发
ConcurrentSkipListMap 高并发同时要求排好顺序 2:队列
ArrayList 不需要同步的情况
LinkedList 不需要同步的情况
Collections.synchronizedXXX 并发量低
Vector 并发量低
CopyOnWriteList 写的时候少,读时候多
Queue
CocurrentLinkedQueue //concurrentArrayQueue 高并发队列
BlockingQueue 阻塞式
LinkedBQ 无界
ArrayBQ 有界
TransferQueue 直接给消费者线程,如果没有消费者阻塞
SynchronusQueue 特殊的transferQueue,容量0
DelayQueue执行定时任务
java高并发编程(四)高并发的一些容器的更多相关文章
- Java并发编程(四):并发容器(转)
解决并发情况下的容器线程安全问题的.给多线程环境准备一个线程安全的容器对象. 线程安全的容器对象: Vector, Hashtable.线程安全容器对象,都是使用 synchronized 方法实现的 ...
- java并发编程与高并发解决方案
下面是我对java并发编程与高并发解决方案的学习总结: 1.并发编程的基础 2.线程安全—可见性和有序性 3.线程安全—原子性 4.安全发布对象—单例模式 5.不可变对象 6.线程封闭 7.线程不安全 ...
- Java并发编程系列-(1) 并发编程基础
1.并发编程基础 1.1 基本概念 CPU核心与线程数关系 Java中通过多线程的手段来实现并发,对于单处理器机器上来讲,宏观上的多线程并行执行是通过CPU的调度来实现的,微观上CPU在某个时刻只会运 ...
- java并发编程--第一章并发编程的挑战
一.java并发编程的挑战 并发编程需要注意的问题: 并发编程的目的是让程序运行的更快,然而并不是启动更多的线程就能让程序最大限度的并发执行.若希望通过多线程并发让程序执行的更快,会受到如下问题的挑战 ...
- 并发编程概述--C#并发编程经典实例
优秀软件的一个关键特征就是具有并发性.过去的几十年,我们可以进行并发编程,但是难度很大.以前,并发性软件的编写.调试和维护都很难,这导致很多开发人员为图省事放弃了并发编程.新版.NET 中的程序库和语 ...
- 【Java并发编程四】关卡
一.什么是关卡? 关卡类似于闭锁,它们都能阻塞一组线程,直到某些事件发生. 关卡和闭锁关键的不同在于,所有线程必须同时到达关卡点,才能继续处理.闭锁等待的是事件,关卡等待的是其他线程. 二.Cycli ...
- Java 并发编程(四):如何保证对象的线程安全性
01.前言 先让我吐一句肺腑之言吧,不说出来会憋出内伤的.<Java 并发编程实战>这本书太特么枯燥了,尽管它被奉为并发编程当中的经典之作,但我还是忍不住.因为第四章"对象的组合 ...
- 【Java并发编程】:并发新特性—Executor框架与线程池
Executor框架简介 在Java5之后,并发编程引入了一堆新的启动.调度和管理线程的API.Executor框架便是Java 5中引入的,其内部使用了线程池机制,它在java.util.cocur ...
- Java并发编程之支持并发的list集合你知道吗
Java并发编程之-list集合的并发. 我们都知道Java集合类中的arrayList是线程不安全的.那么怎么证明是线程不安全的呢?怎么解决在并发环境下使用安全的list集合类呢? 本篇是<凯 ...
- Java并发编程实战笔记—— 并发编程1
1.如何创建并运行java线程 创建一个线程可以继承java的Thread类,或者实现Runnabe接口. public class thread { static class MyThread1 e ...
随机推荐
- 使用定时器通过改变图片的src来切换图片
点击以后开始自动切换图片 $("#page3_cover_1_back").click(function(){ var i=3; var haha=setInterval(func ...
- UE4 C++ Tips
篇写的是关于UE4的C++方面的小技巧: 1.在构造函数里 //构建组件 RootComponent = CreateDefaultSubobject<USceneComponent>(T ...
- C++ Tips
1. 虚函数不能是内联的 因为“内联”是指“在编译期间用被调用的函数体本身来代替函数调用的指令,”但是虚函数的“虚”是指“直到运行时才能知道要调用的是哪一个函数.”如果编译器在某个函数的调用点不知道具 ...
- L2-008 最长对称子串 (25 分)
对给定的字符串,本题要求你输出最长对称子串的长度.例如,给定Is PAT&TAP symmetric?,最长对称子串为s PAT&TAP s,于是你应该输出11. 输入格式: 输入在一 ...
- 初等数论及其应用 (第6版) (Kenneth H.Rosen 著)
第1章 整数 1.1 数和序列 1.2 和与积 1.3 数学归纳法 1.4 斐波那契数 1.5 整除性 第2章 整数的表示法和运算 2.1 整数的表示法 2.2 整数的计算机运算 2.3 整数运算的复 ...
- C# WinForm 菜单项的大小、高宽的手动控制
控制菜单项的第一级的下级菜单项的大小: 直接通过(ContextMenuStrip对象).AutoSize = false.(ContextMenuStrip对象).Size = new Size(5 ...
- Appscan
IBM AppScan该产品是一个领先的 Web 应用安全测试工具,曾以 Watchfire AppScan 的名称享誉业界.Rational AppScan 可自动化 Web 应用的安全漏洞评估工作 ...
- MySQL插入,更新,删除数据
插入 单行插入 1.insert into 表名 values(col1_value,col2_value,...); 每个列必须提供一个值,如果没有值,要提供NULL值 每个列必须与它在表中定义的次 ...
- C# 线程:定时器的使用
转载请注明出处:http://www.cnblogs.com/KeenLeung/p/3911556.html Timer类:设置一个定时器,定时执行用户指定的函数. 定时器启动后,系统将自动建立一个 ...
- CSS vertical-algin的使用
一.什么vertical-algin vertical的意思就是垂直,algin是对齐的意思,连起来就是“垂直对齐方式”,接下来看看vertical-algin有哪些属性,打开浏览器一看 vertic ...