1.volatile
这个关键字修饰的变量,是告诉jvm,在寄存器中的值是不确定的,使用前,需要先从主内存中拿
这个关键字的主要作用是:保证其可见性+禁止重排序
2.多线程通讯里面,在使用notify和wait的时候,锁一定要是加的同一个obj哦
而且这2个方法一定要放在synchronized里面执行
wait和join的区别,目前只知道join是无需唤醒的
而sleep是不会被唤醒的
3.并发队列
这里有2种并发队列:
ConcurrentLinkedQueue 非阻塞队列
BlockingQueue 阻塞队列
一般情况下都是使用第二种阻塞队列
4.队列
队列就是若干个tasks,这些task按照先进先出的原则,排队等待线程进行执行
concurrentLinkQueue.poll等一些队列方法,暂时先不看,
5.模式
生产者+消费者
生产者把很多task添加到队列中
消费者把task从队列中拿出来
也就是入列&出列
6.线程池
线程池的使用优点:
1.降低资源消耗,反复的创建线程是很耗资源的
2.提高程序效率,多线程会提高程序效率
3.方便管理,把线程集中起来一起管理
7.四种线程
newCachedThreadPool 可复用的缓存线程池
newFixedThreadPool 固定线程数量的线程池
newScheduledThreadPool 定时有关的线程池
newSingleThreadExecutor 单线程线程池,不知道有啥卵用...
对于线程池的操作,submit和execute的主要区别:
submit有返回值,execute没有
submit方便Exception处理,可以捕获异常
8.线程池实现原理
上面这4个线程,全部都用到了同一个构造方法:
其中参数,主要注意2点:
corePoolSize: 核心线程数
maximumPoolSize: 最大线程数
keepAliveTime: 存活时间(这个暂时不用纠结)
queue: 队列
我们这里主要讲一下队列:
是这样的,不管是什么线程池,开启了一个核心线程数coreNum和最大线程数maximumNum
你传进去的queue是有队列数queueNum
假如你现在往里面加入的:
taskNum = coreNum;就只有一个core在执行
coreNum < taskNum <= ( queueNum + coreNum );这时候还是只有core线程在跑,没有执行的tasks在排队
( queueNum + coreNum ) < taskNum <= ( queueNum + maximumNum);这个时候,队伍满了,线程池会开辟新的线程来跑
( queueNum + maximumNum) < taskNum;这个时候,task数量已经超过了队列数+最大线程数,那么就会被线程池拒绝而抛异常
以上就是线程池的原理,很简单.
9.合理配置线程数
这里分为:
IO密集:如果有很多IO操作,那么这里最好多搞点线程,避免阻塞,IO一般都是耗时操:
线程数一般配2*cpu核数(这里是最大线程数);
CPU密集:CPU密集就是没有大量IO操作,但是会用到很多多线程的时候
配置线程数=CPU核数
根据具体情况来设置maximum线程数