Android必备知识(二)多线程和线程池

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/Findyoulucky/article/details/87908109

android 必备知识——多线程和线程池

首先,以一道面试题为例:
控制一个方法的并发量,比如同时只能有5个线程进来。

假设现在有个需求方法名称为method(),最简单的限制方法是,在声明这个method方法的时候,加上synchronize,那这样每次不管多少线程同时申请调用它,都只能有一个线程真正能调用到。

有两种正确的解决思路,第一种:
需要用到一个工具类Semaphore,由著名java大牛Doug Lea编写的,这个类为我们提供了对线程的控制,具体语法是:

private Semaphore semaphore =new Semaphore(5);//如果是需要被静态方法调用,
//声明的时候应该加上static
//这里补充静态方法和非静态方法之间的调用关系,在静态方法里面不能调用非静态方法,
//在非静态方法里能调用静态方法,因为静态方法和非静态方法在jvm中存储的位置不一样

其中参数的意义源码写的很清楚:int permits,意思很清楚,就是要求限制的线程数。

相信学过操作系统的同学都了解信号量的申请(P操作)和释放(V操作),在java中,对semaphore的使用原理也是一样的,具体语法是:

semaphore.acquire();
...
核心代码
...
semaphore.release();

理解起来很容易,每当acquire一次,限制的允许线程数就-1,当允许的线程数是0时,再acquire就会陷入等待,等待有release时,申请的线程就会被唤醒。
这样就完成了只允许同时执行5个线程的需求。

第二种思路是使用线程池:
语法是:

private Executor executor=Executors.newCachedThreadPool();//缓存向量池
private Executor executor2=Executors.newFixedThreadPool(5);//固定线程个数的线程池
private Executor executor3=Executors.newScheduledThreadPool(5);//计划任务线程池
private Executor executor4=Executors.newSingleThreadExecutor();//单线程

点进去查看源码可以发现,这个工具类只是对线程池的一个封装,封装中的内容才是真正要掌握的线程池的内容,这才是最重要最关键的,任何一个需要写框架的人都必定会用到的内容,除非一辈子只想用比人写的框架,那永远也无法成为一个高级程序员。

public static ExecutorService newCachedThreadPool() {
        return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
                                      60L, TimeUnit.SECONDS,
                                      new SynchronousQueue<Runnable>());
    }

线程池的使用就比较简单:

		executor2.execute(new Runnable() {
			
			@Override
			public void run() {
				// TODO Auto-generated method stub
				
			}
		});

如果只是满足于使用现有的封装类,那离掌握线程池还差了很远。就比如,线程池的使用策略是什么?

因此必须自己亲手封装一次,才能更进一步理解线程池的原理。

线程不是new的时候创建,而是当要执行线程中的方法时才会创建,当用于存储线程的集合中的线程数达到核心池大小时,如果还要继续创建线程,就会创建到workQueue(任务队列)里面,当任务队列也满了,就会继续在用于存储线程的集合中创建,如果集合中的线程数达到了最大线程数,就会报拒绝异常。这就是线程的创建过程。

在这里插入图片描述

/*
		 * 参数1:核心池个数,google建议最好是cpu核心数+1
		 * 参数2:最大线程池上限个数,也就是同时能运行的线程最多线程数
		 * 参数3:任务执行完后,将线程抹去的延迟时间,时间到了就会抹去所有非核心池的线程
		 * 参数4:时间单位(枚举类型)
		 * 参数5:用于存储任务的工作队列,类型是BlockQueue
		 * 参数6:线程工厂,用于创建线程
		 * 
		 */
		LinkedBlockingQueue<Runnable> blockingQueue=new LinkedBlockingQueue<Runnable>(100);//100是该容器的最大上限
		
		ThreadFactory threadFactory=new ThreadFactory() {
			
			//这是一个线程安全的整数包装类 
			AtomicInteger atomicInteger=new AtomicInteger(0);
			
			@Override
			public Thread newThread(Runnable r) {
				
				Thread thread =newThread(r);
				thread.setName("线程编号是"+atomicInteger.getAndAdd(1));
				
				return thread;
			}
		};
		
		
		
		ThreadPoolExecutor threadPoolExecutor=new ThreadPoolExecutor(5, 10, 1, TimeUnit.SECONDS, blockingQueue, threadFactory);

		//使用自己定义的线程池
		threadPoolExecutor.execute(new Runnable() {
			
			@Override
			public void run() {
				// 这里是需要线程调用的方法
				
			}
		});
		
		
		
	}

这就是封装线程池的核心代码,主要的是搞清楚各个参数的意义,其中需要注意的是,在线程工厂中,用到了一个AtomicInteger 工具类,因为本身我们就是在做多线程,如果只是单纯的用一个定义的整数int i,那对它操作本身就是不安全的,因为可能同时有很多个线程操作。

以上就是java多线程有关的内容,如有不足,请多多指教。

猜你喜欢

转载自blog.csdn.net/Findyoulucky/article/details/87908109