Java并发总结

描述一下线程池ThreadPoolExecutor

corepoolsize:核心池的大小,默认情况下,在创建了线程池之后,线程池中线程数为0,
当有任务来之后,就会创建一个线程去执行任务,当线程池中线程数达到corepoolsize 后,
就把任务放在任务缓存队列中。
Maximumpoolsize:线程池中最多创建多少个线程。
Keeplivetime:线程没有任务执行时,最多保存多久的时间会终止,默认情况下,当线程
池中线程数>corepoolsize 时,Keeplivetime 才起作用,直到线程数不大于corepoolsize。
workQueue:阻塞队列,用来存放等待被执行的任务
threadFactory:线程工厂,用来创建线程。

继承关系
Executor 接口
ExecutoServicer 接口
AbstractExecutoServicer 抽象类
ThreadPoolExecutor

线程池的状态
1.当线程池创建后,初始为running 状态
2.调用shutdown 方法后,处shutdown 状态,此时不再接受新的任务,等待已有的任务执
行完毕
3.调用shutdownnow 方法后,进入stop 状态,不再接受新的任务,并且会尝试终止正在
执行的任务。
4.当处于shotdown 或stop 状态,并且所有工作线程已经销毁,任务缓存队列已清空,线
程池被设为terminated 状态

当有任务提交到线程池之后的一些操作:
1.若当前线程池中线程数<corepoolsize,则每来一个任务就创建一个线程去执行。

  1. 若当前线程池中线程数>=corepoolsize,会尝试将任务添加到任务缓存队列中去,若添
    加成功,则任务会等待空闲线程将其取出执行,若添加失败,则尝试创建线程去执行这个任
    务。
  2. 若当前线程池中线程数>= Maximumpoolsize,则采取拒绝策略(有4 种,1)abortpolicy 丢
    弃任务,抛出RejectedExecutionException 2 )discardpolicy 拒绝执行,不抛异常3 )
    discardoldestpolicy 丢弃任务缓存队列中最老的任务,并且尝试重新提交新的任务4)
    callerrunspolicy 有反馈机制,使任务提交的速度变慢)。

什么是死锁,手撕代码模拟

如哲学家进餐问题,每个哲学家都在等右边哲学家方上筷子,处于僵持等待状态,称为死锁。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
public class LockTest implements Runnable{
public int flag=1;
static Object o1=new Object(),o2=new Object();
@Override
public void run() {
System.out.println("flag="+flag);
if(flag==1){
synchronized (o1) {
try {
Thread.sleep(5000);
} catch (Exception e) {
e.printStackTrace();
}
synchronized (o2) {
System.out.println("1");
}
}
}
if(flag==0){
synchronized (o2) {
try {
Thread.sleep(5000);
} catch (Exception e) {
e.printStackTrace();
}
synchronized (o1) {
System.out.println("0");
}
}
}
}
public static void main(String[] args) {
LockTest lt1=new LockTest();
LockTest lt2=new LockTest();
lt1.flag=1;
lt2.flag=0;
Thread t1=new Thread(lt1);
Thread t2=new Thread(lt2);
t1.start();
t2.start();
}
}

手撕模拟生产者

模拟一个生产者利用生产食物放到篮子里,而一个消费者从篮子里消费食物

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
public class Food { //食物类
int id;
public Food(int id){
this.id=id;
}
}
public class UtilStack {//工具,用来放食物
int index=0; //食物计数变量
Food[] arrFD=new Food[4]; //最多工具能放食物容量
public synchronized void push(Food fd){ //生产食物,必须加锁,保证生产过程不被打断
while(index==arrFD.length)
{
try {
System.out.println("full");
this.wait(); //生产满了,就去休息区休息,等待被唤醒
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
this.notify();//唤醒当前在休息区的线程
arrFD[index]=fd;
index++;
}
public synchronized Food pop(){ //消费
while(index==0) //消费完了,进休息区
try {
System.out.println("empty");
this.wait();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
this.notify();
index--;
return arrFD[index];
}
}
public class Producer implements Runnable{//生产者
UtilStack ss=null;
Producer(UtilStack ss){
this.ss=ss;
}
@Override
public void run() {
for(int i=0;i<20;i++)
{
Food fd=new Food(i);
ss.push(fd);
System.out.println("生产了"+fd.id);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
public class Consumer implements Runnable{//消费者
UtilStack ss=null;
Consumer(UtilStack ss){
this.ss=ss;
}
@Override
public void run() {
for(int i=0;i<20;i++)
{
Food fd=ss.pop();
System.out.println("消费了"+fd.id);
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}

线程的状态是什么

在任意一个时间点,一个线程只能有且只有其中的一种状态
1)新建:创建后尚未启动的线程处于这种状态。
2)运行:包括了OS 中Running 和Ready 状态,也就是处于次状态的线程可能正在运行,
也可能正在等待cpu 为他分配执行时间。
3)无限期等待:处于这种状态的线程不会被分配cpu 执行时间,要等待其他线程显示唤
醒。以下方法会让线程进入无限期等待:1.没有设置timeout 的object.wait()方法2.没有设
置timeout 参数的Thread.join()方法3.LockSupport.park();
4)有限期的等待:处于这种状态的线程也不会被分配cpu 执行时间,不过无需等待被
其他线程显示唤醒,而是在一定时间后,他们会由os 自动唤醒1.设置了timeout 的
object.wait() 方法2. 设置了timeout 参数的Thread.join() 3.LockSupport.parkNanos()
4.LockSupport.parkUnit()
5)阻塞:线程被阻塞了与“等待状态”的区别是:阻塞状态在等待获取一个排它锁,
这个事件将在另外一个线程放弃这个锁的时候发生。等待状态在等待一段时间或者唤醒动
作。
6)结束:已终止线程的线程状态,线程已经结束执行

synchronized(S)VS lock(L)

1)L 是接口,S 是关键字
2)S 在发生异常时,会自动释放线程占有的锁,不会发生死锁。L 在发生异常时,若没
有主动通过unlock()释放锁,则很有可能造成死锁。所以用lock 时要在finally 中释放锁。
3)L 可以当等待锁的线程响应中断,而S 不行,使用S 时,等待的线程将会一直等下去,
不能响应中断。
4)通过L 可以知道是否成功获得锁,S 不可以。
5)L 可以提高多个线程进行读写操作的效率。

