线程和线程池

这篇博客中简单介绍了下线程和线程池,今天主要 从示例上更加了解线程和线程池的工作
http://blog.csdn.net/androidxiaogang/article/details/49682885
文章为自己搜索总结,加上自己的一点看法,如果有误,欢迎指正。

1、进程,线程,多线程

从普通PC应用上来讲:
开个QQ,开了一个进程,在QQ的这个进程里,发送消息或者下载文件之类的,这就是打了线程,我们可能下载多个文件时,就会用到多线程。

从Android上来讲:
当我们点击应用图标时,就是开了进程,然后在main()方法后,打开主界面什么之类(activity,fragment之类的)这是主线程(UI线程),我们可能在这些界面上请求数据之类的,这就要用子线程(如果多个线程的话,就使用多线程,或者线程池)

1、一个进程启动后,它至少有一个线程,这个线程就是主线程。然后这个线程可以创建更多线程,比如说子线程,线程池之类的

2、应用程序不能脱离线程,每一个事件都在主线程或者子线程中运行。进程由线程组成。

2、Java,Android中的主线程

1、当java程序开始运行的时候,程序会创建一个主线程,这个主线程的开启是在main()主函数运行时确定的。

2、Android是由java代码写的,与java一样,都是在main()方法中启动主线程(UI)线程的,一个android应用只有一个Main()函数,Main()函数是应用程序的入口。android中的Main()函数是在framework中的ActivityManagerService(AMS)中的ActivityThread中进行了封装。

注:android和java一样,main函数都是程序的入口,都只有一个,很多人认为android中有很多main函数,就像一个Activity,其实Activity只是一个基本的java类一样,android的framework层封装了Main()函数,不对外暴漏。也就是android中只有一个main函数,在启动main函数时这创建主线程(UI线程)在这个线程中创建Activity,Fragment,Service等。当我们请求后台数据时要开子线程,数据回来后,在主线程中刷新界面。

3、线程

1、线程的创建方式

  1. 继承Thread类创建线程类
  2. 实现Runnable接口创建线程类
  3. jdk1.5后提供一个Callable接口实现线程类

2、线程的生命周期

这里写图片描述
1、新建 new Thread
当new一个新的线程的时候,该线程处于新建状态,由JVM分配内存,程序不会执行线程体中的内容。

2、调用start()方法后,处理就绪
启动线程,是调用start()方法,而不是run()方法,调用run()方法与调用一个普通的java方法没有区别。而不是启动线程的方法。

3、运行或者阻塞状态
运行到阻塞状态:

  1. 线程调用sleep(),join(),wait(),notify()方法,主动放弃处理器资源。
  2. 线程调用IO方法返回
  3. 线程在收到notify通知
  4. 线程中调用resume
  5. 等待同步锁

从阻塞到就绪状态

  1. 线程调用sleep(),join(),wait(),notify()方法结束
  2. 线程调用IO方法,返回前阻塞状态
  3. 线程在等待notify通知
  4. 线程中调用suspend方法(挂起线程,容易引起死锁)
  5. 获取到同步锁

4、死亡状态
1. run方法执行完毕
2. 异常
3. 调用了stop方法结束当前线程(容易导致死锁)

几个重要的方法

1、join():当在某个程序执行中调用了join()方法时,调用线程将被阻塞,直到被join()的线程执行完毕。

2、sleep()和yield()的区别:
sleep():是让当前的线程暂停,进行阻塞状态,在进入sleep()的时候内,该线程不会得到执行的机会。这样就给其他线程带来执行机会。
yield():与sleep()方法相似,但不会阻塞当前线程。它将线程转为就绪状态

4、线程同步

多个线程同时访问时就会出现线程安全问题,比如卖票,如果不进行线程控制,可能把0,-1张票卖出去。

同步代码块:用synchronized修饰的代码块

同步方法:用synchronized修饰的方法

5、线程通信

java中提供一些机制来保证线程协调运行。
wait(),notify()和notifyAll()方法要求在调用时线程已经获得了对象的锁,因此对这两个方法的调用需要放在synchronized方法或synchronized块中。

1、wait():使当前线程等待。直到其他线程用同步监视器(synchronized)的notify()或者notifyAll()方法。
2、notify()唤醒在些同步监视器(synchronized)上等待的单个线程。
3、notifyAll()唤醒在些同步监视器(synchronized)上等待的所有线程。
下面,通过一个示例来演示线程间的通信
示例:我们开启两个线程,一个线程打印1,另一个线程打印2
要求打印结果是:12121212….轮流打印
这里写图片描述
代码如下:

