创建线程方式及应用总结

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

 

标签: 高并发

目录
相关文章
|
2月前
|
缓存 Java 开发者
Java多线程并发编程:同步机制与实践应用
本文深入探讨Java多线程中的同步机制,分析了多线程并发带来的数据不一致等问题,详细介绍了`synchronized`关键字、`ReentrantLock`显式锁及`ReentrantReadWriteLock`读写锁的应用,结合代码示例展示了如何有效解决竞态条件,提升程序性能与稳定性。
157 6
|
1月前
|
监控 Java 数据库连接
Java线程管理:守护线程与用户线程的区分与应用
在Java多线程编程中,线程可以分为守护线程(Daemon Thread)和用户线程(User Thread)。这两种线程在行为和用途上有着明显的区别,了解它们的差异对于编写高效、稳定的并发程序至关重要。
36 2
|
2月前
|
数据采集 存储 数据处理
Python中的多线程编程及其在数据处理中的应用
本文深入探讨了Python中多线程编程的概念、原理和实现方法,并详细介绍了其在数据处理领域的应用。通过对比单线程与多线程的性能差异,展示了多线程编程在提升程序运行效率方面的显著优势。文章还提供了实际案例,帮助读者更好地理解和掌握多线程编程技术。
|
2月前
|
存储 监控 安全
深入理解ThreadLocal:线程局部变量的机制与应用
在Java的多线程编程中,`ThreadLocal`变量提供了一种线程安全的解决方案,允许每个线程拥有自己的变量副本,从而避免了线程间的数据竞争。本文将深入探讨`ThreadLocal`的工作原理、使用方法以及在实际开发中的应用场景。
73 2
|
2月前
|
安全 Java 开发者
Java 多线程并发控制:深入理解与实战应用
《Java多线程并发控制:深入理解与实战应用》一书详细解析了Java多线程编程的核心概念、并发控制技术及其实战技巧,适合Java开发者深入学习和实践参考。
67 6
|
2月前
|
存储 安全 Java
Java多线程编程中的并发容器:深入解析与实战应用####
在本文中,我们将探讨Java多线程编程中的一个核心话题——并发容器。不同于传统单一线程环境下的数据结构,并发容器专为多线程场景设计,确保数据访问的线程安全性和高效性。我们将从基础概念出发,逐步深入到`java.util.concurrent`包下的核心并发容器实现,如`ConcurrentHashMap`、`CopyOnWriteArrayList`以及`BlockingQueue`等,通过实例代码演示其使用方法,并分析它们背后的设计原理与适用场景。无论你是Java并发编程的初学者还是希望深化理解的开发者,本文都将为你提供有价值的见解与实践指导。 --- ####
|
3月前
|
存储 并行计算 安全
C++多线程应用
【10月更文挑战第29天】C++ 中的多线程应用广泛,常见场景包括并行计算、网络编程中的并发服务器和图形用户界面(GUI)应用。通过多线程可以显著提升计算速度和响应能力。示例代码展示了如何使用 `pthread` 库创建和管理线程。注意事项包括数据同步与互斥、线程间通信和线程安全的类设计,以确保程序的正确性和稳定性。
|
3月前
|
监控 Java
在实际应用中选择线程异常捕获方法的考量
【10月更文挑战第15天】选择最适合的线程异常捕获方法需要综合考虑多种因素。没有一种方法是绝对最优的,需要根据具体情况进行权衡和选择。在实际应用中,还需要不断地实践和总结经验,以提高异常处理的效果和程序的稳定性。
36 3
|
3月前
|
调度 Android开发 开发者
构建高效Android应用:探究Kotlin多线程优化策略
【10月更文挑战第11天】本文探讨了如何在Kotlin中实现高效的多线程方案,特别是在Android应用开发中。通过介绍Kotlin协程的基础知识、异步数据加载的实际案例,以及合理使用不同调度器的方法,帮助开发者提升应用性能和用户体验。
70 4
|
3月前
|
数据采集 存储 Java
Crawler4j在多线程网页抓取中的应用
Crawler4j在多线程网页抓取中的应用