`
dyy_gusi
  • 浏览: 209582 次
  • 性别: Icon_minigender_1
  • 来自: 武汉
社区版块
存档分类
最新评论

java线程和线程池的使用

    博客分类:
  • Java
阅读更多

java线程和线程池

一、创建多线程的方式

    java多线程很常见,如何使用多线程,如何创建线程,java中有两种方式,第一种是让自己的类实现Runnable接口,第二种是让自己的类继承Thread类。其实Thread类自己也是实现了Runnable接口。具体使用实例如下:

1、通过实现Runnable接口方式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public class MyThread1 implements Runnable//通过实现Runnable接口方式
{
    String sign = "thread#1@";
 
    @Override
    public void run()
    {
        for (int i = 0; i < 30; i++)
        {
            System.out.println(sign + "->" + i);
            try
            {
                Thread.sleep(100L);
            } catch (InterruptedException e)
            {
                e.printStackTrace();
            }
        }
    }
 
}

 

2、通过继承Thread类的方式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public class MyThread2 extends Thread//通过继承Thread类的方式
{
    String sign = "thread#2@";
 
    @Override
    public void run()
    {
        for (int i = 0; i < 30; i++)
        {
            System.out.println(sign + "->" + i);
            try
            {
                Thread.sleep(100L);
            } catch (InterruptedException e)
            {
                e.printStackTrace();
            }
        }
    }
 
}

 

    再启用上面创建的两种线程,调运代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public static void main(String[] args) throws InterruptedException
{
    long startTime = System.currentTimeMillis();
    // 通过主线程启动自己的线程
 
    // 通过实现runnable接口
    Runnable myThread1 = new MyThread1();
    Thread thread1 = new Thread(myThread1);
    thread1.start();
 
    // 通过继承thread类
    Thread thread2 = new Thread(new MyThread2());
    thread2.start();
 
    // 注意这里不是调运run()方法,而是调运线程类Thread的start方法,在Thread方法内部,会调运本地系统方法,最终会自动调运自己线程类的run方法
 
    // 让主线程睡眠
    Thread.sleep(1000L);
    System.out.println("主线程结束!用时:"
            + (System.currentTimeMillis() - startTime));
    // System.exit(0);
}

 

    输入结果(每次输入可能不同)不再详细列出。对于上面的两种方式,更推荐使用实现Runnable接口的方式实现多线程。一方面是可以防止java单继承的顾虑,另一方面Runnable是面向接口编程,扩展性比起继承Thread更好。所以尽量使用implement Runnable的方式。

 

二、java线程类型说明

    上面是将多线程跑起来了,但是有个问题,如果不让主线程睡眠,当主线程(比如main线程)结束以后,如果子线程还没结束,那么子线程是否还会执行呢?答案是会继续执行,为了说明这个问题,就又涉及到java中线程的类型。java中线程一共有两种类型:守护线程(daemon thread)和用户线程(user thread)又叫非守护线程。可以通过thread.setDaemon()方法设置线程是否为守护线程,默认是设置非守护线程(false)。java虚拟机停止运行的时间是虚拟机中运行的所有线程都是守护线程的时候,也就是说如果jvm中没有user thread的时候,jvm就停止运行。或者说jvm在最后一个非守护线程结束的时候,将停止所有的守护进程,然后退出jvm。

    当使用main方法开启线程时,主线程默认是非守护进程,而用户自己开的进程也是非守护进程。当主线程结束,但是子线程(默认是非守护线程)还没结束,所以虚拟机是不停止运行的,当子线程运行完以后,如果主线程也运行完毕,jvm发现没有非守护线程,就将jvm关闭,所以当main方法的主线程执行完毕以后,子线程是会继续执行的。当然我们也可以让在main主线程执行完毕以后,子线程不再执行,方法就是将所有的子线程设置为守护进程setDaemon(true)即可。但是需要注意的是这个设置需要在线程运行之前设置,不能在线程运行的过程中修改线程类型。

    更直白点说如果用户将线程设置为守护进程,那实际的意思就是告诉jvm你不用搭理我这个线程,jvm想停止的时候,不用考虑我这个线程是否执行结束。这种线程具体的使用比如在垃圾回收机制的线程,就是一个守护线程。

1
2
3
4
5
6
7
8
9
10
11
12
13
Runnable myThread1 = new MyThread1();
Thread thread1 = new Thread(myThread1);
thread1.setDaemon(true);
thread1.start();
 
Thread thread2 = new Thread(new MyThread2());
thread2.setDaemon(false);
thread2.start();
 
System.out.println("mainThread isDaemon:"
        + Thread.currentThread().isDaemon());
System.out.println("thread1 isDaemon:" + thread1.isDaemon());
System.out.println("thread2 isDaemon:" + thread2.isDaemon());

 

 

三、线程池的使用

    上面已经看过了可以在代码中直接新起线程,如果我们在主线程中新起一百个线程,让这一百个线程同时工作,逻辑上是没有任何问题的,但是这样做对系统资源的开销很大,这样会在短时间内处理很多的任务,当然包括新起线程等等。基于这样的考虑,我们是有必要引入线程池这个东西的。线程池就是一个池子,池子里有很多可用线程资源,如果需要就直接从这个池子里拿就是。当不用的时候,放入池子中,线程池会自动帮我们管理。所以使用线程池主要有以下两个好处:1、减少在创建和销毁线程上所花的时间以及系统资源的开销 2、如不使用线程池,有可能造成系统创建大量线程而导致消耗完系统内存 。

    如果我们想要使用线程池,就需要先定义这个线程池。定义线程池的时候,其中的几个主要参数说明如下:
-corePoolSize(int):线程池中保持的线程数量,包括空闲线程在内。也就是线程池释放的最小线程数量界限。

-maximumPoolSize(int):线程池中嫩容纳最大线程数量。

-keepAliveTime(long):空闲线程保持在线程池中的时间,当线程池中线程数量大于corePoolSize的时候。

-unit(TimeUnit枚举类):上面参数时间的单位,可以是分钟,秒,毫秒等等。

-workQueue(BlockingQueue<Runnable>):任务队列,当线程任务提交到线程池以后,首先放入队列中,然后线程池按照该任务队列依次执行相应的任务。可以使用的workQueue有很多,比如:LinkedBlockingQueue等等。

-threadFactory(ThreadFactory类):新线程产生工厂类。

-handler(RejectedExecutionHandler类):当提交线程拒绝执行、异常的时候,处理异常的类。该类取值如下:(注意都是内部类)

ThreadPoolExecutor.AbortPolicy:丢弃任务并抛出RejectedExecutionException异常。 
ThreadPoolExecutor.DiscardPolicy:也是丢弃任务,但是不抛出异常。 
ThreadPoolExecutor.DiscardOldestPolicy:丢弃队列最前面的任务,然后重新尝试执行任务,重复此过程。
ThreadPoolExecutor.CallerRunsPolicy:由调用线程处理该任务。

   除了自定义线程池以外, java提供了几种常用的线程池,可以快捷的供程序员使用,他们分别是:

1、newFixedThreadPool 创建固定大小数量线程池,数量通过传入的参数决定。

2、newSingleThreadExecutor 创建一个线程容量的线程池,所有的线程依次执行,相当于创建固定数量为1的线程池。

3、newCachedThreadPool 创建可缓存的线程池,没有最大线程限制(实际上是Integer.MAX_VALUE)。如果用空闲线程等待时间超过一分钟,就关闭该线程。

4、newScheduledThreadPool 创建计划(延迟)任务线程池,线程池中的线程可以让其在特定的延迟时间之后执行,也可以以固定的时间重复执行(周期性执行)。相当于以前的Timer类的使用。
5、newSingleThreadScheduledExecutor 创建单线程池延迟任务,创建一个线程容量的计划任务。

    其实通过静态方法创建的上面几种线程池,也都是通过传入默认的各个参数,然后返回一个有各自特点的线程池。具体参数可以通过查看jdk源码阅读。

    有了线程池,那么我们如何利用线程池中线程执行我们的任务,由于Java将线程池的封装,我们拿到的线程池的线程其实是一个包含线程任务的执行器,只需要调运执行器的执行方法,就会自动执行我们线程中的任务。对于非计划任务,我们需要拿到一个ThreadPoolExecutor,对于计划任务,我们需要拿到一个ScheduledThreadPoolExecutor(它是ThreadPoolExecutor的子类)。在了解这两个类之前,需要先了解两个接口,ExecutorService以及它的子接口ScheduleThreadExecutorService接口,上面两个接口分别实现了这两个接口,这个两接口定义了execute(Runnable r)方法,这个方法去执行线程的任务。也就是我们通过调运ThreadPoolExecutor或者ScheduledThreadPoolExecutor的execute(Runnable r)方法开启我们的线程,并且执行我们的线程任务,具体代码如下:

    定义一个单例的线程池:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
public class MyPool
{
    private static MyPool myPool = null;
    //单例线程池中有两种具体的线程池
    private ThreadPoolExecutor threadPool = null;
    private ScheduledThreadPoolExecutor scheduledPool = null;
 
    public ThreadPoolExecutor getThreadPool()
    {
        return threadPool;
    }
 
    public ScheduledThreadPoolExecutor getScheduledPool()
    {
        return scheduledPool;
    }
 
    //设置线程池的各个参数的大小
    private int corePoolSize = 10;// 池中所保存的线程数,包括空闲线程。
    private int maximumPoolSize = 20;// 池中允许的最大线程数。
    private long keepAliveTime = 3;// 当线程数大于核心时,此为终止前多余的空闲线程等待新任务的最长时间。
    private int scheduledPoolSize = 10;
 
    private static synchronized void create()
    {
        if (myPool == null)
            myPool = new MyPool();
    }
 
    public static MyPool getInstance()
    {
        if (myPool == null)
            create();
        return myPool;
    }
 
    private MyPool()
    {
        //实例化线程池,这里使用的LinkedBlockingQueue作为workQueue,使用DiscardOldestPolicy作为handler
        this.threadPool = new ThreadPoolExecutor(corePoolSize, maximumPoolSize,
                keepAliveTime, TimeUnit.SECONDS,
                new LinkedBlockingQueue<Runnable>(),
                new ThreadPoolExecutor.DiscardOldestPolicy());
        //实例化计划任务线程池
        this.scheduledPool = new ScheduledThreadPoolExecutor(scheduledPoolSize);
    }
}

 

    获取线程池中的线程,并且执行线程中任务:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
@Test
public void testThreadPool()
{
 
    ThreadPoolExecutor pool1 = (ThreadPoolExecutor) Executors
            .newCachedThreadPool();
    pool1.execute(new Runnable()
    {
 
        @Override
        public void run()
        {
            System.out.println("快捷线程池中的线程!");
        }
    });
 
    ThreadPoolExecutor pool2 = MyPool.getInstance().getThreadPool();
    pool2.execute(new Runnable()
    {
 
        @Override
        public void run()
        {
            System.out.println("普通线程池中的线程");
        }
    });
 
    ScheduledThreadPoolExecutor pool3 = MyPool.getInstance()
            .getScheduledPool();
    pool3.scheduleAtFixedRate(new Runnable()
    {
 
        @Override
        public void run()
        {
            System.out.println("计划任务线程池中的线程");
        }
    }, 0, 1000, TimeUnit.MILLISECONDS);
}

 

 

四、计划任务执行使用

    通过上面的例子,也看到了计划任务线程池的使用方式。对于计划任务,除了可以执行普不同线程池中线程的任务以外,还可以执行计划任务特殊的线程要求,比如:scheduleWithFixedDelay(command, initialDelay, delay, unit);在初始化延迟之后,以特定的延迟时间重复执行。scheduleAtFixedRate(command, initialDelay, period, unit);在初始化延迟时间之后,以固定频率重复执行。这两种的区别是下一次执行时间延迟计算的开始时间不同,第一种是从上一次任务开始执行的时候计算,第二种是从上一次任务执行结束的时候计算。这两种和java之前版本中Timer类很相似。但是Timer有很多缺陷,具体的缺陷不再详细说明。而这些缺陷都可以通过ScheduledExecutorService给完美解决。所以是时候该丢弃Timer了。

 

 

 

2
0
分享到:
评论

相关推荐

    JAVA线程、线程池资料----下载不扣分,回帖加1分,欢迎下载,童叟无欺

    .......................................JAVA线程、线程池资料----下载不扣分,回帖加1分,欢迎下载,童叟无欺JAVA线程、线程池资料----下载不扣分,回帖加1分,欢迎下载,童叟无欺JAVA线程、线程池资料----下载不...

    java线程、线程池、xml解析入门

    Java线程、线程池和XML解析是Java编程中至关重要的三个概念,它们在实际开发中扮演着不可或缺的角色。下面将分别对这三个主题进行深入的介绍。 首先,我们来看Java线程。线程是程序执行的最小单位,一个进程可以...

    java线程和线程池的使用.docx

    下面将详细介绍 Java 中线程的创建方式以及线程池的使用。 1. 创建线程的方式: Java 提供了两种主要的创建线程的方式: - **实现 Runnable 接口**:你的类实现 Runnable 接口,并重写 `run()` 方法。然后通过...

    C#Winform异步多线程和线程池集成的用法

    本文将深入探讨如何在Winform应用中使用异步多线程和线程池。 一、线程基础 线程是操作系统分配CPU时间的基本单元,每个进程至少包含一个线程。在C#中,可以使用`System.Threading.Thread`类来创建和管理线程。通过...

    java线程池使用后到底要关闭吗

    java线程池使用后到底要关闭吗 java线程池是一种高效的并发编程技术,可以帮助开发者更好地管理线程资源,提高系统的性能和可靠性。然而,在使用java线程池时,一个常见的问题是:使用完线程池后到底要不要关闭?...

    java_thread_cn.rar_Java 线程池_java thread books_java线程_线程池_线程池调度

    Java线程池是Java并发编程中的重要组成部分,它在多线程编程中扮演着至关重要的角色,有效地管理和调度了大量的并发任务。...因此,对于任何从事Java并发编程的开发者来说,掌握线程池的使用和优化技巧都是必不可少的。

    Java多线程+线程池.docx

    Java 多线程和线程池 Java 多线程是指在 Java 编程语言中,一个线程的动态执行过程。这个过程包括线程的创建、执行和销毁三个阶段。在 Java 中,创建线程有多种方式,包括通过实现 Runnable 接口、继承 Thread 类...

    JAVA使用线程池查询大批量数据

    除了`ThreadPoolExecutor`,Java还提供了`Executors`工具类,它提供了一些预设的线程池配置,如`newFixedThreadPool`(固定大小线程池)、`newSingleThreadExecutor`(单线程线程池)等,方便开发者快速创建线程池。...

    Java中的线程与线程池.pptx

    2. newSingleThreadExecutor:创建一个单线程线程池,所有任务都在一个线程中顺序执行,保证了任务的执行顺序。 3. newFixedThreadPool:创建一个固定大小的线程池,线程数量保持不变,任务排队等待执行。 4. ...

    Java中多线程的使用线程池.docx

    `Executors` 类是一个静态工厂类,用于创建不同类型的线程池,如固定大小的线程池、单线程线程池、缓存线程池等。 4. **创建线程池**:使用`Executors`类的静态方法创建线程池,例如 `newFixedThreadPool(int ...

    Java实现通用线程池

    Java 实现通用线程池是指使用 Java 语言编写一个通用的线程池,线程池通俗的描述就是预先创建若干空闲线程,等到需要用多线程去处理事务的时候去唤醒某些空闲线程执行处理任务,这样就省去了频繁创建线程的时间,...

    Java简单线程池 线程池中文文档

    简单的线程池程序+中文文档 包结构: com.tangkai.threadpool --SimpleThread.java 工作线程 --TestThreadPool.java 程序入口 --ThreadPoolManager.java 线程池管理类

    java 四种线程池实例

    本文将深入探讨四种常见的Java线程池实例:`ThreadPoolExecutor`、`Executors`提供的固定线程池、单线程池和定时线程池。 1. **ThreadPoolExecutor**: 这是最基础也是最灵活的线程池实现,可以通过`new ...

    java多线程,对多线程,线程池进行封装,方便使用

    使用线程池可以有效地控制运行的线程数量,避免过多线程导致系统资源过度消耗,同时也能简化线程的管理和回收。Java的ExecutorService和ThreadPoolExecutor是线程池的核心组件。 1. **线程创建和销毁成本** 创建和...

    Java实现的线程池、消息队列功能

    标题中的“Java实现的线程池、消息队列功能”是指在Java编程中,如何利用编程技术实现线程池和消息队列这两种重要的并发处理机制。线程池和消息队列是解决多线程环境下资源管理和任务调度的有效手段,它们在高并发、...

    Java8并行流中自定义线程池操作示例

    更多java相关内容感兴趣的读者可查看本站专题:《Java进程与线程操作技巧总结》、《Java数据结构与算法教程》、《Java操作DOM节点技巧总结》、《Java文件与目录操作技巧汇总》和《Java缓存操作技巧汇总》希望本文所...

    Java线程池使用说明

    例如,在代码示例中,使用Executors.newSingleThreadExecutor()创建了一个单线程线程池,并且通过pool.execute(t1);等方法提交了多个任务到线程池中执行。最后,通过调用pool.shutdown()方法来关闭线程池,释放相关...

    java线程池完整代码

    Java 线程池是 Java 语言中的一个重要概念,它允许开发者创建和管理多个线程,以提高程序的并发性和性能。下面是对给定文件的解析,包括 title、description、标签和部分内容的解析。 标题解析 标题 "Java 线程池...

    Java、Android多线程、线程池Demo

    在Java和Android开发中,多线程和线程池是两个关键的概念,它们对于提高程序的执行效率和优化系统资源的使用至关重要。本教程将深入探讨这两个主题,并通过一个名为"ThreadPoolDemo"的示例代码来阐述其核心原理和...

Global site tag (gtag.js) - Google Analytics