Java 天生就是多线程,主线程main,GC线程、分发处理jvm信号线程等。
1. 线程创建的三种方式 :
1.1 通过继承Thread,重写run方法
1.2 实现runable接口
1.3 实现Callable接口
tips:
1.Thread 与Runable关系,Thread实现了Runable接口,并提供入参为Runable引用的构造器,以实现静态代理模式。
2. Callable接口 :配合ExecutorService、Future可实现程序的异步任务及超时控制,异步回调等功能。
下面列举超时控制的一种实现方式 :
以下列举了同步第三方数据超时控制的简要程序。
/**
* Author: Velen
* Created Date: 2017-12-06 13:45
*/
@Service("realTimeSynApi")
public class RealTimeSynApi {
private static final Logger LOG = LoggerFactory.getLogger(RealTimeSynApi.class);
@Autowired
private RedisService redisService;
@Autowired
private IntegrationFactory integrationFactory;
/**
* 用于业务接口实时同步api信息,利用redis根据casNo计数,控制每个casNo每天只同步一次,且设置超时时限
* @param casNo
* @param custId
* @param timeOutLimit 单位毫秒
* @return
*/
public Boolean synApiDataInfo(String casNo,String custId,Long timeOutLimit) {
if (StringUtils.isBlank(casNo)) {
return false;
}
Long currentValue = getCounterNum(casNo);
if (null != currentValue && currentValue.intValue() >= 1) {
return false;
}
Boolean result = true;
if (null != timeOutLimit && timeOutLimit.intValue()>0) {
String resultCode = timerSynApi(casNo,custId,timeOutLimit);
if (JobResultCode.FAILURE.toString().equals(resultCode) || JobResultCode.TIME_OUT.toString().equals(resultCode)) {
result = false;
}
} else {
result = integrationFactory.synSingleCasInfo(casNo,custId);
}
if (!result) {
return false;
}
try {
//计数+1,失效时间 1 天
incrCounter(casNo,1L);
setExpireTime(casNo,1L);
} catch (Exception e) {
LOG.error("RealTimeSynApi synApiDataInfo redis error!",e);
return false;
}
return true;
}
/**
* 超时控制同步API信息
* @param casNo
* @param custId
* @return
*/
private String timerSynApi(String casNo,String custId,Long timeOutLimit) {
final SynApiDataJob job = new SynApiDataJob(casNo,custId);
final ExecutorService exec = Executors.newFixedThreadPool(1);
String resultCode = "";
try {
Future<String> future = exec.submit(job);
String obj = future.get(timeOutLimit, TimeUnit.MILLISECONDS); //任务处理超时时间设为 1 秒
resultCode = obj;
LOG.info("任务处理结果",obj);
} catch (TimeoutException ex) {
resultCode = JobResultCode.TIME_OUT.toString();
/**目前业务需求无法满足1s返回结果,超时异常暂不打印,该场景需要时可打开**/
/*LOG.error("业务调用API任务处理超时",ex);*/
/*ex.printStackTrace();*/
} catch (Exception e) {
resultCode = JobResultCode.FAILURE.toString();
LOG.error("任务处理异常!",e);
e.printStackTrace();
}
exec.shutdown();
return resultCode;
}
/**
* 同步API数据job类
*/
class SynApiDataJob implements Callable<String>{
private String casNo = null;
private String custId = null;
public SynApiDataJob(String casNo, String custId) {
this.casNo = casNo;
this.custId = custId;
}
@Override
public String call() throws Exception {
if (integrationFactory.synSingleCasInfo(this.casNo,this.custId)) {
return JobResultCode.SUCCESS.toString();
} else {
return JobResultCode.FAILURE.toString();
}
}
}
/**
* 处理结果枚举类
*/
enum JobResultCode {
SUCCESS("处理完成"),
FAILURE("处理失败"),
TIME_OUT("处理超时");
private String desc;
JobResultCode(String desc) {
this.desc = desc;
}
public String getDesc() {
return desc;
}
}
/**
* 获取计数器当前值
* @param key
* @return
*/
public Long getCounterNum(String key) {
return redisService.getRedisClient().getNoIncr(key);
}
/**
* 计数增加
* @param key
* @param value
* @return
*/
public Long incrCounter(String key, Long value) {
return redisService.getRedisClient().incr(key,value);
}
/**
* 设置失效时间
* @param key
* @param value
*/
public void setExpireTime(String key,long value) {
redisService.getRedisClient().expire(key, value, TimeUnit.DAYS);
}
/**
* 删除当前计数器
* @param key
*/
public void resetCounter(String key) {
redisService.deleteCache(key);
}
}
2. 线程状态转换 :
3. 线程的几种常见api :
3.1 join() :
join 可以看做线程之间协作的一种方式,有些业务场景需要两个线程按照一定规则处理,B需要在某个节点依赖A线程后再携手并进。此时就需要使用到join(),thradA.join(),此时B线程会等待A线程执行终止后再继续执行.
3.2 sleep():
按照一定时间休眠。需要注意sleep()方法如果当前线程已经获得了锁,sleep不会释放锁.
sleep() VS wait()
两者主要的区别:
- sleep()方法是Thread的静态方法,而wait是Object实例方法
- wait()方法必须要在同步方法或者同步块中调用,也就是必须已经获得对象锁。而sleep()方法没有这个限制可以在任何地方种使用。另外,wait()方法会释放占有的对象锁,使得该线程进入等待池中,等待下一次获取资源。而sleep()方法只是会让出CPU并不会释放掉对象锁;
- sleep()方法在休眠时间达到后如果再次获得CPU时间片就会继续执行,而wait()方法必须等待Object.notift/Object.notifyAll通知后,才会离开等待池,并且再次获得CPU时间片才会继续执行
3.3 yield() :
一旦执行此方法,代表当前线程将让出时间片,让出不代表当前线程不再执行,如果让出后该线程在竞争中再次获得时间片,则会继续执行。需要注意的是,该方法让出的时间片只能供相同优先级的线程竞争。