java允许多线程并发控制,当多个线程同时操作一个可共享的资源变量时(如数据的增删改查),
将会导致数据不准确,相互之间产生冲突,因此加入同步锁以避免在该线程没有完成操作之前,被其他线程的调用,
从而保证了该变量的唯一性和准确性。
出现一票多卖的情况.开三个线程模拟三个窗口卖票.
class TicketWindows implements Runnable {
private int tickets = 20;// 总票数
public void run() {
while (true) {
// if else 要保证其原子行 同步代码块
if (tickets > 0) {
// 显示售票信息
System.out.println(Thread.currentThread().getName() + "正在售票中 ....第 " + tickets + "张票");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
tickets--;
System.out.println("票还余: " + tickets + "张 ...");
} else {
// 退出售票窗口
break;
}
// 判断是否还有票
}
}
public static class TestTickets {
public static void main(String[] args) {
TicketWindows tWindows1 = new TicketWindows();
Thread thread1 = new Thread(tWindows1);
Thread thread2 = new Thread(tWindows1);
Thread thread3 = new Thread(tWindows1);
thread1.start();
thread2.start();
thread3.start();
}
}
}
输出结果为:
Thread-0正在售票中 ....第 20张票
Thread-2正在售票中 ....第 20张票
Thread-1正在售票中 ....第 20张票
票还余: 19张 ...
Thread-0正在售票中 ....第 19张票
票还余: 17张 ...
Thread-1正在售票中 ....第 17张票
票还余: 17张 ...
Thread-2正在售票中 ....第 17张票
票还余: 16张 ...
Thread-0正在售票中 ....第 16张票
票还余: 15张 ...
Thread-1正在售票中 ....第 15张票
票还余: 14张 ...
Thread-2正在售票中 ....第 14张票
票还余: 13张 ...
Thread-0正在售票中 ....第 13张票
票还余: 12张 ...
Thread-1正在售票中 ....第 12张票
票还余: 11张 ...
Thread-2正在售票中 ....第 11张票
票还余: 10张 ...
Thread-0正在售票中 ....第 10张票
票还余: 9张 ...
Thread-1正在售票中 ....第 9张票
票还余: 8张 ...
Thread-2正在售票中 ....第 8张票
票还余: 7张 ...
Thread-0正在售票中 ....第 7张票
票还余: 6张 ...
Thread-1正在售票中 ....第 6张票
票还余: 5张 ...
Thread-2正在售票中 ....第 5张票
票还余: 4张 ...
Thread-0正在售票中 ....第 4张票
票还余: 3张 ...
Thread-1正在售票中 ....第 3张票
票还余: 2张 ...
Thread-2正在售票中 ....第 2张票
票还余: 1张 ...
Thread-0正在售票中 ....第 1张票
票还余: 0张 ...
票还余: -1张 ...
票还余: -2张 ...
可以看出一票被多个窗口售卖,并且还出先卖负数票的情况.是因为:前一个线程进入run()后开始执行售卖操作,票数减一,操作还没结束,另一个线程也进来了,此时的票数其实已经减过了(负数),后来的线程之后执行到输出语句的时候才知道是负数.
所以,就需要在一个线程进入之后锁住这一方法,等操作结束另一线程才能进入.这一线程操作的时候,其他线程只能等待.
只粘出修改的代码,其实就是加上一个同步代码块(同步锁的一种,还有同步代码块方法)
注: synchronized关键字也可以修饰静态方法,此时如果调用该静态方法,将会锁住整个类
public void run() {
while (true) {
synchronized (this) {
// if else 要保证其原子行 同步代码块
if (tickets > 0) {
// 显示售票信息
System.out.println(Thread.currentThread().getName() + "正在售票中 ....第 " + tickets + "张票");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
tickets--;
System.out.println("票还余: " + tickets + "张 ...");
} else {
// 退出售票窗口
break;
}
}
}
}
此时的输出结果为:
Thread-0正在售票中 ....第 20张票
票还余: 19张 ...
Thread-0正在售票中 ....第 19张票
票还余: 18张 ...
Thread-2正在售票中 ....第 18张票
票还余: 17张 ...
Thread-2正在售票中 ....第 17张票
票还余: 16张 ...
Thread-2正在售票中 ....第 16张票
票还余: 15张 ...
Thread-2正在售票中 ....第 15张票
票还余: 14张 ...
Thread-2正在售票中 ....第 14张票
票还余: 13张 ...
Thread-2正在售票中 ....第 13张票
票还余: 12张 ...
Thread-1正在售票中 ....第 12张票
票还余: 11张 ...
Thread-1正在售票中 ....第 11张票
票还余: 10张 ...
Thread-2正在售票中 ....第 10张票
票还余: 9张 ...
Thread-2正在售票中 ....第 9张票
票还余: 8张 ...
Thread-2正在售票中 ....第 8张票
票还余: 7张 ...
Thread-0正在售票中 ....第 7张票
票还余: 6张 ...
Thread-0正在售票中 ....第 6张票
票还余: 5张 ...
Thread-0正在售票中 ....第 5张票
票还余: 4张 ...
Thread-2正在售票中 ....第 4张票
票还余: 3张 ...
Thread-2正在售票中 ....第 3张票
票还余: 2张 ...
Thread-1正在售票中 ....第 2张票
票还余: 1张 ...
Thread-1正在售票中 ....第 1张票
票还余: 0张 ...
此时的结果才是正常的情况!
public synchronized void addMoney(int money){ }
synchronized (this) { }
public void addMoney(int money) {
lock.lock();//上锁
try{
count += money;
System.out.println(System.currentTimeMillis() + "存进:" + money);
}finally{
lock.unlock();//解锁
}
}