public class Number {
    private  int i=1;
    public Number(){

    }
    public synchronized  void printFisrt()  {
        System.out.print(1);
        //先唤醒其他同步监视器下的线程,然后当前线程处于阻塞状态;
        //如果先调用wait()然后调用notify的话会发生死锁。
        notify();
        try {
            wait();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

    }
    public synchronized  void printSecond()  {
        System.out.print(2);
        //先唤醒其他同步监视器下的线程,然后当前线程处于阻塞状态;
        notify();
        try {
            wait();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

    }
}

打印1的线程FirstThread代码

public class FisrtThread extends Thread {
    Number n;

    public FisrtThread(Number n) {
        this.n = n;
    }

    @Override
    public void run() {
        for (int i = 0; i < 10; i++) {
            n.printFisrt();
        }
    }
}

打印2的线程SecondThread代码

public class SecondThread extends Thread {
    Number n;
    public SecondThread(Number n){
        this.n=n;
    }
    @Override
    public void run() {
     for(int i=0;i<10;i++){
         n.printSecond();
     }
    }
}

测试代码如下:

public class ThreadDemo {
    public static void main(String[] args) {
        Number n = new Number();
        Thread t1 = new FisrtThread(n);
        Thread t2 = new SecondThread(n);
        t1.start();
        t2.start();
    }
}

这里写图片描述

6、线程池

线程池是一种多线程处理形式,处理过程中将任务添加到队列,然后在创建线程后自动启动这些任务。
适用情况:
大量短小的任务。Web 服务器、数据库服务器、文件服务器或邮件服务器之类的许多服务器应用程序都面向处理来自某些远程来源的大量短小的任务。请求以某种方式到达服务器,这种方式可能是通过网络协议(例如 HTTP、FTP 或 POP)、通过 JMS 队列或者可能通过轮询数据库。不管请求如何到达,服务器应用程序中经常出现的情况是:单个任务处理的时间很短而请求的数目却是巨大的。

线程池的好处:
1、提高性能(减少线程的创建与销毁,对于一些短小的请求,线程的创建和销毁,比线程使用更耗费内存)
2、有效的集中控制并发的数量,容易管理维护。
3、根据需求,手动设计线程池的大小。

线程池的工作原理:
jdk1.5提供创建线程池的源代码如下:

public ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue workQueue,
                              ThreadFactory threadFactory,
                              RejectedExecutionHandler handler) {
        if (corePoolSize < 0 ||
            maximumPoolSize <= 0 ||
            maximumPoolSize < corePoolSize ||
            keepAliveTime < 0)
            throw new IllegalArgumentException();
       if (workQueue == null || threadFactory == null || handler == null)
             throw new NullPointerException();
         this.corePoolSize = corePoolSize;
         this.maximumPoolSize = maximumPoolSize;
         this.workQueue = workQueue;
         this.keepAliveTime = unit.toNanos(keepAliveTime);
         this.threadFactory = threadFactory;
         this.handler = handler;
     }

1、corePoolSize :核心运行的poolSize,也就是当超过这个范围的时候,就需要将新的 Runnable 放入到等待队列 workQueue 中了,我们把这些Runnable就叫做要去执行的任务吧。

2、maximumPoolSize :一般你用不到,当大于了这个值就会将 任务 由一个丢弃处理机制来处理,但是当你发生: newFixedThreadPool 的时候, corePoolSize 和 maximumPoolSize 是一样的,而 corePoolSize 是先执行的,所以他会先被放入等待队列,而不会执行到下面的丢弃处理中,看了后面的代码你就知道了。

3、workQueue :等待队列,当达到corePoolSize的时候,就向该等待队列放入线程信息(默认为一个 LinkedBlockingQueue ),运行中的线程属性为: workers ,为一个 HashSet ;我们的 Runnable 内部被包装了一层,后面会看到这部分代码;这个队列默认是一个无界队列(你也可以设定一个有界队列),所以在生产者疯狂生产的时候,考虑如何控制的问题。

4、keepAliveTime :默认都是0,当线程没有任务处理后,保持多长时间,当你使用: newCachedThreadPool() ,它将是60s的时间。这个参数在运行中的线程从 workQueue 获取任务时,当(poolSize >corePoolSize || allowCoreThreadTimeOut)会用到,当然 allowCoreThreadTimeOut 要设置为true,也会先判定 keepAliveTime 是大于0的,不过由于它在 corePoolSize 上采用了 Integer.MAX_VALUE ,当遇到系统遇到瞬间冲击, workers 就会迅速膨胀,所以这个地方就不要去设置 allowCoreThreadTimeOut =true,否则结果是这些运行中的线程会持续60s以上;另外,如果corePoolSize的值还没到Integer.MAX_VALUE,当超过那个值以后,这些运行中的线程,也是

5、threadFactory :是构造Thread的方法,你可以自己去包装和传递,主要实现 newThread 方法即可;

6、handler :也就是参数 maximumPoolSize 达到后丢弃处理的方法,java提供了5种丢弃处理的方法,当然你也可以自己根据实际情况去重写,主要是要实现接口: RejectedExecutionHandler 中的方法: public void rejectedExecution(Runnabler, ThreadPoolExecutor e) java默认的是使用:AbortPolicy,他的作用是当出现这中情况的时候会抛出一个异常;

参考博客
http://www.tuicool.com/articles/7ZzENj

©️2020 CSDN 皮肤主题: 技术黑板 设计师:CSDN官方博客 返回首页