简单并发中方法总结

继承thread或者Runnable,调用run方法,start启动
sleep:暂时停止线程当前的操作
wait-把线程放入休息室
notify方法-从休息区拿出线程
notifyAll从线程区中拿出所有线程
Lock lock=new ReentrantLock();读写锁
ReadWriteLock rwLock=new ReentrantReadWriteLock();读-读不互斥,读-写互斥,写-写互斥,并发性提高
CountDownLatch: 倒数计时器
ReenTrantLock :公平锁:线程先来,先得到锁,可在构造函数中设置
join方法。该方法主要作用是在该线程中的run方法结束后,才往下执行
yield方法。这个是线程本身的调度方法,使用时你可以在run方法执行完毕时,调用该方法,告知你已可以出让内存资源。
关键字volatile 是轻量级的同步机制。
Volatile 变量对于all 线程的可见性,指当一条线程修改了这个变量的值,新值对于其他
线程来说是可见的、立即得知的。

什么是同步与公平锁

同步:多个线程并发访问共享数据时,保证共享数据在同一个时刻,只被一个(or 一
些,使用信号量)线程使用,而互斥是实现同步的一种手段,临界区,互斥量,信号量都是
主要的互斥实现方式。互斥是因,同步是果;互斥是方法,同步是目的

公平锁:(先申请先得到)多个线程在等待同一个锁时,必须按照申请锁的时间顺序
来依次获取,而非公平锁则不保证这一点。Synchronized 不是公平锁,reetrantlock 默认下也
是非公平的,但是在构造函数中,可以设置为公平的。

描述一下java自带的锁机制

在java 设计中,每一个对象自打娘胎里出来就带了一把看不见的锁,即monitor 锁,有一张记录表记录线程锁住状况,
锁主要有4 中状态:无锁状态、偏向状态、轻量级状态、重量级状态。他们会随着竞争
的激烈而逐渐升级,锁可以升级但不可以降级
轻量级锁:传统的锁是重量级锁,他是用系统的互斥量来实现。轻量级锁的本意是在没
有多线程竞争的前提下,减少传统的重量级锁使用操作系统互斥量产生的性能消耗
Synchronized 用的锁时存在java 对象头里。对象头包含标记字段和类型指针
JVM 可以通过对象的元数据信息确定对象的大小,但是无法从数组的元数据来确定数组
的大小

乐观锁VS 悲观锁

悲观锁:就是很悲观,每次去拿数据的时候都认为别人会修改,
所以每次在拿数据的时候都会上锁。这样别人想拿这个数据就会block 直到它拿到锁。传统
的关系型数据库就用到了很多这种机制,比如行锁,写锁等,都是在操作之前上锁
2)乐观锁:就是很乐观,每次去拿数据的时候都认为别人不会修改,所以不会上锁,但是在更新
的时候会判断一下在此期间别人有没有去更新这个数据。适用于多读

解释一下同步阻塞,异步等

同步阻塞,用户空间的应用程序执行一个系统调用,这意味着应用程序会一直阻塞,直
到系统调用完成为止(数据传输完成或者发生错误)
同步非阻塞,设备以非阻塞形式打开,这意味着io 操作不会立刻完成,需要应用程序
调用多次来等待完成
同步和异步1)同步:发出一个调用时,在没有得到结果前,该调用就不返回,一旦返
回就有结果。2)异步:调用在发出之后就直接返回,所以没有返回结果,换句话说,当一
个异步调用发生后,调用者不会立即得到结果,而是在调用发生后,被调用者通过状态通知
来通知调用者,或者通过回调函数来处理这个调用
阻塞和非阻塞1)阻塞:调用结果返回之前,当前线程会被挂起,调用线程只有在得到
结果之后才会返回。2)非阻塞:不能立刻得到结果之前,该调用不会阻塞当前线程

BIO:同步并阻塞,一个连接一个线程,适用于链接数量小且固定的架构。
NIO:同步非阻塞:一个请求一个线程,客户端发送的链接请求都会注册到多路复用器
上,多路复用器轮训到链接有io 请求时才启动一个线程进行处理,适用于链接比较多,比
较短
AIO:异步非阻塞,一个有效请求一个线程,适用于链接数目多且长。

热评文章