线程安全问题
/* 需求: 模拟3个窗口同时在售50张 票 。 问题1 :为什么50张票被卖出了150次? 出现 的原因: 因为num是非静态的,非静态的成员变量数据是在每个对象中都会维护一份数据的,三个线程对象就会有三份。 解决方案:把num票数共享出来给三个线程对象使用。使用static修饰。 问题2: 出现了线程安全问题 ? 线程 安全问题的解决方案:sun提供了线程同步机制让我们解决这类问题的。 java线程同步机制的方式: 方式一:同步代码块 同步代码块的格式: synchronized(锁对象){ 需要被同步的代码... } 同步代码块要注意事项: 1. 任意的一个对象都可以做为锁对象。 2. 在同步代码块中调用了sleep方法并不是释放锁对象的。 3. 只有真正存在线程安全问题的时候才使用同步代码块,否则会降低效率的。 4. 多线程操作的锁 对象必须是 唯一共享的。否则无效。
synchronized (new Object()) { } 无效
synchronized ("锁") { } 有效,
"锁"被创建后,不会再被创建了。
5. 一个线程在同步代码块中sleep了,并不会释放锁对象。
6. 如果不存在着线程安全问题,千万不要使用同步代码块,因为会降低效率。
出现线程安全问题的根本原因: 1. 存在两个或者两个以上 的线程对象,而且线程之间共享着一个资源。 2. 有多个语句(两句及以上)操作了共享资源。(执行到一半,cpu执行权被抢走) */ class SaleTicket extends Thread{ static int num = 50;//票数 非静态的成员变量,非静态的成员变量数据是在每个对象中都会维护一份数据的。 static Object o = new Object(); public SaleTicket(String name) { super(name); } @Override public void run() { while(true){ //同步代码块 synchronized ("锁") { if(num>0){ System.out.println(Thread.currentThread().getName()+"售出了第"+num+"号票"); try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } num--; }else{ System.out.println("售罄了.."); break; } } } } } public class Demo4 { public static void main(String[] args) { //创建三个线程对象,模拟三个窗口 SaleTicket thread1 = new SaleTicket("窗口1"); SaleTicket thread2 = new SaleTicket("窗口2"); SaleTicket thread3 = new SaleTicket("窗口3"); //开启线程售票 thread1.start(); thread2.start(); thread3.start(); } }
如果采用第二种方式创建多线程,则不用static修饰num。因为只传了一个对象。
class SaleTicket implements Runnable{ int num = 50; // 票数 @Override public void run() { while(true){ synchronized ("锁") { if(num>0){ System.out.println(Thread.currentThread().getName()+"售出了第"+ num+"号票"); num--; }else{ System.out.println("售罄了.."); break; } } } } } public class Demo4 { public static void main(String[] args) { //创建了一个Runnable实现类的对象 SaleTicket saleTicket = new SaleTicket(); //创建三个线程对象模拟三个窗口 Thread thread1 = new Thread(saleTicket,"窗口1"); Thread thread2 = new Thread(saleTicket,"窗口2"); Thread thread3 = new Thread(saleTicket,"窗口3"); //开启线程售票 thread1.start(); thread2.start(); thread3.start(); } }