1.进程,线程概念
2.多线程
3.线程池
4.并发可能存在的问题。
5.例1 - 如何充分利用多核CPU,计算很大的List中所有整数的和
6.例2 - 生产者/消费者问题的多种Java实现方式
1.概念
进程(process):
▪ 操作系统中运行的一个任务(一个应用程序运行在一个进程中)。
▪ 进程是一块包含了某些资源的内存区域。操作系统利用进程把它的工作划分为一些功能单元。
线程(thread):
▪ 进程中所包含的一个或多个执行单元。
进程和线程的关系:
▪ 线程只能归属于一个进程且只能访问该进程所拥有的资源。
▪ 当操作系统创建一个进程后,该进程会自动申请一个名为主线程或首要线程的线程。
▪ 一个线程是进程的一个顺序执行留。
▪ 同类的多个线程共享一块内存空间和一组系统资源,线程本身有一个供程序执行时的堆栈。线程在切换时负荷小。
▪ 一个进程中可以包含多个线程
▪ 一个进程至少有一个线程
2.多线程
多线程是合理充分利用了CPU,内存资源。
实现多线程的两个方法:
1.继承Thread类
2.实现Runnable接口
3.线程池
使用线程池的原因:
一个服务器完成一项任务所需时间:创建线程时间T1,在线程中执行任务的时间T2,销毁线程时间T3。
当T1+T3远大于T2时,采用多线程技术可以减少处理器单元的闲置时间,增加处理器单元的吞吐能力。
线程池就是一个线程的容器,每次只执行额定数量的线程,线程池就是限制系统中执行线程的数量。
除了可以控制T1,T3的时间外,还显著减少了创建线程的数量
常用线程池
java通过java.util.concurrent.Executors提供了四种创建线程池的方法
Executors.newCachedThreadPool():创建一个可缓存线程池,如果线程池长度超过处理需要,可灵活回收空闲线程,若无可回收,则新建线程。
Executors.newFixedThreadPool():创建一个定长线程池,可控制线程最大并发数,超出的线程会在队列中等待。
Executors.newScheduledThreadPool():创建一个定长线程池,支持定时及周期性任务执行。
Executors.newSingleThreadExecutor(): 创建一个单线程化的线程池,它只会用唯一的工作线程来执行任务,保证所有任务按照指定顺序(FIFO, LIFO, 优先级)执行。
4.并发可能存在的问题。
并发原理线程并发运行,OS将时间划分为很多时间片段,尽可能均匀分配给每一个线程,获取时间片的线程被CPU运行,而其他线程全部等待。
这种现象较并发,并非绝对意义上的“同时发生”。
常见问题
1.多线程读写共享数据同步问题
2.并发读数据,保持各个线程读取到的数据一致性的问题
解决方案
1.同步(synchronized)和Lock并发所:主要解决多线程共享数据同步问题
2.ThreadLocal主要解决多线程中数据因并发产生不一致问题。
概念
1.synchronized利用锁机制,使变量或代码块在某一时刻只能被一个线程访问
2.ThreadLocal(JDK1.2,5.0以上版本支持泛型)为每一个线程提供了变量的副本,是的每个线程在某一时间访问到的并不是同一个对象。
区别:
synchronized用于多线程间通信时能够获得数据共享
ThreadLocal由于访问的不是同一个对象而隔离了多个线程对数据的共享。
5.例1 - 如何充分利用多核CPU,计算很大的List中所有整数的和
题目:如何充分利用多核CPU,计算很大的List中所有整数的和
分析:
1.多核+很大的List,需要采用多线程设计。将很大的List分割,每一小块分配一个线程计算求和。
2.依赖同步辅助类(java.util.concurrent.CyclicBarrier),保证一组线程可以互相等待,等到一组线程都完成(直到到达某个公共屏障点 (common barrier point)),再进入到下一个步骤。
3.分割List,根据采用的线程数平均分配,即list.size()/threadCounts。
4.定义一个记录“很大List”中所有整数和的变量sum,采用一个线程处理一个分割后的子List,计算子List中所有整数和(subSum),然后把和(subSum)累加到sum上。
5.等待所有线程(任务)完成后输出总和(sum)的值。
java code:
- /**
- * 计算List中所有整数的和<br>
- * 采用多线程,分割List计算
- * @author 飞雪无情
- * @since 2010-7-12
- */
- public class CountListIntegerSum {
- private long sum;//存放整数的和
- private CyclicBarrier barrier;//障栅集合点(同步器)
- private List<Integer> list;//整数集合List
- private int threadCounts;//使用的线程数
- public CountListIntegerSum(List<Integer> list,int threadCounts) {
- this.list=list;
- this.threadCounts=threadCounts;
- }
- /**
- * 获取List中所有整数的和
- * @return
- */
- public long getIntegerSum(){
- ExecutorService exec=Executors.newFixedThreadPool(threadCounts);
- int len=list.size()/threadCounts;//平均分割List
- //List中的数量没有线程数多(很少存在)
- if(len==0){
- threadCounts=list.size();//采用一个线程处理List中的一个元素
- len=list.size()/threadCounts;//重新平均分割List
- }
- barrier=new CyclicBarrier(threadCounts+1);
- for(int i=0;i<threadCounts;i++){
- //创建线程任务
- if(i==threadCounts-1){//最后一个线程承担剩下的所有元素的计算
- exec.execute(new SubIntegerSumTask(list.subList(i*len,list.size())));
- }else{
- exec.execute(new SubIntegerSumTask(list.subList(i*len, len*(i+1)>list.size()?list.size():len*(i+1))));
- }
- }
- try {
- barrier.await();//关键,使该线程在障栅处等待,直到所有的线程都到达障栅处
- } catch (InterruptedException e) {
- System.out.println(Thread.currentThread().getName()+":Interrupted");
- } catch (BrokenBarrierException e) {
- System.out.println(Thread.currentThread().getName()+":BrokenBarrier");
- }
- exec.shutdown();
- return sum;
- }
- /**
- * 分割计算List整数和的线程任务
- * @author lishuai
- *
- */
- public class SubIntegerSumTask implements Runnable{
- private List<Integer> subList;
- public SubIntegerSumTask(List<Integer> subList) {
- this.subList=subList;
- }
- public void run() {
- long subSum=0L;
- for (Integer i : subList) {
- subSum += i;
- }
- synchronized(CountListIntegerSum.this){//在CountListIntegerSum对象上同步
- sum+=subSum;
- }
- try {
- barrier.await();//关键,使该线程在障栅处等待,直到所有的线程都到达障栅处
- } catch (InterruptedException e) {
- System.out.println(Thread.currentThread().getName()+":Interrupted");
- } catch (BrokenBarrierException e) {
- System.out.println(Thread.currentThread().getName()+":BrokenBarrier");
- }
- System.out.println("分配给线程:"+Thread.currentThread().getName()+"那一部分List的整数和为:\tSubSum:"+subSum);
- }
- }
- }
- public class CountListIntegerSumMain {
- /**
- * @param args
- */
- public static void main(String[] args) {
- List<Integer> list = new ArrayList<Integer>();
- int threadCounts = 10;//采用的线程数
- //生成的List数据
- for (int i = 1; i <= 1000000; i++) {
- list.add(i);
- }
- CountListIntegerSum countListIntegerSum=new CountListIntegerSum(list,threadCounts);
- long sum=countListIntegerSum.getIntegerSum();
- System.out.println("List中所有整数的和为:"+sum);
- }
- }
6.例2 - 生产者/消费者问题的多种Java实现方式
分析: 并发控制的原理,它描述是有一块缓冲区作为仓库,生产者可以将产品放入仓库,消费者则可以从仓库中取走产品。
解决生产者/消费者问题的方法可分为两类:
(1)采用某种机制保护生产者和消费者之间的同步;
(2)在生产者和消费者之间建立一个管道。
第一种方式有较高的效率,并且易于实现,代码的可控制性较好,属于常用的模式。
第二种管道缓冲区不易控制,被传输数据对象不易于封装等,实用性不强。
同步问题核心在于:
如何保证同一资源被多个线程并发访问时的完整性。
常用的同步方法是采用信号或加锁机制,保证资源在任意时刻至多被一个线程访问。
Java语言在多线程编程上实现了完全对象化,提供了对同步机制的良好支持。
在Java中一共有四种方法支持同步,其中前三个是同步方法,一个是管道方法。
(1)wait() / notify()方法
(2)await() / signal()方法
(3)BlockingQueue阻塞队列方法
(4)PipedInputStream / PipedOutputStream
java code: