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("计划任务线程池中的线程");
        }
    }, 01000,
TimeUnit.MILLISECONDS);
}

四、计划任务运行使用

通过上面的样例。也看到了计划任务线程池的使用方式。对于计划任务,除了能够运行普不同线程池中线程的任务以外。还能够运行计划任务特殊的线程要求。比方:scheduleWithFixedDelay(command, initialDelay, delay, unit);在初始化延迟之后,以特定的延迟时间反复运行。scheduleAtFixedRate(command, initialDelay,
period, unit);在初始化延迟时间之后,以固定频率反复运行。这两种的差别是下一次运行时间延迟计算的開始时间不同,第一种是从上一次任务開始运行的时候计算。另外一种是从上一次任务运行结束的时候计算。这两种和java之前版本号中Timer类非常相似。

可是Timer有非常多缺陷,具体的缺陷不再具体说明。而这些缺陷都能够通过ScheduledExecutorService给完美解决。所以是时候该丢弃Timer了。

java线程和线程池的使用的更多相关文章

  1. java 多线程 4 线程池

    系统启动一个新线程的成本是比较高的,因为它涉及到与操作系统的交互.在这种情况下,使用线程池可以很好的提供性能,尤其是当程序中需要创建大量生存期很短暂的线程时,更应该考虑使用线程池. 与数据库连接池类似 ...

  2. (转)java自带线程池和队列详细讲解 - CSDN过天的专栏

    一简介 线程的使用在java中占有极其重要的地位,在jdk1.4极其之前的jdk版本中,关于线程池的使用是极其简陋的.在jdk1.5之后这一情况有了很大的改观.Jdk1.5之后加入了java.util ...

  3. 线程和线程池的理解与java简单例子

    1.线程 (1)理解,线程是系统分配处理器时间资源的基本单元也是系统调用的基本单位,简单理解就是一个或多个线程组成了一个进程,进程就像爸爸,线程就像儿子,有时候爸爸一个人干不了活就生了几个儿子干活,会 ...

  4. Java 四种线程池newCachedThreadPool,newFixedThreadPool,newScheduledThreadPool,newSingleThreadExecutor

    介绍new Thread的弊端及Java四种线程池的使用,对Android同样适用.本文是基础篇,后面会分享下线程池一些高级功能. 1.new Thread的弊端执行一个异步任务你还只是如下new T ...

  5. Java四种线程池

    Java四种线程池newCachedThreadPool,newFixedThreadPool,newScheduledThreadPool,newSingleThreadExecutor 时间:20 ...

  6. java自带线程池和队列详细讲解

    Java线程池使用说明 一简介 线程的使用在java中占有极其重要的地位,在jdk1.4极其之前的jdk版本中,关于线程池的使用是极其简陋的.在jdk1.5之后这一情况有了很大的改观.Jdk1.5之后 ...

  7. java笔记--使用线程池优化多线程编程

    使用线程池优化多线程编程 认识线程池 在Java中,所有的对象都是需要通过new操作符来创建的,如果创建大量短生命周期的对象,将会使得整个程序的性能非常的低下.这种时候就需要用到了池的技术,比如数据库 ...

  8. Java四种线程池newCachedThreadPool,newFixedThreadPool,newScheduledThreadPool,newSingleThreadExecutor

    1.new Thread的弊端 执行一个异步任务你还只是如下new Thread吗? Java new Thread(new Runnable() { @Override public void ru ...

  9. Java多线程之线程池

    现在是多核的时代,面向多核的编程很重要,因此基于java的并发和多线程开发非常重要. 线程池是于队列密切相关的,其中队列保存了所有等待执行的任务.工作者线程的任务很简单:从队列中获取一个任务,执行任务 ...

  10. Java多线程和线程池

    转自:http://blog.csdn.net/u013142781/article/details/51387749 1.为什么要使用线程池 在Java中,如果每个请求到达就创建一个新线程,开销是相 ...

随机推荐

  1. 树莓派zero_w设置中文(已成功)

    树莓派默认是采用英文字库的,而且系统里没有预装中文字库,所以即使你在locale中改成中文,也不会显示中文,只会显示一堆方块.因此需要我们手动来安装中文字体. 好在有一个中文字体是免费开源使用的.ss ...

  2. 【C++】智能指针简述(三):scoped_ptr

    在介绍scoped_ptr之前,我们先回顾一下前两篇文章的内容. 首先,智能指针采用RAII机制,通过对象来管理指针,构造对象时,完成资源的初始化;析构对象时,对资源进行清理及汕尾. auto_ptr ...

  3. java 物理分页和逻辑分页

    A.逻辑分页利用游标分页,好处是所有数据库都统一,坏处就是效率低.1.逻辑分页的第一种方式,利用ResultSet的滚动分页.这种分页方式依靠的是对结果集的算法来分页,因此通常被称为“逻辑分页”.步骤 ...

  4. 由DB2分页想到的,关于JDBC ResultSet 处理大数据量

    最近在处理DB2 ,查询中,发现如下问题.如果一个查询 count(*),有几十万行,分页如何实现 select row_number() over (order by fid desc ) as r ...

  5. swift extension 的最终目的是结合

    与数据结合:对数据进行操作: 与行为结合:使用原有行为生成更便捷的行为: 与协议结合:实现协议: 与类型结合:对类型数据进行操作,添加新的行为: 与关联类型.泛型结合:对类型做出限定.

  6. RabbitMQ系列(三)--Java API

    基于java使用RabbitMQ 框架:SpringBoot1.5.14.RELEASE maven依赖: <dependency> <groupId>com.rabbitmq ...

  7. linux-vmstat-显示虚拟内存状态

    推荐:更多linux 性能监测与优化 关注:linux命令大全 vmstat命令的含义为显示虚拟内存状态(“Viryual Memor Statics”),但是它可以报告关于进程.内存.I/O等系统整 ...

  8. LINUX-初始化一个文件系统

    mkfs /dev/hda1 在hda1分区创建一个文件系统 mke2fs /dev/hda1 在hda1分区创建一个linux ext2的文件系统 mke2fs -j /dev/hda1 在hda1 ...

  9. dstat系统分析工具的使用

    1.安装 方法一:yum #yum install -y dstat 方法二:rpm 官网下载地址: http://dag.wieers.com/rpm/packages/dstat #wget ht ...

  10. 【转】sizeof()用法总结

    传送门:https://blog.csdn.net/u011677209/article/details/52837065