同步
多个线程同时修改一个数据时(也叫并发操作),容易导致同步问题(同时修改),产生脏数据
解决方法
在多个线程同时修改的数据上加锁,当一个线程修改时,另外的线程排队等待,直到释放锁后其它线程才能占用,避免同时修改问题
MyStack实例代码
死锁
- 线程1占用A,接着请求占用B
- 线程2占用B,接着请求占用A
- 就这样,两个线程占用各自的,请求对方的,就形成死锁
来自 how2j.cn
解决方法
- synchronized必须通过谨慎和良好的设计,才能减少死锁的发生。
- Lock可以选择性的获取锁,trylock()会在指定时间范围内试图占用,没有占用成功会继续往下走
// Lock 核心代码
boolean locked = lock.tryLock(1,TimeUnit.SECONDS);
if(locked){
// 申请占用成功操作
}else{
// 没有占用成功操作
}
交互
所谓交互,就是线程之间的交互通知
- syschronized 交互:wait(), notify(), notifyAll()
- Lock 交互:await(), signal(), signalAll()
生产者消费者问题
生产者消费者问题是一个非常典型性的线程交互的问题。它们公有一个缓冲区,该问题的关键就是要保证生产者不会在缓冲区满时加入数据,消费者也不会在缓冲区中空时消耗数据。
实现方法
- 使用栈来存放数据
- 把栈改造为支持线程安全
- 把栈的边界操作进行处理,当栈里的数据是0的时候,访问pull的线程就会等待。 当栈里的数据时200的时候,访问push的线程就会等待
- 提供一个生产者(Producer)线程类,生产随机大写字符压入到堆栈
- 提供一个消费者(Consumer)线程类,从堆栈中弹出字符并打印到控制台
- 提供一个测试类,使两个生产者和三个消费者线程同时运行
代码
线程池
使用原因
每个线程的启动和结束是比较消耗时间和占用内存的(每个线程需要1MB内存,线程开的越多,消耗的内存越大,最后会死机),为了解决这个问题,所以引入线程池的概念
设计原理
线程池的思路与消费者生产者模型很相似,主要两函数,任务添加 和 运行任务
- 准备一个任务容器
- 一次性启动10个(根据需要)消费线程
- 刚开始任务容器是空的,所以线程都是 wait()
- 直到外部环境往任务容器里放进“任务”,就会有消费线程被唤醒notify()
来自 how2j.cn
实例代码
区别
生产者消费者和线程池实现的区别在于:前一个是手动消费,后一个是自动消费