您的位置:首页 > 其它

【线程】线程/多线程问题/线程池

2017-02-11 15:26 204 查看
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:
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息