"多线程和线程池"-之通俗易懂的介绍+图文,代码解析

来源:互联网 发布:软件p光头 编辑:程序博客网 时间:2024/06/07 06:57

一.多线程的重点部分

1.两种创建方式

1.继承Threa类2.实现Runnable接口最后都通过重写run方法实现线程

2.wait和sleep区别

1.wait释放锁,sleep不释放2.wait用于线程间交互,sleep用于暂停3.wait等待CPU,sleep攒着CPU睡觉

3.synchronized和volatile关键字作用

volatile :易变,不稳定之意,一个成员变量被修饰后:1.保证了不同线程对这个变量操作时的可见性:一个线程修改了某变量值,该值对其他线程立即可见.2.禁止进行指令重排序    volatile告诉jvm,当前变量在寄存器(工作内存)中值是不确定的,需要从主存中读取    synchronized锁定当前变量,只有当前线程可访问,其他的阻塞.    1.volatile只能在变量级别        synchronized可在变量,方法,类级别    2.volatile紧能实现变量的修改可见性,不能保证原子性        synchronized能实现变量的修改可见性并能保证原子性    3.volatile不会造成线程的阻塞        synchronized可能会造成线程的阻塞。    4.volatile 标记的变量不会被编译器优化;        synchronized 标记的变量可以被编译器优化;

4.线程并发访问代码

public class Demo3 {    public static void main(String[] args) {        final Counter counter = new Counter();        for (int i = 0; i < 1000; i++) {            new Thread(new Runnable() {                @Override                public void run() {                    counter.inc();                }            }).start();        }        System.out.println(counter);    }}class Counter {    private volatitle int count = 0;    public void inc() {        try {            Thread.sleep(3);        } catch (Exception e) {            e.printStackTrace();        }        //增加        count++;    }    @Override    public String toString() {        return "[count = " + count + "]";    }}

问,是否打印1000?

不是,结果不可能等于1000,肯定是小于1000的,线程安全问题,所以小于,不管有无都小于.

线程池的重点部分

5.线程池是什么?怎么使用?
1.”事先”将”多个线程”放入一个容器中;
2.使用的时候就不用new线程而是从”池”中拿;
3.节省了开辟线程的时间,提高代码的执行效率;

使用方法Executors的工厂方法提供的5种不同的线程池,都是.newXXX,自动回收的,固定大小的,调度的,单例的.    (java.util.concurrent.Executors-同时进行.执行):    1.ExecutorService newCachedThreadPool  = Executors.newCachedThreadPool();     2.ExecutorService newFixedThreadPool = Executors.newFixedThreadPool(4);    3.ScheduledExecutorService newScheduledThreadPool = Executors.newScheduledThreadPool(4);     4.ExecutorService newSingleThreadExecutor = Executors.newSingleThreadExecutor();    然后再调用他们的execute()方法即可;

创建举例:
固定线程数量和单个:

public class Demo3 {    public static void main(String[] args) {        //预先创建6个线程        MyThread t1 = new MyThread();        MyThread t2 = new MyThread();        MyThread t3 = new MyThread();        MyThread t4 = new MyThread();        MyThread t5 = new MyThread();        MyThread t6 = new MyThread();        //创建一个线程池,即容器        ExecutorService executorService = Executors.newFixedThreadPool(4);//设定同时运行的个数        //单例线程,任意时间池中只能有一个线程        //一个池最多只能同时执行一个        ExecutorService executor = Executors.newSingleThreadExecutor();        //把线程"们"放入池中,并执行        executorService.execute(t1);        executorService.execute(t2);        executorService.execute(t3);        executorService.execute(t4);        executorService.execute(t5);        executorService.execute(t6);        //关闭池        executorService.shutdown();    }}class MyThread extends Thread {    @Override    public void run() {        for (int i = 1; i < 101; i++) {            System.out.println(Thread.currentThread().getName() + " 正在执行...." + i);        }    }}

输出结果(4个线程):

pool-1-thread-2 正在执行....4pool-1-thread-4 正在执行....1pool-1-thread-1 正在执行....11pool-1-thread-4 正在执行....2pool-1-thread-2 正在执行....5pool-1-thread-3 正在执行....2

一次只能执行一次线程(1个线程):

pool-1-thread-1 正在执行....5pool-1-thread-1 正在执行....6pool-1-thread-1 正在执行....7pool-1-thread-1 正在执行....8pool-1-thread-1 正在执行....9pool-1-thread-1 正在执行....10

>

6.对线程池的理解

三个好处:1.降低能源消耗,通过反复利用已经创建线程,减少线程的创建和销毁造成的消耗2.提高响应速度,任务一到达不需要等待线程创建就能立即执行2.提高了线程的可管理性,线程是奇缺资源,如果不停的创建,还会消耗系统资源,对系统的稳定性造成影响.

线程池

1."线程池"就是一个容器,存放"Thread"or"Runable",2.若每次有任务都"new Thread()"开启一个线程,那么就会"大量的消耗CPU"的资源,导致"Android"运行变慢,甚至"OOM(out of memory)" ,因而"java"就出现了一个"ThreadPoolExecutor"来管理这些线程。    1.控制最多的线程数"maximumPoolSize"     2.核心线程数"corePoolSize",来管理我们需要开启的线程数。目的:减少了创建和销毁线程的次数,每个工作线程都可以被重复利用,可执行多个任务所以:我们就可以根据"手机的CPU"核数来控制App可以开的最大线程数。保证程序的合理运行创建线程池对象

几个参数的完全理解

1.比如去火车站买票, 有10个售票窗口, 但目前只有5个窗口开放(日常情况). 那么对外开放的5个窗口称为核心线程数"corePoolSize",2.而最大线程数"maximumPoolSize"是10个窗口.3.如果5个窗口都被占用, 那么后来的人就必须在后面排队, 但后来售票厅人越来越多, 已经人满为患, 就类似于线程队列new  "LinkedBlockingQueue<Runnable>()"已满.4.这时候火车站站长下令, 把剩下的5个窗口也打开, 也就是目前已经有10个窗口同时运行(春运). 后来又来了一批人,5.10个窗口也处理不过来了, 而且售票厅人已经满了, 这时候站长就下令封锁入口,不允许其他人再进来, 这就是线程异常处理策略.6.而线程存活时间"keepAliveTime"指的是, 允许售票员休息的最长时间, 以此限制售票员偷懒的行时间。休息一下在处理。线程池实际:来5个人,正好,来第六个,如果等了500毫秒还没人出来,那么开第6个窗口,然后再78910个人就开10个窗口,多过10个人,就按照自定义策略处理,当少<10个人,那么窗口会对比空闲时间,超过就关闭.

7.线程池启动策略

这里写图片描述

图解
这里写图片描述

官方建议用Executor的工厂方法创建,以前最常用的方法是ThreadPoolExecutor,线程池执行

案例1:

