"多线程和线程池"-之通俗易懂的介绍+图文,代码解析
来源:互联网 发布:软件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
- "多线程和线程池"-之通俗易懂的介绍+图文,代码解析
- "startActivityForResult"-通俗易懂图文+代码全解析
- AVL树建立 删除 最详细 最通俗易懂的代码 加图文解析!!!
- "二叉树"-实现数据结构算法,完全解析,通俗易懂的图文及代码讲解
- 带你通俗易懂的理解——线程、多线程与线程池
- 带你通俗易懂的理解——线程、多线程与线程池
- 多线程之四大线程池的使用介绍
- 多线程加强之线程解析
- 线程池(通俗易懂)
- "二分法"-"折半法"-查找算法-之通俗易懂,图文+代码详解-java编程
- Adaboost入门教程——最通俗易懂的原理介绍(图文实例)
- 二项堆(一)之 图文解析 和 C语言的实现
- iOS开发教程之线程关于多线程的简单介绍
- 线程和进程的深入理解(通俗易懂版)
- 通俗易懂的php多线程解决方案
- 多线程 之 线程组(ThreadGroup)和线程组的中断
- Android进阶——多线程系列之四大线程池的使用介绍
- 多线程(四)线程的同步之同步代码块
- 希尔排序算法思想及实现过程
- Swift中设置tableview的分割线(separator)的样式、颜色、边距
- RAII惯用法:C++资源管理的利器
- Java web使用第三方插件JFreeChart进行图形开发
- C# 多线程的自动管理(线程池)
- "多线程和线程池"-之通俗易懂的介绍+图文,代码解析
- android UI 组件-对话框
- javascript 检查邮件是否正确
- java 容器、数组相互转换
- Eclipse很卡很受不了,这个小技巧能帮上忙
- NAT类型与NAT穿越技术
- Preconditions优雅的检验参数
- linux环境下新建weblogic域
- 用Redis的set指令实现锁