多线程操作共享的数据,例如买票问题,可能会出现相同的票,或者负数的票。出现这种问题的条件
1. 多线程环境
2. 多线程之间共享数据
3. 对共享数据的操作不是原子性的(原子性:不可分割)
多线程操作同一数据源,对数据的操作中断了(线程的时间片结束),另一个线程进来了
public void run() {
// 卖同一张票的情况?
// 卖第 0 张 第 -1 张票的情况?
while (true) {
if (count >= 1) {
// t3 在这里时间片到了,返回就绪状态
System.out.println(Thread.currentThread().getName() + "售出第 " + count + " 张票");
try {
Thread.sleep(100); // t1 t2 走到这里,
} catch (InterruptedException e) {
e.printStackTrace();
}
count--; // coumt-- count++ 它不是原子性操作 count = count - 1
}
}
}
解决办法-将对共享数据的非原子性操作“变成”原子性操作
05-2-1线程同步方式
- 同步代码块
- 同步方法
- Lock锁机制
05-2.1线程同步问题操作
-
同步代码块
格式
synchronized(锁对象){
需要同步的代码
}
同步锁:
1. 代码块中的锁对象可以是任意对象
2. 但是必须保证多个线程使用的锁对象是同一个
* 锁对象作用:只让一个线程在同步代码块中执行
创建的锁对象,必须在run方法的外部,使得唯一,如果在run方法内部,那么各个线程执行run方法的时候,都会创建将要使用的锁对象,锁都一不一样,没用。原理,是到了同步方法,会检查是否有锁,有的话,就拿锁进去,直到执行完才归还。当另外线程执行到同步方法,检查,没有锁,阻塞。
-
同步方法
格式
public synchroniczed void method{
可能会产生线程安全问题的代码
}
同步方法会把方法内部的代码锁住,只让一个线程执行
同步方法的锁对象:
也就是实现类对象(new RunnableImpl()
),也就是this
(代表当前对象)
静态代码块对象:本类的class对象-->class文件对象(类名.class)
-
Lock锁
Lock
接口中方法,实现比Synchronized
方法和语句可获得更广泛的锁的操作
Lock
接口的方法;
void lock
获取锁
void unLock
释放锁
ReentrantLock implements Lock
接口
使用步骤:- 在成员位置创建一个
ReentrantLock
对象 - 在可能出现安全问题的代码前调用Lock接口中的方法lock获取锁
- 在可能出现安全问题的代码后调用Lock接口中的方法unlock释放锁
- 在成员位置创建一个
l.lock();
线程安全的代码
finally{
//无论程序是够异常,都把锁释放
l.unLock();
}