    public class ThreadPool {        public static void main(String[] args) {            //线程池            ThreadPoolExecutor threadPool = new ThreadPoolExecutor(                    3   //corePoolSize:线程池维护线程的最少数量                    , 5 //maximumPoolSize:最大限度线程数                    , 3 //keepAliveTime:线程池维护线程的空闲时间                    , TimeUnit.SECONDS //unit:对应单位是什么                    , new ArrayBlockingQueue<Runnable>(5)//workQueue,缓存队列数                    , new ThreadPoolExecutor.DiscardOldestPolicy()//handler:拒绝任务的处理策略            );            for (int i = 1; i <= 10; i++)            {                try                {                    // 产生一个任务,并将其加入到线程池                    String task = "task@ " + i;                    System.out.println("put " + task);                    threadPool.execute(new ThreadPoolTask(task));                    // 便于观察,等待一段时间,慢点加                    Thread.sleep(10);                }                catch (Exception e)                {                    e.printStackTrace();                }            }            //关闭            threadPool.shutdown();        }    }    /**     * 参数:     * 1.任务通过executor(Runnable)方法添加到线程池     * 2.任务就是一个runable对象     * 3.认为的执行方法就是run();     *     *优先级:     * 1.核心线程corePoolSize     * 2.任务队队列workQueue     * 3.最大限度线程     * 如果都满了,hanlder策略处理     *     * 线程数量大于最小数量,如果空闲时间超过了keepAliveTime,那么线程将会被终止.即线程池动态的调整池中线程数量.     */    class ThreadPoolTask implements Runnable    {        // 保存任务所需要的数据        private Object threadPoolTaskData;        ThreadPoolTask(Object tasks)        {            this.threadPoolTaskData = tasks;        }        public void run()        {            // 处理一个任务,仅是一个打印            System.out.println(Thread.currentThread().getName()+"...start ..." + threadPoolTaskData);            try            {               //便于观察,等待一段时间                Thread.sleep(1000);            }            catch (Exception e)            {                e.printStackTrace();            }        }    }   

打印结果:

put task@ 1pool-1-thread-1...start ...task@ 1put task@ 2pool-1-thread-2...start ...task@ 2put task@ 3pool-1-thread-3...start ...task@ 3put task@ 4put task@ 5put task@ 6put task@ 7put task@ 8put task@ 9pool-1-thread-4...start ...task@ 9put task@ 10pool-1-thread-5...start ...task@ 10pool-1-thread-1...start ...task@ 4pool-1-thread-2...start ...task@ 5

案例2:

