创建线程方式及应用总结

本文涉及的产品
.cn 域名,1个 12个月
简介: 创建线程方式及应用总结

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

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

 

标签: 高并发

目录
相关文章
|
23天前
|
存储 并行计算 安全
C++多线程应用
【10月更文挑战第29天】C++ 中的多线程应用广泛,常见场景包括并行计算、网络编程中的并发服务器和图形用户界面(GUI)应用。通过多线程可以显著提升计算速度和响应能力。示例代码展示了如何使用 `pthread` 库创建和管理线程。注意事项包括数据同步与互斥、线程间通信和线程安全的类设计,以确保程序的正确性和稳定性。
|
29天前
|
监控 Java
在实际应用中选择线程异常捕获方法的考量
【10月更文挑战第15天】选择最适合的线程异常捕获方法需要综合考虑多种因素。没有一种方法是绝对最优的,需要根据具体情况进行权衡和选择。在实际应用中,还需要不断地实践和总结经验,以提高异常处理的效果和程序的稳定性。
19 3
|
1月前
|
调度 Android开发 开发者
构建高效Android应用:探究Kotlin多线程优化策略
【10月更文挑战第11天】本文探讨了如何在Kotlin中实现高效的多线程方案,特别是在Android应用开发中。通过介绍Kotlin协程的基础知识、异步数据加载的实际案例,以及合理使用不同调度器的方法,帮助开发者提升应用性能和用户体验。
46 4
|
1月前
|
数据采集 存储 Java
Crawler4j在多线程网页抓取中的应用
Crawler4j在多线程网页抓取中的应用
|
1月前
|
数据挖掘 程序员 调度
探索Python的并发编程:线程与进程的实战应用
【10月更文挑战第4天】 本文深入探讨了Python中实现并发编程的两种主要方式——线程和进程,通过对比分析它们的特点、适用场景以及在实际编程中的应用,为读者提供清晰的指导。同时,文章还介绍了一些高级并发模型如协程,并给出了性能优化的建议。
31 3
|
1月前
|
Java 数据处理 数据库
Java多线程的理解和应用场景
Java多线程的理解和应用场景
49 1
|
20天前
|
Java 开发者
Java中的多线程基础与应用
【10月更文挑战第24天】在Java的世界中,多线程是提高效率和实现并发处理的关键。本文将深入浅出地介绍如何在Java中创建和管理多线程,以及如何通过同步机制确保数据的安全性。我们将一起探索线程生命周期的奥秘,并通过实例学习如何优化多线程的性能。无论你是初学者还是有一定经验的开发者,这篇文章都将为你打开一扇通往高效编程的大门。
17 0
|
2月前
|
负载均衡 Java 调度
探索Python的并发编程:线程与进程的比较与应用
本文旨在深入探讨Python中的并发编程,重点比较线程与进程的异同、适用场景及实现方法。通过分析GIL对线程并发的影响,以及进程间通信的成本,我们将揭示何时选择线程或进程更为合理。同时,文章将提供实用的代码示例,帮助读者更好地理解并运用这些概念,以提升多任务处理的效率和性能。
60 3
|
2月前
|
Java 开发者
Java中的多线程基础与应用
【9月更文挑战第22天】在Java的世界中,多线程是一块基石,它支撑着现代并发编程的大厦。本文将深入浅出地介绍Java中多线程的基本概念、创建方法以及常见的应用场景,帮助读者理解并掌握这一核心技术。
|
1月前
|
网络协议 安全 Java
难懂,误点!将多线程技术应用于Python的异步事件循环
难懂,误点!将多线程技术应用于Python的异步事件循环
61 0