【线程】线程/多线程问题/线程池
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:
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:
相关文章推荐
- 浅谈C#中的单线程与多线程问题
- 多线程编程范例 --- 生成线程/线程池
- java线程研究---(7)Thread同步:多线程数据共用会产生问题
- 细说多线程(四)—— CLR线程池的工作者线程
- 【Java多线程与并发库】8.java5线程并发库之线程池的应用
- 关于CoreData和SQLite多线程访问时的线程安全问题
- 黑马程序员-多线程(线程的安全问题与锁的理解)
- 线程问题的核心: 怎么退出线程才是合适的----小话多线程(2)
- 线程、多线程与线程池
- java多线程之线程并发库的线程池
- 利用委托解决多线程中“不是此控件创建的线程”的问题
- 09_张孝祥_多线程_java5线程并发库的应用_线程池
- 【Java多线程】写入同一文件,自定义线程池与线程回收利用
- 【多线程】 Future Callable 使用(线程池线程执行完,执行主线程)
- 多线程补充、多线程问题及处理、线程的同步及相关内容
- 线程、多线程、线程池
- 线程、多线程与线程池总结
- [置顶] java多线程之多线程的线程安全问题
- iOS多线程全套:线程生命周期,多线程的四种解决方案,线程安全问题,GCD的使用,NSOperation的使用(下)
- Java多线程设计(三)线程安全问题