线程安全的解释


思路:三个窗口卖票,我们需要设计3条线程表示都实现卖票的操作。并且因为我们的票数是一定的,所以我们需要将票数共享

package com.cook.test;
//实现卖票操作(实现Runnable接口实现)
public class MyThread3 implements Runnable {
    //定义票数并共享
    public static int ticket = 0;
    @Override
    public void run() {
      while (true){
         if(ticket<100){
             ticket++;
             try {
                 Thread.sleep(100);
             } catch (InterruptedException e) {
                 throw new RuntimeException(e);
             }
             System.out.println(Thread.currentThread().getName()+"已经卖了"+ticket+"张票"+"还剩下"+(100-ticket)+"张票");
         }else {
             break;
         }

      }
    }
}

package com.cook.test;

public class MyThreadTest3 {
    public static void main(String[] args) {
        MyThread3 t = new MyThread3();
        Thread t1 = new Thread(t,"窗口1");
        Thread t2 = new Thread(t,"窗口2");
        Thread t3 = new Thread(t,"窗口3");
        t1.start();
        t2.start();
        t3.start();
    }
}

但是以上程序会导致以下问题:

对于票号重复的解释


当我们的线程有自增后(tick=1)CPU被线程2抢走了,线程2自增后CPU并线程3抢走了(tick=3)然后打印的都是tick=3了

出现超出范围的票的解释


当我们的ticket是99的时候,线程1抢到cpu执行了ticket++(ticket100)的时候cpu被线程2签到也是执行到ticket++(ticket101)的时候cpu被线程3抢走然后执行ticket++(ticket==102)然后3个线程再次抢夺,打印出来的票号都是102了

以上2种原因的本质是:线程执行时,有随机性

解决方法---同步代码块

我们的某一个线程在执行的时候,可能不知道什么时候CPU的执行权就被其他线程抢走了。我们可以设置同步代码块,我们的一个线程在执行同步代码块时,这个同步代码块的锁就会关闭,只有当该线程执行完了锁才会打开,别的线程才能进入执行

package com.cook.test;
//实现卖票操作(实现Runnable接口实现)
public class MyThread3 implements Runnable {
    //定义票数并共享
    public static int ticket = 0;
    @Override
    public void run() {
      while (true){
          synchronized (MyThread3.class){
              if(ticket<100){
                  ticket++;
                  try {
                      Thread.sleep(100);
                  } catch (InterruptedException e) {
                      throw new RuntimeException(e);
                  }
                  System.out.println(Thread.currentThread().getName()+"已经卖了"+ticket+"张票"+"还剩下"+(100-ticket)+"张票");
              }else {
                  break;
              }

          }

      }
    }
}

package com.cook.test;

public class MyThreadTest3 {
    public static void main(String[] args) {
        MyThread3 t = new MyThread3();
        Thread t1 = new Thread(t,"窗口1");
        Thread t2 = new Thread(t,"窗口2");
        Thread t3 = new Thread(t,"窗口3");
        t1.start();
        t2.start();
        t3.start();
    }
}


为了确保我们的锁的对象唯一,我们可以时候当前类的字节码文件对象MyThread.class来表示,因为类的字节码文件是唯一的,所以其字节码文件的对象也是唯一的,锁对象的唯一使得我们的每一个线程面对的都是同一把锁,如果锁对象不唯一,线程面对的锁不一样,这其实就和没有写锁一样(锁对象用别的也可以,唯一就行)

posted @ 2023-03-07 20:02  一往而深,  阅读(20)  评论(0编辑  收藏  举报