同步分为类级别和对象级别,对应类锁和对象锁。类锁一个类只有一个,static方法被synchronized时需要获得类锁。
同步块和同步方法?
同步方法会锁住整个对象,同步块不会。
sleep()/wait():sleep是Thread类的方法,是个静态方法,所以只对当前线程有效;wait是Object类的方法,调用wait前需要获得对象锁,调用wait后释放对象锁,当wait被notify唤醒后,需要再次获得对象锁以继续执行;若有多个线程wait,notify只会唤醒一个线程,由jvm决定哪个,notifyAll会唤醒所有wait的线程(wait/notify和锁有关,所以必须和symchronized一起使用,必须写在synchronized(obj) {}代码段内)。
Object类中的wait,notify,notifyAll方法用于线程间通信关于资源的锁的状态。
死锁:多个线程无限阻塞,互相等待所需资源。
ThreadLocal:线程级别的局部变量。必须初始化,否则为null。
private static ThreadLocal<Integer> numberContainer = new ThreadLocal<Integer>() {
@Override
protected Integer initialValue() {
return 0;
}
};
numberContainer.get()/set(Integer i)/remove();
原子操作?
i++不是原子操作,可能会线程不安全
java.util.concurrent.atomic提供了int和long类型的原子封装类,可以原子操作不需要同步。
并发容器?
java的集合类是快速失败的,集合被并发访问并发生冲突时会抛出ConcurrentModificationException
并发容器支持并发的遍历和更新
CocurrentHashMap
ConcurrentHashMap如何实现线程安全?
它把hash表分成很短Segment,每个segment有自己的锁。只要多个操作发生在不同的段上,就可以并发进行。支持完全并发的读,和部分并发的写
put()和get()都是通过key的hash值获取位置,获得链表entry后通过key获得value *因此重写key的equals方法也得重写hashcode方法
size()方法:不加锁size两次,若结果相同返回,否则对每个segment都加锁再size
CopyOnWriteArrayList
CopyOnWriteArraySet
java.util.concurrent
Executor 线程任务的执行者;接口,只有一个execute(Runnable command)方法
ExecutorService 线程池管理者;继承Executor,submit方法对execute方法进行了扩展
ScheduledExecutorService 继承ExecutorService,可延迟执行或定期执行任务
Executors
newCachedThreadPool 可设置线程回收时间,默认60秒
newFixedThreadPool
Callable 类似Runnable,但可以返回对象或抛出异常,在线程池提交Callable任务后返回Future对象,调用Future.get()等待Callable结束并获取返回对象。
BlockingQueue
ArrayBlockingQueue
固定长度创建;只有插入取出用同一个锁(ReenTrantLock实现);数组实现,读快插慢
LinkedBlockingQueue
创建时可不设长度,默认MAX; 插入多和取出锁分开;链表实现,读慢插快
CountDownLatch
CountDownLatch threadSignal = new CountDownLatch(5);
threadSignal.countdown()/await();
给CountDownLatch的实例一个初始值,每执行完一个线程countdown一次,await等待CountDownLatch的实例减到0在执行后面的代码,可以保证多个线程同时结束。
java.util.concurrent.locks
ReenTrantLock 重入锁
激烈争用下性能更佳
Lock lock = new ReenTrantLock(true/false); // true: 按序排队 false:允许插队
lock.lock();
try {
...
} finally {
lock.unlock();
}
java.util.concurrent.atomic
线程安全的基本类型封装类
package com.travelzen.etermface.client.data;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.travelzen.etermface.common.utils.HttpClientUtil;
import com.travelzen.framework.config.tops.TopsConfEnum.ConfScope;
import com.travelzen.framework.config.tops.TopsConfReader;
import com.travelzen.framework.core.json.JsonUtil;
import com.travelzen.rosetta.eterm.common.pojo.EtermRtktResponse;
/**
* Eterm RTKT 出票后预览票面
* <p>
* 远程服务提供方:
* <p>
* op:http://192.168.160.183:8080
* <p>
* op3:http://192.168.161.87:8880
* <p>
* beta:http://eterm.if.beta.tdxinfo.com
* <p>
* product:http://eterm.if.tdxinfo.com
* <p>
* @author yiming.yan
* @Date Mar 2, 2016
*/
public class EtermRtktClient {
private static final Logger LOGGER = LoggerFactory.getLogger(EtermRtktClient.class);
private static final String ETERM_SERVER_ADDRESS = "tz-eterm-interface-client/eterm-server-address.properties";
private static String prefixUrl = null;
private static ExecutorService threadPool = null;
static {
prefixUrl = TopsConfReader.getConfContent(ETERM_SERVER_ADDRESS, "url", ConfScope.R);
// 允许最大线程数根据配置数量调整
threadPool = Executors.newFixedThreadPool(2);
}
/**
* Eterm RTKT 出票后预览票面
* <p>
* @param officeId
* @param isDomestic
* @param tktPacks
* @return
*/
public static List<EtermRtktResponse> rtkt(String office, boolean isDomestic, List<List<String>> tktPacks) {
LOGGER.info("Eterm RTKT 打包任务请求 office:{}, isDomestic:{}, tktPacks:{}", office, isDomestic, tktPacks);
if (null == prefixUrl) {
LOGGER.error("获取 TZ-Eterm server address 失败!");
return null;
}
Map<String, Future<EtermRtktResponse>> futureMap = new HashMap<String, Future<EtermRtktResponse>>();
for (List<String> tktPack:tktPacks) {
if (tktPack.size() == 0)
continue;
for (String tktNo:tktPack) {
Future<EtermRtktResponse> future = threadPool.submit(new RtktTask(office, isDomestic, tktNo));
futureMap.put(tktNo, future);
}
}
List<EtermRtktResponse> responses = new ArrayList<EtermRtktResponse>();
PACK_LOOP:
for (List<String> tktPack:tktPacks) {
if (tktPack.size() == 0) {
LOGGER.error("Eterm RTKT 票号组为空!");
responses.add(new EtermRtktResponse(false, "Eterm RTKT 票号组为空!"));
continue PACK_LOOP;
}
List<EtermRtktResponse> tmpResponses = new ArrayList<EtermRtktResponse>();
for (String tktNo:tktPack) {
try {
EtermRtktResponse response = futureMap.get(tktNo).get();
tmpResponses.add(response);
} catch (Exception e) {
LOGGER.error("Eterm RTKT 任务执行异常:" + e.getMessage(), e);
responses.add(new EtermRtktResponse(false, "Eterm RTKT 任务执行异常!"));
continue PACK_LOOP;
}
}
EtermRtktResponse packResponse = mergePackResponses(tmpResponses);
if (packResponse.isSuccess()) {
String mainTktNo = null;
for (int i=0; i<tmpResponses.size(); i++) {
if (0 != tmpResponses.get(i).getFare() && 0 != tmpResponses.get(i).getTax()) {
mainTktNo = tktPack.get(i);
break;
}
}
if (null != mainTktNo)
packResponse.setMainTktNo(mainTktNo);
}
if (packResponse.getFare() == 0 && packResponse.getTax() == 0) {
packResponse.setSuccess(false);
packResponse.setErrorMsg("Eterm RTKT 价格异常!");
}
if (tktPack.size() == 1)
LOGGER.info("Eterm RTKT 票号:{} 结果:{}", tktPack, packResponse);
else
LOGGER.info("Eterm RTKT 连续票号:{} 结果:{}", tktPack, packResponse);
responses.add(packResponse);
}
if (responses.size() != tktPacks.size()) {
LOGGER.error("Eterm RTKT 打包任务结果异常!");
return null;
}
LOGGER.info("Eterm RTKT 打包任务结果:{}", responses);
return responses;
}
private static EtermRtktResponse mergePackResponses(List<EtermRtktResponse> tmpResponses) {
if (tmpResponses.size() == 1)
return tmpResponses.get(0);
EtermRtktResponse response = tmpResponses.get(0);
if (!response.isSuccess())
return new EtermRtktResponse(false, "Eterm RTKT 连续票号的第1个票号解析异常:" + response.getErrorMsg());
if (tmpResponses.size() == 1)
return response;
for (int i = 1; i < tmpResponses.size(); i++) {
if (!tmpResponses.get(i).isSuccess())
return new EtermRtktResponse(false, "Eterm RTKT 连续票号的第" + (i+1) + "个票号解析异常:" + response.getErrorMsg());
response.getFlights().addAll(tmpResponses.get(i).getFlights());
}
return response;
}
public static class RtktTask implements Callable<EtermRtktResponse> {
private String office;
private boolean isDomestic;
private String tktNo;
public RtktTask(String office, boolean isDomestic, String tktNo) {
this.office = office;
this.isDomestic = isDomestic;
this.tktNo = tktNo;
}
@Override
public EtermRtktResponse call() throws Exception {
LOGGER.info("Eterm RTKT 请求 office:{}, isDomestic:{}, tktNo:{}", office, isDomestic, tktNo);
String url = prefixUrl + "/tz-eterm-interface-web/rtkt";
Map<String, String> params = new HashMap<String, String>();
params.put("office", office);
params.put("isDomestic", String.valueOf(isDomestic));
params.put("tktNo", tktNo);
String responseJson = HttpClientUtil.post(url, params, "UTF-8", "UTF-8", 5 * 1000, 120 * 1000);
if (null == responseJson) {
LOGGER.error("Eterm RTKT 返回结果为空!");
return new EtermRtktResponse(false, "TZ-Eterm 返回结果为空!");
}
LOGGER.info("Eterm RTKT 返回Json {}", responseJson);
EtermRtktResponse response = null;
try {
response = (EtermRtktResponse) JsonUtil.fromJson(responseJson, EtermRtktResponse.class);
} catch (IOException e) {
LOGGER.error("Json反序列化异常:" + e.getMessage(), e);
return new EtermRtktResponse(false, "TZ-Eterm 返回结果异常!");
}
LOGGER.info("Eterm RTKT 返回结果 {}", response);
return response;
}
}