为什么使用多线程
使用多线程,可以把一些大任务分解成多个小任务来执行,多个小任务之间互不影像,同时进行,这样,充分利用了cpu资源。
实现多线程的两种方式
- 继承Thread类,重写run方法;
- 实现Runable接口,实现run方法;
由于JAVA只支持单继承,在继承了其他类的基础上再想实现多线程就只能用实现Runable接口了,所以更加推荐第二种方法
线程的状态
新建,就绪,运行,阻塞,死亡
线程池的作用
(1)降低资源消耗,可重复利用
(2)加快响应速度,不需要重复创建
(3)便于管理
spring 中的线程池
Spring中的线程池是由ThreadPoolTaskExecutor类来实现的
Java代码初始化:
private void test2(){
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(10);
executor.setMaxPoolSize(15);
executor.setKeepAliveSeconds(1);
executor.setQueueCapacity(5);
executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
executor.initialize();
executor.execute(new Runnable(){
@Override
public void run() {
//执行的代码
}
});
}
线程通信
通信方式
- 等待通知机制:两个线程通过对同一对象调用等待wait()和通知notify()方法来进行通信案例:两个线程交替打印奇偶数 深入理解线程通信
- join()方法:其实也是调用等待通知机制
- volatile共享内存:java采用共享内存的方式进行同信(也可以使用管道)
- 线程响应中断
- 线程池的awaitTermination() 方法
- 管道通信
volatile关键字
典型案例:i++和++i不是原子操作
作用
- 保证内存可见性
- 防止 JVM 进行指令重排优化
解释
cpu的速度往往与内存的速度很难匹配,所以在cpu和内存之间加入高速缓存(每个线程都有自己的高速缓存),在cpu没有命中高速缓存的时候才会去内存中取,在并发编程中就会出现高速缓存没有同步到内存中的情况。
用volatile修饰的变量会强制任何线程对此变量的操作都会立即刷新到主内存中,并且强制让缓存了此变量的栈清空,必须从主内存中获取数据,保证了一致性(注意,修饰之后并不是让线程从主内存中取,依然将变量拷贝到缓存区)。所以可以保证取出的值一定是最新的。
注意
volatile并不保证原子性,不保证线程安全性
更多请看:volatile关键字详解