多线程
1) 使用场景
a)
生产者消费者问题
假设仓库(list或者阻塞队列)中只能存放一件产品
生产者(线程1)将产品放入(往队列push)仓库,消费者(线程2)将仓库中的产品取走(从队列pop)消费
如果仓库中没产品,生产者将产品放入仓库(notify),否则停止生产并等待(wait)
如果仓库中有产品,消费者将产品取走消费(notify),否则停止消费并等待(wait),直到仓库中再次放入产品
b)
web服务器本身,如servlet容器建立一个新的线程来处理http请求,多个请求同时到达有并发问题,如共享数据的读写
c)
后台任务: 向100万用户发送邮件,使用多线程分解大任务
d)
异步处理: 发微博,记录日志等
e)
计算密集型: 如有1000个文件要处理,每个耗时1分钟,可以用起50个线程来缩短时间
f)
io密集型,采用异步机制
g)
普通的集合类如list,map的并发读写有多线程问题可以用Collections.synchronized…
h)
后台的定时任务
2) 线程安全和同步
a)
在并发的情况之下,代码经过多线程使用,线程的调度顺序不影响任何结果,则线程安全
b)
同步是指通过人为的控制和调度,保证共享资源的多线程访问成为线程安全,保证结果的准确
c)
synchronized: 与wait,notify结合,典型场景生产者消费者问题
d)
volatile: 解决多线程中变量在内存中的可见性问题
每次针对该变量的操作都激发一次load and save
多线程的内存模型:main memory(主存)、working memory(线程栈)
在处理数据时,线程会把值从主存load到本地栈,完成操作后再save回去
e)
线程实例的yield()方法: 调用该方法的线程让出执行权,但不保证一定得到执行
f)
线程实例的join()方法
当前运行的线程会阻塞
调用该方法的线程会执行
执行完了才轮到当前运行的线程
比如在主线程中,新建了一个线程t1,t1调用join()方法后, 主线程会阻塞直到t1执行完才会继续执行
3) 创建线程: 继承Thread,实现Runnable接口,实现Callable接口
a)
Callable
ExecutorService e = Executors.newFixedThreadPool(3);
//submit方法有多重参数版本,及支持callable也能够支持runnable接口类型.
Future future = e.submit(new myCallable());
future.isDone() //return true,false 无阻塞
future.get() // return 返回值,阻塞直到该线程运行结束
4) 高级多线程控制
a)
ThreadLocal
线程局部变量是局限于线程内部的变量,属于线程自身所有,不在多个线程间共
任何线程局部变量一旦在工作完成后没有释放,Java 应用就存在内存泄露的风险
为每个使用该变量的线程提供了一个独立的变量副本,每个线程修改副本时不影响其它线程对象的副本
当多个线程访问 ThreadLocal 实例时,每个线程维护ThreadLocal 提供的独立的变量副本
例: 数据库链接(connection)被每一个线程独立的维护,互不影响
b)
原子类
AtomicInteger、AtomicBoolean
使用自己保证原子的操作,则等同于synchronized
与并发集合如ConcurrentHashMap类似, 类本身提供的方法都是线程安全的,等同于synchronized
c)
可重入锁: ReentrantLock
主要目的是和synchronized一样, 两者都是为了解决同步问题
提供多种加锁方案,lock 阻塞式,trylock无阻塞式,lockInterruptily可打断式
和Condition类配合使用
unlock(),要放在finally里面,以防止异常跳出了正常流程
可重入读写锁(读写锁的一个实现)
ReentrantReadWriteLock lock = new ReentrantReadWriteLock()
ReadLock r = lock.readLock();
WriteLock w = lock.writeLock();
两者都有lock,unlock方法。写写,写读互斥;读读不互斥。可以实现并发读的高效线程安全代码
d)
Condition
创建
Lock lock = new ReentrantLock();
Condition condition = lock.newCondition();
用await()代替wait(),用signal()代替notify(),用signalAll()代替notifyAll(),传统线程的通信方式,Condition都可以实现
Condition的强大之处在于它可以为多个线程间建立不同的Condition
final Condition push = lock.newCondition();//往队列添加的线程
final Condition pop = lock.newCondition();//从队列获取的线程
当消费者队列长度大于1时, 在同一个方法内push.await(),pop.signal()实现更细粒度的控制
e) 容器类
BlockingQueue: 阻塞队列,普通的队列可以在队列头添加元素和在队尾删除或取出元素,特别适用于先进先出策略的一些应用场景, BlockingQueue在队列的基础上添加了多线程协作的功能
ConcurrentHashMap,线程安全hashmap
f) 管理类: 用于管理线程,本身不是多线程的,如ThreadPoolExecutor
5) 线程安全的单例
public enum Singleton{
INSTANCE;
public void show(){
System.out.println("Singleton using Enum in Java");
}
}
Singleton.INSTANCE.show();