线程并发小知识
1、start()和run()的区别
线程阶段:创建(start),就绪,运行(run),阻塞,死亡
在调用start()时 里面实现了run方法,它是开启了一个新的线程,这才是实现了多线程。如果方法中直接调用run,则只在原来线程中跑,不会开启新的线程,所以只调用run没有效果
2、
CountDownLatch是通过一个计数器来实现的,计数器的初始值为线程的数量。每当一个线程完成了自己的任务后,计数器的值就会减1。当计数器值到达0时,它表示所有的线程已经完成了任务
CountDownLatch(
int
count)
就是闭锁需要等待的线程数量。
- static class Worker extends Thread{
- String workerName;
- int workTime;
- CountDownLatch latch;
- public Worker(String workerName ,int workTime ,CountDownLatch latch){
- this.workerName=workerName;
- this.workTime=workTime;
- this.latch=latch;
- }
- public void run(){
- doWork();//工作了
- latch.countDown();//工人完成工作,计数器减一
- }
- }
- public static void main(String[] args) throws InterruptedException {
- CountDownLatch latch=new CountDownLatch(2);//两个工人的协作
- Worker worker1=new Worker("zhang san", 5000, latch);
- Worker worker2=new Worker("li si", 8000, latch);
- worker1.start();//
- worker2.start();//
- latch.await();//等待所有工人完成工作
- }
3、volatile关键字
使用volatile关键字会强制将修改的值立即写入主存,所有线程对其都可见
声明变量是 volatile 的,JVM 保证了每次读变量都从内存中读,跳过 CPU cache 这一步。
4、ThreadLocal
在线程中将变量设置为ThreadLocal,为了防止多线程之间的数据共享,将数据隔离,解决线程安全问题
5、wait,notify,notifyAll都是object的方法,必须要在同步块中
- 如果对象调用了wait方法就会使持有该对象的线程把该对象的控制权交出去,然后处于等待状态。
- 如果对象调用了notify方法就会通知某个正在等待这个对象的控制权的线程可以继续运行。
- 如果对象调用了notifyAll方法就会通知所有等待这个对象控制权的线程继续运行。
6、线程池
优势:如果每次启动线程都是自己启动,很消耗性能,每个线程都需要给栈分配一些内存。
我们可以把并发执行的任务传递给一个线程池,来替代为每个并发执行的任务都启动一个新的线程
Java通过Executors提供四种线程池,分别为:
newCachedThreadPool创建一个可缓存线程池,如果线程池长度超过处理需要,可灵活回收空闲线程,若无可回收,则新建线程。
newFixedThreadPool 创建一个定长线程池,可控制线程最大并发数,超出的线程会在队列中等待。
newScheduledThreadPool 创建一个定长线程池,支持定时及周期性任务执行。
newSingleThreadExecutor 创建一个单线程化的线程池,它只会用唯一的工作线程来执行任务,保证所有任务按照指定顺序(FIFO, LIFO, 优先级)执行。