马士兵高并发编程系列记录
https://www.bilibili.com/video/av11076511/?spm_id_from=333.788.videocard.4
问题描述
有N张火车票,每张票都有一个编号
同时有10个窗口对外售票
请写一个模拟程序
TicketSeller1
分析下面的程序可能会产生哪些问题?
重复销售
超量销售
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();
}
}
}
TicketSeller2
使用Vector或者Collections.synchronizedXXX
分析一下,这样能解决问题吗?(不能)
import java.util.Vector;
import java.util.concurrent.TimeUnit;
public class TicketSeller2 {
static Vector<String> tickets = new Vector<>();//Vector是同步容器
static {
for(int i=0; i<1000; i++)
tickets.add("票 编号:" + i);
}
//tickets.size()与tickets.remove()都是原子性操作,但是tickets.size()与tickets.remove()之间的操作不是原子性的
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();
}
}
}
TicketSeller3(可以解决问题)
就算操作A和B都是同步的,但A和B组成的复合操作也未必是同步的,仍然需要自己进行同步
就像这个程序,判断size和进行remove必须是一整个的原子操作
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();
}
}
}
TicketSeller4(可以解决问题,效率高)
使用ConcurrentQueue提高并发性
import java.util.Queue;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.TimeUnit;
public class TicketSeller4 {
static Queue<String> tickets = new ConcurrentLinkedQueue<>();
//ConcurrentLinkedQueue是并发容器 这个程序效率比3高
// (拿)。。。。。。。。。。。。(加)
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();
//拿到头位置的那个票,为空说明没拿到,poll()是有原子性的
// 判断后没有修改队列操作,所以不需要加锁
if(s == null)
break;
else
System.out.println("销售了--" + s);
}
}).start();
}
}
}
总结
同步容器类
1:Vector Hashtable :早期使用synchronized实现
2:ArrayList HashSet :未考虑多线程安全(未实现同步)
3:HashSet vs Hashtable StringBuilder vs StringBuffer
4:Collections.synchronized***工厂方法使用的也是synchronized
使用早期的同步容器以及Collections.synchronized***方法的不足之处,请阅读:
http://blog.csdn.net/itm_hadf/article/details/7506529
使用新的并发容器
http://xuganggogo.iteye.com/blog/321630