创建线程方式及应用总结

本文为博主原创,未经允许不得转载:

  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创建并启动新线程。

当我们了解java8的时候,你会发现上面创建线程的方式其实是很复杂的。java8提供了函数式接口编程,函数式接口编程极简化了线程的创建方式,增强了代码的可读性。什么是函数式接口编程呢?jdk8引入的lambda表达式和Stream为java平台提供了函数式编程的支持,极大的提高了开发效率。

函数式编程针对为函数式接口而言,函数式接口是有且只有一个抽象方法的接口。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);
    }; 

通过了解函数式编程接口之后我们发现通过函数式接口可以极大简化代码和开发效率。当我们在创建线程的时候也可以使用函数式编程接口的方法来创建线程。

这里粘贴以下项目中使用FutureTask在线程池中的使用,可以在多个线程同时执行计算时的应用:

  @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,可以控制各个厂商数据获得的结果等待,由于网宿白山的数据量最大,响应时间也最长,放在最后获取可以保证各个厂商的数据都可以获取到。。

 

posted @ 2020-03-21 20:29  香吧香  阅读(267)  评论(0编辑  收藏  举报