1. 进程是资源分配的最小单位,线程是CPU调度的最小单位java
2. 一个进程由一个或多个线程组成mysql
3. 进程之间相互独立,每一个进程都有独立的代码和数据空间,但同一进程下的各个线程之间共享进程的代码和内存空间,每一个线程有独立的运行栈和程序计数器sql
4. 线程上下文切换比进程上下文切换要快得多数据库
在java中要想实现多线程,有两种手段,一种是继续Thread类(extends )缓存
另一种是实现Runable接口(implements ,须要先经过Thread类的构造方法Thread(Runnable target) 构造出对象)。安全
实现runnable接口的优点:服务器
适合于资源的共享多线程
能够避免java中的单继承的限制并发
增长程序的健壮性,代码能够被多个线程共享ide
新建状态(New):新建立了一个线程对象。
就绪状态(Runnable):线程对象建立后,其余线程调用了该对象的start()方法。变得可运行,等待获取CPU的使用权。
运行状态(Running):就绪状态的线程获取了CPU,执行程序代码。
阻塞状态(Blocked):阻塞状态是线程由于某种缘由放弃CPU使用权,暂时中止运行。
死亡状态(Dead):线程执行完了或者因异常退出了run()方法,该线程结束生命周期。
1 线程间有数据共享,而且数据是须要修改的(不一样任务间须要大量共享数据或频繁通讯时);
2 提供非均质的服务(有优先级任务处理)事件响应有优先级;
3 单任务并行计算,提升响应速度,下降时延;
4 与人有IO交互的应用,良好的用户体验(键盘鼠标的输入,马上响应)
1. 作WEB,主线程专门监听用户的HTTP请求,而后启动子线程去处理用户的HTTP请求。提升吞吐量
2.
某种任务,虽然耗时,可是不耗CPU的操做时,开启多个线程,效率会有显著提升。
好比读取文件,而后处理。 磁盘IO是个很耗费时间,可是不耗CPU计算的工做。 因此能够一个线程读取数据,一个线程处理数据。确定比
3. 数据库操做
产生缘由:
互斥条件:一个资源每次只能被一个进程使用。
不剥夺条件:进程已得到的资源,在末使用完以前,不能强行剥夺。
请求与保持条件:一个进程因请求资源而阻塞时,对已得到的资源保持不放。
循环等待条件:若干进程之间造成一种头尾相接的循环等待资源关系。
如何避免死锁:
加锁顺序(线程按照必定的顺序加锁,只有得到了从顺序上排在前面的锁以后,才能获取后面的锁。与解锁顺序无关)
加锁时限(线程尝试获取锁的时候加上必定的时限,超过期限则放弃对该锁的请求,并释放本身占有的锁,而后等待一段随机的时间再重试)
死锁检测
原子操做:由一组相关的操做完成,这些操做可能会操纵与其它的线程共享的资源,为了保证获得正确的运算结果,一个线程在执行原子操做其间,应该采起其余的措施使得其余的线程不能操纵共享资源。
1. sleep(long millis): 在指定的毫秒数内让当前正在执行的线程休眠,进入阻塞状态,不会释放锁
2. join():当前线程进入阻塞状态,等待加入线程终止后才能执行。
3. setPriority(): 更改线程的优先级。
4. setName(): 为线程设置一个名称。
5. interrupt():中断某个线程,这种结束方式比较粗暴,若是t线程打开了某个资源还没来得及关闭也就是run方法尚未执行完就强制结束线程,会致使资源没法关闭
6. wait()、Obj.wait()、Obj.notify()
必需要与synchronized(Obj)一块儿使用,也就是wait,与notify是针对已经获取了Obj锁进行操做,从语法角度来讲就是Obj.wait(),Obj.notify必须在synchronized(Obj){...}语句块内。从功能上来讲wait就是说线程在获取对象锁后,主动释放对象锁,同时本线程休眠。直到有其它线程调用对象的notify()唤醒该线程,才能继续获取对象锁,并继续执行。相应的notify()就是对对象锁的唤醒操做。但有一点须要注意的是notify()调用后,并非立刻就释放对象锁的,而是在相应的synchronized(){}语句块执行结束,自动释放锁后,JVM会在wait()对象锁的线程中随机选取一线程,赋予其对象锁,唤醒线程,继续执行。这样就提供了在线程间同步、唤醒的操做。Thread.sleep()与Object.wait()两者均可以暂停当前线程,释放CPU控制权,主要的区别在于Object.wait()在释放CPU同时,释放了对象锁的控制。
wait和sleep区别
sleep()睡眠时,保持对象锁,仍然占有该锁;是thread的方法
而wait()睡眠时,释放对象锁。是object的方法
线程安全:就是说多线程访问同一代码,不会产生不肯定的结果。
1. synchronized同步方法
即有synchronized关键字修饰的方法。因为java的每一个对象都有一个内置锁,当用此关键字修饰方法时,内置锁会保护整个方法。
也能够修饰静态方法,此时若是调用该静态方法,将会锁住整个类
2. synchronized同步代码块
即有synchronized关键字修饰的语句块。
当一个线程访问object的一个synchronized(this)同步代码块时,它就得到了这个object的对象锁。结果,其它线程对该object对象全部同步代码部分的访问都被暂时阻塞,但仍然能够访问该object中的非synchronized(this)同步代码块。
3. volatile实现线程同步
用volatile修饰的变量,线程在每次使用变量的时候,都会从主存中读取变量最新值。变量修改后会直接改变主存内容。保证可见性,不能保证原子性
4. 使用重入锁实现线程同步ReentrantLock
ReentrantLock 拥有Synchronized相同的并发性和内存语义,此外还多了 锁投票,定时锁等候和中断锁等候,好比能够放弃锁等待先作别的事情(trylock),而Synchronized不能
synchronized是在JVM层面上实现的,JVM会自动释放锁定,可是使用Lock则不行,lock是经过代码实现的,要保证锁定必定会被释放,就必须将unLock()放到finally{}中
在资源竞争很激烈的状况下,ReetrantLock的性能要优于Synchronized
5. 使用ThreadLocal管理变量
使用该变量的线程都得到该变量的副本,副本之间相互独立,这样每个线程均可以随意修改本身的变量副本,而不会对其余线程产生影响。
吞吐量:单位时间内成功地传送数据的数量
1. synchronied关键字wait()/notify()、notifyAll()机制:
2. 条件对象的等待/通知机制(await()、signal()、signalAll()):所谓的条件对象也就是配合前面咱们分析的Lock锁对象,经过锁对象的条件对象来实现等待/通知机制Condition conditionObj=ticketLock.newCondition()
3. 管道通讯
经过管道,将一个线程中的二进制数据消息发送给另外一个。
进程间通讯:
1.管道
匿名管道:管道是一种半双工的通讯方式,数据只能单向流动,并且只能在具备亲缘关系(父子)的进程间使用。
有名管道: 有名管道也是半双工的通讯方式,可是它容许无亲缘关系进程间的通讯。
2.信号量: 信号量是一个计数器,能够用来控制多个进程对共享资源的访问。
3.消息队列: 消息队列是由消息的链表,存放在内核中并由消息队列标识符标识。消息队列克服了信号传递信息少、管道只能承载无格式字节流
4.信号:用于通知接收进程某个事件已经发生。
5 .共享内存:共享内存是最快的一种 IPC,由于进程是直接对内存进行存取。共享内存就是一段能被其余进程所访问的内存,这段共享内存由一个进程建立,但多个进程均可以访问,信号量+共享内存一般结合在一块儿使用,来达到进程间的同步与互斥。
4.1 什么是线程池?
线程池是一个线程集合,而后在须要执行新的任务时重用这些线程而不是新建一个线程。线程池中的每一个线程都有被分配一个任务,一旦任务已经完成了,线程回到池子中并等待下一次分配任务。
4.2 好处
第一:下降资源消耗。经过重复利用已建立的线程下降线程建立和销毁形成的消耗。
第二:提升响应速度。当任务到达时,任务能够不须要的等到线程建立就能当即执行。
第三:提升线程的可管理性。
4.3 适用场合?
当一个Web服务器接受到大量短小线程的请求时,使用线程池技术是很是合适的,它能够大大减小线程的建立和销毁次数,提升服务器的工做效率。但若是线程要求的运行时间比较长,此时线程的运行时间比建立时间要长得多,单靠减小建立时间对系统效率的提升不明显,此时就不适合应用线程池技术,须要借助其它的技术来提升服务器的服务效率。
4.4 建立线程池四种方式
咱们能够经过Executors工具类的静态方法来建立线程池。
1. newFixedThreadPool(int nThreads)
建立一个固定长度的线程池,每当提交一个任务就建立一个线程,直到达到线程池的最大数量,这时线程规模将再也不变化
2. newCachedThreadPool()
建立一个可缓存的线程池,适当状况下可回收添加线程
3. newSingleThreadExecutor()
这是一个单线程的Executor
4. newScheduledThreadPool(int corePoolSize)
建立了一个固定长度的线程池,并且以延迟或定时的方式来执行任务,相似于Timer。
Executors 类使用 ExecutorService 提供了一个 ThreadPoolExecutor 的简单实现,但 ThreadPoolExecutor 提供的功能远不止这些。
ThreadPoolExecutor mExecutor = new ThreadPoolExecutor(
corePoolSize,// 核心线程数,当提交一个任务到线程池时,线程池会建立一个线程来执行任务,即便其余空闲的基本线程可以执行新任务也会建立线程,等到须要执行的任务数大于线程池基本大小时就再也不建立。
maximumPoolSize, // 最大线程数 ,线程池容许建立的最大线程数。若是队列满了,而且已建立的线程数小于最大线程数,则线程池会再建立新的线程执行任务。
keepAliveTime, // 线程活动保持时间,闲置线程存活时间 当线程空闲时间达到keepAliveTime,该线程会退出,直到线程数量等于corePoolSize。此时不会退出线程
TimeUnit.MILLISECONDS,// 时间单位,此处为毫秒
runnableTaskQueuenew ,// 任务队列,线程队列用于保存执行任务的阻塞队列
Executors.defaultThreadFactory(),// 线程工厂
RejectedExecutionHandler// 饱和策略,当队列和线程池都满了,说明线程池处于饱和状态,那么必须采起一种策略处理提交的新任务。这个策略默认状况下是AbortPolicy,表示没法处理新任务时抛出异常。
);
4.5 线程池的处理流程
1. 首先线程池判断基本线程池是否已满?没满,建立一个工做线程来执行任务。满了,则进入下个流程。
2. 其次线程池判断工做队列是否已满?没满,则将新提交的任务存储在工做队列里。满了,则进入下个流程。
3. 最后线程池判断整个线程池是否已满?没满,则建立一个新的工做线程来执行任务,满了,则交给饱和策略来处理这个任务。
4.6 线程池组成
线程池管理器(ThreadPool):用于建立并管理线程池,包括 建立线程池,销毁线程池,添加新任务;
工做线程(PoolWorker):线程池中线程,在没有任务时处于等待状态,能够循环的执行任务;
任务队列(taskQueue):用于存放没有处理的任务。提供一种缓冲机制。
任务接口(Task):每一个任务必须实现的接口,以供工做线程调度任务的执行,它主要规定了任务的入口,任务执行完后的收尾工做,任务的执行状态等;
4.7 合理的配置线程池
能够从如下几个角度来进行分析:
1. 任务的性质:CPU密集型任务、IO密集型任务 。
任务性质不一样的任务能够用不一样规模的线程池分开处理。CPU密集型任务配置尽量少的线程数量。IO密集型任务则因为须要等待IO操做,线程并非一直在执行任务,则配置尽量多的线程。
2. 任务的优先级:高,中和低。
优先级不一样的任务可使用优先级队列PriorityBlockingQueue来处理。(任务队列里的一种)
3. 任务的执行时间:长,中和短。
可使用优先级队列,让执行时间短的任务先执行。
4. 任务的依赖性:是否依赖其余系统资源,如数据库链接。
如依赖数据库链接池的任务,由于线程提交SQL后须要等待数据库返回结果,若是等待的时间越长CPU空闲时间就越长,那么线程数应该设置越大,这样才能更好的利用CPU。
public class MyThreadPrinter2 implements Runnable {
private String name;
private Object prev;
private Object self;
private MyThreadPrinter2(String name, Object prev, Object self) {
this.name = name;
this.prev = prev;
this.self = self;
}
@Override
public void run() {
int count = 10;
while (count > 0) {
synchronized (prev) {
synchronized (self) {
System.out.print(name);
count--;
self.notify();
}
try {
prev.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
public static void main(String[] args) throws Exception {
Object a = new Object();
Object b = new Object();
Object c = new Object();
MyThreadPrinter2 pa = new MyThreadPrinter2("A", c, a);
MyThreadPrinter2 pb = new MyThreadPrinter2("B", a, b);
MyThreadPrinter2 pc = new MyThreadPrinter2("C", b, c);
new Thread(pa).start();
Thread.sleep(100); //确保按顺序A、B、C执行
new Thread(pb).start();
Thread.sleep(100);
new Thread(pc).start();
Thread.sleep(100);
}
}
主要的思想就是,为了控制执行的顺序,必需要先持有prev锁,也就前一个线程要释放自身对象锁,再去申请自身对象锁,二者兼备时打印,以后首先调用self.notify()释放自身对象锁,唤醒下一个等待线程,再调用prev.wait()释放prev对象锁,等待下次获取prev锁后运行,终止当前线程,等待循环结束后再次被唤醒。
特征:
单例类只能有一个实例。
单例类必须本身建立本身的惟一实例。
单例类必须给全部其余对象提供这一实例。
懒汉式单例:
Public class Singleton {
Private Singleton (){};
Private static Singleton single = null;
Public static Singleton getInstance(){
If(Singleton == null)
Single = new Singleton ();
return single;
}
}
可是以上懒汉式单例的实现没有考虑线程安全问题,它是线程不安全的,并发环境下极可能出现多个Singleton实例,要实现线程安全,有如下三种方式
方法一:在getInstance方法上加同步
public static synchronized Singleton getInstance(){
If(Single == null)
Single = new Singleton ();
return single;
}
方法二 : 双检索
public static Singleton getInstance(){
If(single == null){
Synchronized(Singleton.class){
if(single == null)
Single = new Singleton ();
}
}
return single;
}
方法三:静态内部类
饿汉式单例:
Public class singleton{
Private singleton(){}
Private static final singleton single = new singleton();
Public static singleton getInstance(){
return single;
}
}
单生产者单消费者模式:
1. public class KaoYaResource {
2.
3. private String name;
4. private int count = 1;//烤鸭的初始数量
5. private boolean flag = false;//判断是否有须要线程等待的标志
6. public synchronized void product(String name){
7. if(flag){
8. //此时有烤鸭,等待
9. try {
10. this.wait();
11. } catch (InterruptedException e) {
12. e.printStackTrace();
13. }
14. }
15. this.name=name+count;//设置烤鸭的名称
16. count++;
17. System.out.println(Thread.currentThread().getName()+"...生产者..."+this.name);
18. flag=true;//有烤鸭后改变标志
19. notifyAll();//通知消费线程能够消费了
20. }
21. public synchronized void consume(){
22. if(!flag){//若是没有烤鸭就等待
23. try{this.wait();}catch(InterruptedException e){}
24. }
25. System.out.println(Thread.currentThread().getName()+"...消费者........"+this.name);//消费烤鸭1
26. flag = false;
27. notifyAll();//通知生产者生产烤鸭
28. }
29. }
30.
1. public class Single_Producer_Consumer {
2. public static void main(String[] args)
3. {
4. KaoYaResource r = new KaoYaResource();
5. Producer pro = new Producer(r);
6. Consumer con = new Consumer(r);
7. //生产者线程
8. Thread t0 = new Thread(pro);
9. //消费者线程
10. Thread t2 = new Thread(con);
11. //启动线程
12. t0.start();
13. t2.start();
14. }
15. }
16. class Producer implements Runnable
17. {
18. private KaoYaResource r;
19. Producer(KaoYaResource r)
20. {
21. this.r = r;
22. }
23. public void run()
24. {
25. while(true)
26. {
27. r.product("北京烤鸭");
28. }
29. }
30. }
31. class Consumer implements Runnable
32. {
33. private KaoYaResource r;
34. Consumer(KaoYaResource r)
35. {
36. this.r = r;
37. }
38. public void run()
39. {
40. while(true)
41. {
42. r.consume();
43. }
44. }
}
1. 进程是资源分配的最小单位,线程是CPU调度的最小单位
2. 一个进程由一个或多个线程组成
3. 进程之间相互独立,每一个进程都有独立的代码和数据空间,但同一进程下的各个线程之间共享进程的代码和内存空间,每一个线程有独立的运行栈和程序计数器
4. 线程上下文切换比进程上下文切换要快得多
在java中要想实现多线程,有两种手段,一种是继续Thread类(extends )
另一种是实现Runable接口(implements ,须要先经过Thread类的构造方法Thread(Runnable target) 构造出对象)。
实现runnable接口的优点:
适合于资源的共享
能够避免java中的单继承的限制
增长程序的健壮性,代码能够被多个线程共享
新建状态(New):新建立了一个线程对象。
就绪状态(Runnable):线程对象建立后,其余线程调用了该对象的start()方法。变得可运行,等待获取CPU的使用权。
运行状态(Running):就绪状态的线程获取了CPU,执行程序代码。
阻塞状态(Blocked):阻塞状态是线程由于某种缘由放弃CPU使用权,暂时中止运行。
死亡状态(Dead):线程执行完了或者因异常退出了run()方法,该线程结束生命周期。
1 线程间有数据共享,而且数据是须要修改的(不一样任务间须要大量共享数据或频繁通讯时);
2 提供非均质的服务(有优先级任务处理)事件响应有优先级;
3 单任务并行计算,提升响应速度,下降时延;
4 与人有IO交互的应用,良好的用户体验(键盘鼠标的输入,马上响应)
1. 作WEB,主线程专门监听用户的HTTP请求,而后启动子线程去处理用户的HTTP请求。提升吞吐量
2.
某种任务,虽然耗时,可是不耗CPU的操做时,开启多个线程,效率会有显著提升。
好比读取文件,而后处理。 磁盘IO是个很耗费时间,可是不耗CPU计算的工做。 因此能够一个线程读取数据,一个线程处理数据。确定比
3. 数据库操做
产生缘由:
互斥条件:一个资源每次只能被一个进程使用。
不剥夺条件:进程已得到的资源,在末使用完以前,不能强行剥夺。
请求与保持条件:一个进程因请求资源而阻塞时,对已得到的资源保持不放。
循环等待条件:若干进程之间造成一种头尾相接的循环等待资源关系。
如何避免死锁:
加锁顺序(线程按照必定的顺序加锁,只有得到了从顺序上排在前面的锁以后,才能获取后面的锁。与解锁顺序无关)
加锁时限(线程尝试获取锁的时候加上必定的时限,超过期限则放弃对该锁的请求,并释放本身占有的锁,而后等待一段随机的时间再重试)
死锁检测
原子操做:由一组相关的操做完成,这些操做可能会操纵与其它的线程共享的资源,为了保证获得正确的运算结果,一个线程在执行原子操做其间,应该采起其余的措施使得其余的线程不能操纵共享资源。
1. sleep(long millis): 在指定的毫秒数内让当前正在执行的线程休眠,进入阻塞状态,不会释放锁
2. join():当前线程进入阻塞状态,等待加入线程终止后才能执行。
3. setPriority(): 更改线程的优先级。
4. setName(): 为线程设置一个名称。
5. interrupt():中断某个线程,这种结束方式比较粗暴,若是t线程打开了某个资源还没来得及关闭也就是run方法尚未执行完就强制结束线程,会致使资源没法关闭
6. wait()、Obj.wait()、Obj.notify()
必需要与synchronized(Obj)一块儿使用,也就是wait,与notify是针对已经获取了Obj锁进行操做,从语法角度来讲就是Obj.wait(),Obj.notify必须在synchronized(Obj){...}语句块内。从功能上来讲wait就是说线程在获取对象锁后,主动释放对象锁,同时本线程休眠。直到有其它线程调用对象的notify()唤醒该线程,才能继续获取对象锁,并继续执行。相应的notify()就是对对象锁的唤醒操做。但有一点须要注意的是notify()调用后,并非立刻就释放对象锁的,而是在相应的synchronized(){}语句块执行结束,自动释放锁后,JVM会在wait()对象锁的线程中随机选取一线程,赋予其对象锁,唤醒线程,继续执行。这样就提供了在线程间同步、唤醒的操做。Thread.sleep()与Object.wait()两者均可以暂停当前线程,释放CPU控制权,主要的区别在于Object.wait()在释放CPU同时,释放了对象锁的控制。
wait和sleep区别
sleep()睡眠时,保持对象锁,仍然占有该锁;是thread的方法
而wait()睡眠时,释放对象锁。是object的方法
线程安全:就是说多线程访问同一代码,不会产生不肯定的结果。
1. synchronized同步方法
即有synchronized关键字修饰的方法。因为java的每一个对象都有一个内置锁,当用此关键字修饰方法时,内置锁会保护整个方法。
也能够修饰静态方法,此时若是调用该静态方法,将会锁住整个类
2. synchronized同步代码块
即有synchronized关键字修饰的语句块。
当一个线程访问object的一个synchronized(this)同步代码块时,它就得到了这个object的对象锁。结果,其它线程对该object对象全部同步代码部分的访问都被暂时阻塞,但仍然能够访问该object中的非synchronized(this)同步代码块。
3. volatile实现线程同步
用volatile修饰的变量,线程在每次使用变量的时候,都会从主存中读取变量最新值。变量修改后会直接改变主存内容。保证可见性,不能保证原子性
4. 使用重入锁实现线程同步ReentrantLock
ReentrantLock 拥有Synchronized相同的并发性和内存语义,此外还多了 锁投票,定时锁等候和中断锁等候,好比能够放弃锁等待先作别的事情(trylock),而Synchronized不能
synchronized是在JVM层面上实现的,JVM会自动释放锁定,可是使用Lock则不行,lock是经过代码实现的,要保证锁定必定会被释放,就必须将unLock()放到finally{}中
在资源竞争很激烈的状况下,ReetrantLock的性能要优于Synchronized
5. 使用ThreadLocal管理变量
使用该变量的线程都得到该变量的副本,副本之间相互独立,这样每个线程均可以随意修改本身的变量副本,而不会对其余线程产生影响。
吞吐量:单位时间内成功地传送数据的数量
1. synchronied关键字wait()/notify()、notifyAll()机制:
2. 条件对象的等待/通知机制:所谓的条件对象也就是配合前面咱们分析的Lock锁对象,经过锁对象的条件对象来实现等待/通知机制Condition conditionObj=ticketLock.newCondition()
3. 管道通讯
经过管道,将一个线程中的二进制数据消息发送给另外一个。
4.1 什么是线程池?
线程池是一个线程集合,而后在须要执行新的任务时重用这些线程而不是新建一个线程。线程池中的每一个线程都有被分配一个任务,一旦任务已经完成了,线程回到池子中并等待下一次分配任务。
4.2 好处
第一:下降资源消耗。经过重复利用已建立的线程下降线程建立和销毁形成的消耗。
第二:提升响应速度。当任务到达时,任务能够不须要的等到线程建立就能当即执行。
第三:提升线程的可管理性。
4.3 适用场合?
当一个Web服务器接受到大量短小线程的请求时,使用线程池技术是很是合适的,它能够大大减小线程的建立和销毁次数,提升服务器的工做效率。但若是线程要求的运行时间比较长,此时线程的运行时间比建立时间要长得多,单靠减小建立时间对系统效率的提升不明显,此时就不适合应用线程池技术,须要借助其它的技术来提升服务器的服务效率。
4.4 建立线程池四种方式
咱们能够经过Executors工具类的静态方法来建立线程池。
1. newFixedThreadPool(int nThreads)
建立一个固定长度的线程池,每当提交一个任务就建立一个线程,直到达到线程池的最大数量,这时线程规模将再也不变化
2. newCachedThreadPool()
建立一个可缓存的线程池,适当状况下可回收添加线程
3. newSingleThreadExecutor()
这是一个单线程的Executor
4. newScheduledThreadPool(int corePoolSize)
建立了一个固定长度的线程池,并且以延迟或定时的方式来执行任务,相似于Timer。
Executors 类使用 ExecutorService 提供了一个 ThreadPoolExecutor 的简单实现,但 ThreadPoolExecutor 提供的功能远不止这些。
ThreadPoolExecutor mExecutor = new ThreadPoolExecutor(
corePoolSize,// 核心线程数,当提交一个任务到线程池时,线程池会建立一个线程来执行任务,即便其余空闲的基本线程可以执行新任务也会建立线程,等到须要执行的任务数大于线程池基本大小时就再也不建立。
maximumPoolSize, // 最大线程数 ,线程池容许建立的最大线程数。若是队列满了,而且已建立的线程数小于最大线程数,则线程池会再建立新的线程执行任务。
keepAliveTime, // 线程活动保持时间,闲置线程存活时间 当线程空闲时间达到keepAliveTime,该线程会退出,直到线程数量等于corePoolSize。此时不会退出线程
TimeUnit.MILLISECONDS,// 时间单位,此处为毫秒
runnableTaskQueuenew ,// 任务队列,线程队列用于保存执行任务的阻塞队列
Executors.defaultThreadFactory(),// 线程工厂
RejectedExecutionHandler// 饱和策略,当队列和线程池都满了,说明线程池处于饱和状态,那么必须采起一种策略处理提交的新任务。这个策略默认状况下是AbortPolicy,表示没法处理新任务时抛出异常。
);
4.5 线程池的处理流程
1. 首先线程池判断基本线程池是否已满?没满,建立一个工做线程来执行任务。满了,则进入下个流程。
2. 其次线程池判断工做队列是否已满?没满,则将新提交的任务存储在工做队列里。满了,则进入下个流程。
3. 最后线程池判断整个线程池是否已满?没满,则建立一个新的工做线程来执行任务,满了,则交给饱和策略来处理这个任务。
4.6 线程池组成
线程池管理器(ThreadPool):用于建立并管理线程池,包括 建立线程池,销毁线程池,添加新任务;
工做线程(PoolWorker):线程池中线程,在没有任务时处于等待状态,能够循环的执行任务;
任务队列(taskQueue):用于存放没有处理的任务。提供一种缓冲机制。
任务接口(Task):每一个任务必须实现的接口,以供工做线程调度任务的执行,它主要规定了任务的入口,任务执行完后的收尾工做,任务的执行状态等;
4.7 合理的配置线程池
能够从如下几个角度来进行分析:
1. 任务的性质:CPU密集型任务、IO密集型任务 。
任务性质不一样的任务能够用不一样规模的线程池分开处理。CPU密集型任务配置尽量少的线程数量。IO密集型任务则因为须要等待IO操做,线程并非一直在执行任务,则配置尽量多的线程。
2. 任务的优先级:高,中和低。
优先级不一样的任务可使用优先级队列PriorityBlockingQueue来处理。(任务队列里的一种)
3. 任务的执行时间:长,中和短。
可使用优先级队列,让执行时间短的任务先执行。
4. 任务的依赖性:是否依赖其余系统资源,如数据库链接。
如依赖数据库链接池的任务,由于线程提交SQL后须要等待数据库返回结果,若是等待的时间越长CPU空闲时间就越长,那么线程数应该设置越大,这样才能更好的利用CPU。
public class MyThreadPrinter2 implements Runnable {
private String name;
private Object prev;
private Object self;
private MyThreadPrinter2(String name, Object prev, Object self) {
this.name = name;
this.prev = prev;
this.self = self;
}
@Override
public void run() {
int count = 10;
while (count > 0) {
synchronized (prev) {
synchronized (self) {
System.out.print(name);
count--;
self.notify();
}
try {
prev.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
public static void main(String[] args) throws Exception {
Object a = new Object();
Object b = new Object();
Object c = new Object();
MyThreadPrinter2 pa = new MyThreadPrinter2("A", c, a);
MyThreadPrinter2 pb = new MyThreadPrinter2("B", a, b);
MyThreadPrinter2 pc = new MyThreadPrinter2("C", b, c);
new Thread(pa).start();
Thread.sleep(100); //确保按顺序A、B、C执行
new Thread(pb).start();
Thread.sleep(100);
new Thread(pc).start();
Thread.sleep(100);
}
}
主要的思想就是,为了控制执行的顺序,必需要先持有prev锁,也就前一个线程要释放自身对象锁,再去申请自身对象锁,二者兼备时打印,以后首先调用self.notify()释放自身对象锁,唤醒下一个等待线程,再调用prev.wait()释放prev对象锁,等待下次获取prev锁后运行,终止当前线程,等待循环结束后再次被唤醒。
特征:
单例类只能有一个实例。
单例类必须本身建立本身的惟一实例。
单例类必须给全部其余对象提供这一实例。
懒汉式单例:
Public class Singleton {
Private Singleton (){};
Private static Singleton single = null;
Public static Singleton getInstance(){
If(Singleton == null)
Single = new Singleton ();
return single;
}
}
可是以上懒汉式单例的实现没有考虑线程安全问题,它是线程不安全的,并发环境下极可能出现多个Singleton实例,要实现线程安全,有如下三种方式
方法一:在getInstance方法上加同步
public static synchronized Singleton getInstance(){
If(Single == null)
Single = new Singleton ();
return single;
}
方法二 : 双检索
public static Singleton getInstance(){
If(single == null){
Synchronized(Singleton.class){
if(single == null)
Single = new Singleton ();
}
}
return single;
}
方法三:静态内部类
饿汉式单例:
Public class singleton{
Private singleton(){}
Private static final singleton single = new singleton();
Public static singleton getInstance(){
return single;
}
}
单生产者单消费者模式:
1. public class KaoYaResource {
2.
3. private String name;
4. private int count = 1;//烤鸭的初始数量
5. private boolean flag = false;//判断是否有须要线程等待的标志
6. public synchronized void product(String name){
7. if(flag){
8. //此时有烤鸭,等待
9. try {
10. this.wait();
11. } catch (InterruptedException e) {
12. e.printStackTrace();
13. }
14. }
15. this.name=name+count;//设置烤鸭的名称
16. count++;
17. System.out.println(Thread.currentThread().getName()+"...生产者..."+this.name);
18. flag=true;//有烤鸭后改变标志
19. notifyAll();//通知消费线程能够消费了
20. }
21. public synchronized void consume(){
22. if(!flag){//若是没有烤鸭就等待
23. try{this.wait();}catch(InterruptedException e){}
24. }
25. System.out.println(Thread.currentThread().getName()+"...消费者........"+this.name);//消费烤鸭1
26. flag = false;
27. notifyAll();//通知生产者生产烤鸭
28. }
29. }
30.
1. public class Single_Producer_Consumer {
2. public static void main(String[] args)
3. {
4. KaoYaResource r = new KaoYaResource();
5. Producer pro = new Producer(r);
6. Consumer con = new Consumer(r);
7. //生产者线程
8. Thread t0 = new Thread(pro);
9. //消费者线程
10. Thread t2 = new Thread(con);
11. //启动线程
12. t0.start();
13. t2.start();
14. }
15. }
16. class Producer implements Runnable
17. {
18. private KaoYaResource r;
19. Producer(KaoYaResource r)
20. {
21. this.r = r;
22. }
23. public void run()
24. {
25. while(true)
26. {
27. r.product("北京烤鸭");
28. }
29. }
30. }
31. class Consumer implements Runnable
32. {
33. private KaoYaResource r;
34. Consumer(KaoYaResource r)
35. {
36. this.r = r;
37. }
38. public void run()
39. {
40. while(true)
41. {
42. r.consume();
43. }
44. }
}