    /**     * 需求:     * 1.100个生产任务     * 2.核心线程数是3     * 3.最大线程数是5     * 4.2秒进行线程的回收     * 5.队列最多有6个在排队     *     * 1.当排队满的时候,给出提示,正在使用策略     * 2.打印过程数据     */    public class ThreadPoolExecutorTest    {        private ThreadPoolExecutor mTpe;        public void createThreadPool() {            /*             * 创建线程池,最小线程数为3,最大线程数为5,线程池维护线程的空闲时间为3秒,             * 使用队列深度为6的有界队列,如果执行程序尚未关闭,则位于工作队列头部的任务将被删除,             */            /**             * AbortPolicy  队列满"抛出异常"             * CallerRunsPolicy     "重试添加"当前任务,会自动调用execute()方法             * DiscardOldestPolicy  抛弃"旧"任务             * DidscardPolicy       抛弃"当前任务"             *             */            ThreadPoolExecutor.CallerRunsPolicy HANDLER = new ThreadPoolExecutor.CallerRunsPolicy();            mTpe = new ThreadPoolExecutor(3, 5, 2, TimeUnit.SECONDS, new ArrayBlockingQueue<Runnable>(6),HANDLER);            // 向线程池中添加 100 个任务            for (int i = 0; i < 100; i++) {                mTpe.execute(new TaskThreadPool(i));            }            // 关闭线程池            mTpe.shutdown();        }        public static void main(String[] args)        {            ThreadPoolExecutorTest test = new ThreadPoolExecutorTest();            test.createThreadPool();        }        class TaskThreadPool implements Runnable        {            private int index;            public TaskThreadPool(int index)            {                this.index = index;            }            public void run() {                System.out.println("线程池中任务数是" + mTpe.getQueue().size());                System.out.println(Thread.currentThread().getName() + " 生产中... :" + index);                try {                    //模拟生产耗时,一个工序要3秒                    Thread.sleep(100);                } catch (InterruptedException e) {                    e.printStackTrace();                }                System.out.println(Thread.currentThread().getName() + " 完成了 :" + index);            }        }    }

打印结果

线程池中任务数是6线程池中任务数是6线程池中任务数是6线程池中任务数是6main 生产中... :11线程池中任务数是6pool-1-thread-1 生产中... :0pool-1-thread-4 生产中... :9线程池中任务数是6pool-1-thread-3 生产中... :2pool-1-thread-2 生产中... :1pool-1-thread-5 生产中... :10pool-1-thread-1 完成了 :0pool-1-thread-4 完成了 :9main 完成了 :11线程池中任务数是4pool-1-thread-4 生产中... :4线程池中任务数是5pool-1-thread-1 生产中... :3线程池中任务数是6main 生产中... :14pool-1-thread-2 完成了 :1pool-1-thread-5 完成了 :10pool-1-thread-3 完成了 :2线程池中任务数是3pool-1-thread-3 生产中... :7

8.怎么控制某个方法允许并发访问线程个数

采用Semaphore,信号的意思

1.创建对象,构造传入并发线程数量       static Semaphore semaphore = new Semaphore(5, true);2.申请请求    semaphore.acquire();3.释放    semaphore.release();

例子:

    class Demo3 {        //1.创建一个对象        static Semaphore semaphore = new Semaphore(5, true);        public static void main(String[] args) {            //并发100个线程            for (int i = 0; i < 100; i++) {                new Thread(new Runnable() {                    @Override                    public void run() {                        test();                    }                }).start();            }        }        public static void test(){            try {                //2.申请一个请求 ,相当于收尾包住                semaphore.acquire();            } catch (InterruptedException e1) {                e1.printStackTrace();            }            System.out.println(Thread.currentThread().getName()+"进来了");            try {                Thread.sleep(1000);            } catch (InterruptedException e) {                e.printStackTrace();            }            System.out.println(Thread.currentThread().getName()+"走了");            //3.释放一个请求            semaphore.release();        }    }

打印结果
添加后,控制了,进来了同时最多5个:

Thread-1进来了Thread-56进来了Thread-2进来了Thread-3进来了Thread-4进来了Thread-1走了Thread-5进来了

不添加时:

Thread-0进来了Thread-1进来了Thread-2进来了Thread-3进来了Thread-4进来了Thread-5进来了Thread-6进来了Thread-7进来了
0 0
原创粉丝点击