创建线程方式及应用总结
本文为博主原创,未经允许不得转载:
java中创建线程主要有三种方式:
第一种:继承Thread类,创建线程类:
主要有以下三步:
1.定义Thread类的子类,并重写该类的run方法,该run方法的方法体就代表了线程要完成的任务。因此把run()方法称为执行体。
2.创建Thread子类的实例,即创建了线程对象。
3.调用线程对象的start()方法来启动该线程。
public class ThreadTest extends Thread{ /** * 需要手动重写run方法,run方法内为线程执行体 */ public void run() { for (int i = 0; i < 100; i++) { System.out.println(getName()+" "+i); } } public static void main(String[] args) { System.out.println(Thread.currentThread().getName()); for (int i = 0; i < 100; i++) { if(i==20){ new ThreadTest().start(); new ThreadTest().start(); } } } }
上述代码中Thread.currentThread()方法返回当前正在执行的线程对象。getName()方法返回调用该方法的线程的名字。
在实际应用过程中,我们经常会使用匿名内部类的方式在方法或代码块中创建线程,其示例如下:
public static void main(String[] args) { new Thread() { public void run() { while (true) { try { System.out.println("线程输出"); //休眠两秒 Thread.sleep(2 * 1000); } catch (InterruptedException e) { e.printStackTrace(); } } }; }.start(); } }
二.通过Runnable接口创建线程类
(1).定义runnable接口的实现类,并重写该接口的run()方法,该run()方法的方法体同样是该线程的线程执行体。
(2).创建Runnable实现类的实例,并依此实例作为Thread的target来创建Thread对象,该Thread对象才是真正的线程对象。
(3).调用线程对象的start()方法来启动该线程。
public class RunnableThreadTest implements Runnable{ @Override public void run() { for (int i = 0; i < 100; i++) { System.out.println(Thread.currentThread().getName()); } } public static void main(String[] args) { System.out.println(Thread.currentThread().getName()); for (int i = 0; i < 100; i++) { if(i==20){ RunnableThreadTest test = new RunnableThreadTest(); Thread thread1 = new Thread(test); Thread thread2 = new Thread(test); thread1.start(); thread2.start(); } } } }
三.通过Callable和Future创建线程
(1).创建Callable接口的实现类,并实现call()方法,该call()方法将作为线程执行体,并且有返回值。
(2).创建Callable实现类的实例,使用FutureTask类来包装Callable对象,该FutureTask对象封装了该Callable对象的call()方法的返回值。
(3).使用FutureTask对象作为Thread对象的target创建并启动新线程。
(4).调用FutureTask对象的get()方法来获得子线程执行结束后的返回值。
import java.util.concurrent.Callable; import java.util.concurrent.ExecutionException; import java.util.concurrent.FutureTask; public class CallableThreadTest implements Callable<Integer>{ @Override public Integer call() throws Exception { int i=0; for (; i < 10; i++) { System.out.println(Thread.currentThread().getName()); } return i; } public static void main(String[] args) { CallableThreadTest thredClass = new CallableThreadTest(); FutureTask<Integer> future = new FutureTask<>(thredClass); for (int i = 0; i < 10; i++) { System.out.println(Thread.currentThread().getName()); if(i==2){ new Thread(future).start(); } } try { future.get(); } catch (InterruptedException e) { e.printStackTrace(); } catch (ExecutionException e) { e.printStackTrace(); } } }
上述示例是通过实现Callable接口的普通类来创建线程,在实际的应用过程中,我们会经常通过匿名内部类在方法或代码块中创建线程,示例如下:
import java.util.concurrent.FutureTask; import java.util.concurrent.Callable; public class FutureThread { public static void main(String[] args) { FutureTask<Integer> future = new FutureTask<Integer>(new Callable<Integer>() { @Override public Integer call() throws Exception { System.out.println(Thread.currentThread().getName()); return 2+3; } }); Thread futurerThread = new Thread(future); futurerThread.start(); } }
当我们通过FutureTask类来创建实例对象时,我们会发现FutureTask的泛型参数是一个必填参数,我们可以打开FutureTask的底层会发现,FutureTask类有两个构造函数,其底层构造代码如下:
* Creates a {@code FutureTask} that will, upon running, execute the * given {@code Callable}. * * @param callable the callable task * @throws NullPointerException if the callable is null */ public FutureTask(Callable<V> callable) { if (callable == null) throw new NullPointerException(); this.callable = callable; this.state = NEW; // ensure visibility of callable } /** * Creates a {@code FutureTask} that will, upon running, execute the * given {@code Runnable}, and arrange that {@code get} will return the * given result on successful completion. * * @param runnable the runnable task * @param result the result to return on successful completion. If * you don't need a particular result, consider using * constructions of the form: * {@code Future<?> f = new FutureTask<Void>(runnable, null)} * @throws NullPointerException if the runnable is null */ public FutureTask(Runnable runnable, V result) { this.callable = Executors.callable(runnable, result); this.state = NEW; // ensure visibility of callable }
第一个构造是通过传入一个Callable的对象创建线程,Callable对象会自动执行call()方法,第二个构造是通过传入一个实现Runnable的对象创建线程,后面有一个result参数,其用来返回线程执行的成功失败的状态。所以我们可以通过以上两种构造方式来创建FutureTask对象,然后将其作为Thread对象的target创建并启动新线程。
函数式编程针对为函数式接口而言,函数式接口是有且只有一个抽象方法的接口。Lambda表达式允许你直接以内联的形式为函数式接口的抽象方法提供实现,并把整个表达式作为函数式接口的实例。当我们把一个Lambda表达式赋给一个函数式接口时,这个表达式对应的必定是接口中唯一的抽象方法。因此就不需要以匿名类那么繁琐的形式去实现这个接口。可以说在语法简化上,Lambda表达式完成了方法层面的简化,函数式接口完成了类层面的简化。
函数式编程接口都只有一个抽象方法,编译器会将函数编译后当做该抽象方法的实现。如果接口有多个抽象方法,编译器就不知道这段函数应该是实现哪个方法了。例如:
以Callable接口作为示例,它是一个函数式接口,包含一个抽象方法call()。现在要定义一个Callable对象,传统方式是这样的:
Callable c = new Callable() { @Override public void accept(Object o) { System.out.println(o); } };
而使用函数式编程,可以这样定义:
Consumer c = (o) -> {
System.out.println(o);
};
@Override @Transactional(noRollbackFor = Exception.class) public void getDomainTotalData(int minuteInterval, int count) throws FucdnException { // 查询截止时间 final Date endDate = new Date(); // 查询开始时间 final Date beginDate = Utils.getWholeMinuteTime(DateUtils.addMinutes(endDate, -minuteInterval), FucdnNumConstant.NUM_5.getNum().intValue()); // 多线程查询厂商数据 ExecutorService executor = Executors.newCachedThreadPool(); // 苏宁 FutureTask<Map<Long, Map<String, CustomerDomainBandwidth>>> snTask = new FutureTask<>(() -> suStatisticService.getDomainSummaryManufacturerData(beginDate, endDate, count)); executor.submit(snTask); // 百度 FutureTask<Map<Long, Map<String, CustomerDomainBandwidth>>> bdTask = new FutureTask<>(() -> baiduStatisticsService.getDomainSummaryManufacturerData(beginDate, endDate, count)); executor.submit(bdTask); // 神狐 FutureTask<Map<Long, Map<String, CustomerDomainBandwidth>>> shTask = new FutureTask<>(() -> shStatisticsService.getDomainSummaryManufacturerData(beginDate, endDate, count)); executor.submit(shTask); // 腾讯 FutureTask<Map<Long, Map<String, CustomerDomainBandwidth>>> txTask = new FutureTask<>(() -> txStatisticsService.getDomainSummaryManufacturerData(beginDate, endDate, count)); executor.submit(txTask); // 云帆 FutureTask<Map<Long, Map<String, CustomerDomainBandwidth>>> yfTask = new FutureTask<>(() -> yunFanStatisticsService.getDomainSummaryManufacturerData(beginDate, endDate, count)); executor.submit(yfTask); // 竞信 FutureTask<Map<Long, Map<String, CustomerDomainBandwidth>>> jxTask = new FutureTask<>(() -> jxStatisticsService.getDomainSummaryManufacturerData(beginDate, endDate, count)); executor.submit(jxTask); // 阿里 FutureTask<Map<Long, Map<String, CustomerDomainBandwidth>>> aliTask = new FutureTask<>(() -> aliStatisticsService.getDomainSummaryManufacturerData(beginDate, endDate, count)); executor.submit(aliTask); // 网宿白山 FutureTask<Map<Long, Map<String, CustomerDomainBandwidth>>> wbTask = new FutureTask<>(() -> commonFlowTotalService.addDomainFlowList(minuteInterval, beginDate, endDate)); executor.submit(wbTask); // 苏宁域名带宽数据 Map<Long, Map<String, CustomerDomainBandwidth>> snMap = getDataMap(snTask); // 百度域名带宽数据 Map<Long, Map<String, CustomerDomainBandwidth>> bdMap = getDataMap(bdTask); // 神狐域名带宽数据 Map<Long, Map<String, CustomerDomainBandwidth>> shMap = getDataMap(shTask); // 腾讯域名带宽数据 Map<Long, Map<String, CustomerDomainBandwidth>> txMap = getDataMap(txTask); // 云帆域名带宽数据 Map<Long, Map<String, CustomerDomainBandwidth>> yfMap = getDataMap(yfTask); // 竞信域名带宽数据 Map<Long, Map<String, CustomerDomainBandwidth>> jxMap = getDataMap(jxTask); // 阿里域名带宽数据 Map<Long, Map<String, CustomerDomainBandwidth>> aliMap = getDataMap(aliTask); // 网宿白山域名汇总带宽 Map<Long, Map<String, CustomerDomainBandwidth>> wbMap = getDataMap(wbTask); // 关闭线程池 executor.shutdown(); // key合集 Set<Long> allKey = new HashSet<>(); allKey.addAll(snMap.keySet()); allKey.addAll(bdMap.keySet()); allKey.addAll(shMap.keySet()); allKey.addAll(txMap.keySet()); allKey.addAll(yfMap.keySet()); allKey.addAll(jxMap.keySet()); allKey.addAll(aliMap.keySet()); allKey.addAll(wbMap.keySet()); // 时间数组 String[] timeArray = Utils.getTimeArray(beginDate, endDate, FucdnNumConstant.NUM_5.getNum().intValue()); // 批量入库带宽数据 List<CustomerDomainBandwidth> dataLst = new ArrayList<>(); // 多厂商相同域名汇总 for (Long domainId : allKey) { if (domainId == null) { continue; } // 苏宁带宽数据 Map<String, CustomerDomainBandwidth> snSubMap = snMap.getOrDefault(domainId, new HashMap<>()); // 百度带宽数据 Map<String, CustomerDomainBandwidth> bdSubMap = bdMap.getOrDefault(domainId, new HashMap<>()); // 神狐带宽数据 Map<String, CustomerDomainBandwidth> shSubMap = shMap.getOrDefault(domainId, new HashMap<>()); // 腾讯带宽数据 Map<String, CustomerDomainBandwidth> txSubMap = txMap.getOrDefault(domainId, new HashMap<>()); // 云帆带宽数据 Map<String, CustomerDomainBandwidth> yfSubMap = yfMap.getOrDefault(domainId, new HashMap<>()); // 竞信带宽数据 Map<String, CustomerDomainBandwidth> jxSubMap = jxMap.getOrDefault(domainId, new HashMap<>()); // 阿里带宽数据 Map<String, CustomerDomainBandwidth> aliSubMap = aliMap.getOrDefault(domainId, new HashMap<>()); // 网宿白山带宽数据 Map<String, CustomerDomainBandwidth> wbSubMap = wbMap.getOrDefault(domainId, new HashMap<>()); // 相同域名同一时间点数据汇总 for (String time : timeArray) { if (StringUtils.isBlank(time)) { continue; } // 苏宁带苦啊数据 CustomerDomainBandwidth snData = snSubMap.get(time); // 百度带宽数据 CustomerDomainBandwidth bdData = bdSubMap.get(time); // 神狐带宽数据 CustomerDomainBandwidth shData = shSubMap.get(time); // 腾讯带宽数据 CustomerDomainBandwidth txData = txSubMap.get(time); // 云帆带宽数据 CustomerDomainBandwidth yfData = yfSubMap.get(time); // 竞信带宽数据 CustomerDomainBandwidth jxData = jxSubMap.get(time); // 阿里带宽数据 CustomerDomainBandwidth aliData = aliSubMap.get(time); // 网宿白山数据 CustomerDomainBandwidth wbData = wbSubMap.get(time); // 多厂商总带宽 double bandTotal = FucdnNumConstant.NUM_0.getNum().doubleValue(); if (null != snData) { bandTotal += snData.getBandwidth(); } if (null != bdData) { bandTotal += bdData.getBandwidth(); } if (null != shData) { bandTotal += shData.getBandwidth(); } if (null != txData) { bandTotal += txData.getBandwidth(); } if (null != yfData) { bandTotal += yfData.getBandwidth(); } if (null != jxData) { bandTotal += jxData.getBandwidth(); } if (null != aliData) { bandTotal += aliData.getBandwidth(); } if (null != wbData) { bandTotal += wbData.getBandwidth(); } CustomerDomainBandwidth cuDoBandwidth = new CustomerDomainBandwidth(); cuDoBandwidth.setBandwidth(bandTotal); cuDoBandwidth.setDomainId(domainId); cuDoBandwidth.setStatDate(Utils.formatDate(Utils.parseStr2Date(time, FucdnStrConstant.YEAR_MONTH_DAY_HOUR_MINUTE.getConstant()), FucdnStrConstant.YEAR_MONTH_DAY.getConstant())); cuDoBandwidth.setStatTime(time); dataLst.add(cuDoBandwidth); } } cuDoBandwidthService.batchInsertCuDoBandwidth(dataLst); } private Map<Long, Map<String, CustomerDomainBandwidth>> getDataMap(FutureTask<Map<Long, Map<String, CustomerDomainBandwidth>>> task) { try { return task.get(); } catch (Exception e) { LOGGER.error(e.getMessage(), e); } return new HashMap<>(); }
这段代码的功能是统计同一时间段内域名各厂商带宽汇总的任务。采用了多线程的方式,通过使用FutureTask,可以控制各个厂商数据获得的结果等待,由于网宿白山的数据量最大,响应时间也最长,放在最后获取可以保证各个厂商的数据都可以获取到。。