多线程、并发

同步分为类级别和对象级别,对应类锁和对象锁。类锁一个类只有一个,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;
		}
	}


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值