先看有问题的源码
package Sell; public class SellTicket implements Runnable { private int tickets = 100; private Object obj = new Object(); @Override public void run() { while (true) { if (tickets > 0) { //通过sleep()方法来模拟出票时间 try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName() + "正在出售第" + tickets + "张票"); tickets--; } } } }
package Sell; public class Text { public static void main(String[] args) { SellTicket sellTicket = new SellTicket(); Thread s1 = new Thread(sellTicket,"小王1"); Thread s2 = new Thread(sellTicket,"小王2"); Thread s3 = new Thread(sellTicket,"小王3"); s1.start(); s2.start(); s3.start(); } }
运行结果如下
可以看到三个人卖同一张票,还有票数应该是从大到小顺序卖的,少了些票,这是为什么呢?
这是因为线程线程执行的随机性导致的
先看在测试类中创建了多个线程,用
while (true) {//假设t1线程抢到CPU的执行权,进行下一步 if (tickets > 0) { //通过sleep()方法来模拟出票时间 try { Thread.sleep(100); //t1线程休息100毫秒,其他的线程有执行资格,会抢夺CPU的执行权 //t2线程抢到了CPU的执行权,t2线程就开始执行,执行到这里的时候,t2线程休息100毫秒 //t3线程抢到了CPU的执行权,t3线程就开始执行,执行到这里的时候,t3线程休息100毫秒 } catch (InterruptedException e) { e.printStackTrace(); } //假设线程按照顺序醒过来, //t1抢到CPU的执行权,在控制台输出:小王1正在出售第100张票 System.out.println(Thread.currentThread().getName() + "正在出售第" + tickets + "张票"); //t2抢到CPU的执行权,在控制台输出:小王2正在出售第100张票 //t3抢到CPU的执行权,在控制台输出:小王3正在出售第100张票 tickets--; //如果这三个线程还是按照顺序来,这里就执行了3次--的操作,也就是三个窗口拿到了同一张票,总票数100-3张出售的票,剩余97张票,同理后面出线重复票也是这样的 } }
如何解决此类安全问题 ?
安全问题出现必定有三个条件
有共享数据
同理解决问题也是从这三个条件入手,多线程环境和有共享数据,无法入手,再看源码中,也就只能从第三个条件入手
既然都是执行同一串代码,我们只需要将此刻只让一条线程来执行,让其他的线程执行不了,也就把它锁起来,等到执行完解开锁,让下个有执行权的线程进入在锁住。
Java提供三种方法用来解决此类安全问题
synchronized(任意对象) {
多条语句操作共享数据的代码
}
synchronized(任意对象):就相当于给代码加锁了,任意对象就可以看成是一把锁
解决代码示例
@Override public void run() { while (true) { synchronized (obj) { //t1进来后,就会把这段代码给锁起来 if (tickets > 0) { try { Thread.sleep(100); //t1休息100毫秒 } catch (InterruptedException e) { e.printStackTrace(); } //小王1正在出售第100张票 System.out.println(Thread.currentThread().getName() + "正在出售第" + tickets + "张票"); tickets--; //tickets = 99; } } //t1出来了,这段代码的锁就被释放了 } }
同步方法:就是把synchronized关键字加到方法上
修饰符 synchronized 返回值类型 方法名(方法参数) {
方法体;
}
同步方法的锁对象是什么呢?
this
静态同步方法
同步静态方法:就是把synchronized关键字加到静态方法上
修饰符 static synchronized 返回值类型 方法名(方法参数) {
方法体;
}
同步静态方法的锁对象是什么呢?
@Override public void run() { while (true) { sellTicket(); } } // 静态同步方法 private static synchronized void sellTicket() { if (tickets > 0) { try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName() + "正在出售第" + tickets + "张票"); tickets--; } }
方法名 | 说明 |
---|---|
ReentrantLock() | 创建一个ReentrantLock的实例 |
加锁解锁方法
方法名 | 说明 |
---|---|
void lock() | 获得锁 |
void unlock() |
@Override public void run() { while (true) { try { lock.lock(); if (tickets > 0) { try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName() + "正在出售第" + tickets + "张票"); tickets--; } } finally { lock.unlock(); } } }
原文:https://www.cnblogs.com/521521cm/p/14504958.html