解决线程安全问题的三种方式

本文详细介绍了Java中实现多线程的两种常见方式:通过实现Runnable接口和继承Thread类,并展示了同步代码块和同步方法的用法。通过示例代码展示了如何使用synchronized关键字进行线程同步,防止并发问题。此外,还提到了JDK5.0引入的Lock锁(以ReentrantLock为例),并比较了synchronized和Lock的异同。最后,讨论了在多线程编程中选择锁策略的一般顺序。
摘要由CSDN通过智能技术生成

例子:创建三个窗口卖票,总票数为100张

方式一:同步代码块

synchronized(同步监视器){

//需要被同步的代码

}

1.使用实现Runnable接口的方式实现

public class Window1Test {
    public static void main(String[] args) {
        Window1 w=new Window1();
        Thread t1=new Thread(w);
        Thread t2=new Thread(w);
        Thread t3=new Thread(w);
        t1.setName("窗口一");
        t2.setName("窗口二");
        t3.setName("窗口三");
        t1.start();
        t2.start();
        t3.start();

    }
}
class Window1 implements Runnable{
     private int ticket=100;
     Object obj=new Object();
    @Override
    public void run() {
         while(true){
             synchronized(this){
                 if(ticket>0){
                     System.out.println(Thread.currentThread().getName()+"卖票,票号为:"+ticket);
                     try {
                         Thread.sleep(100);
                     } catch (InterruptedException e) {
                         e.printStackTrace();
                     }
                     ticket--;
                 }else{
                     break;
                 }
             }
         }
    }
}

2. 使用继承Thread类的方式实现

public class WindowTest2 {
    public static void main(String[] args) {
        Window2 t1=new Window2();
        Window2 t2=new Window2();
        Window2 t3=new Window2();
        t1.setName("窗口一");
        t2.setName("窗口二");
        t3.setName("窗口三");
        t1.start();
        t2.start();
        t3.start();
    }
}
class Window2 extends Thread{
      private static int ticket=100;
      private static Object obj=new Object();
    @Override
    public void run() {
         while(true){
             synchronized(Window2.class){

                 if(ticket>0){
                     try {
                         Thread.sleep(100);
                     } catch (InterruptedException e) {
                         e.printStackTrace();
                     }
                     System.out.println(Thread.currentThread().getName()+"卖票,票号为:"+ticket);
                     ticket--;
                 }else{
                     break;
                 }
             }
         }
    }
}

3.说明

(1)同步监视器,俗称:锁。任何一个类的对象,都可以充当锁。(要求:多个线程必须要共用同一把锁

(2)在实现Runnable接口创建多线程的方式中,我们可以考虑使用this充当同步监视器

(3)在实现继承Thread类创建多线程的方式中,我们可以考虑使用Window2.class充当同步监视器

方式二:同步方法

1.使用实现Runnable接口的方式实现

public class WindowTest3 {
    public static void main(String[] args) {
        Window3 w=new Window3();
        Thread t1=new Thread(w);
        Thread t2=new Thread(w);
        Thread t3=new Thread(w);
        t1.setName("窗口一");
        t2.setName("窗口二");
        t3.setName("窗口三");
        t1.start();
        t2.start();
        t3.start();
    }
}
class Window3 implements Runnable {
    private int ticket = 100;

    @Override
    public  void run() {
        while (true) {
            show();
        }
    }

    public synchronized void show() {//同步监视器:this

            if (ticket > 0) {
                System.out.println(Thread.currentThread().getName() + "卖票,票号为:" + ticket);
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                ticket--;
            }
    }

2.使用继承Thread类的方式实现

public class WindowTest4 {
    public static void main(String[] args) {
        Window4 t1=new Window4();
        Window4 t2=new Window4();
        Window4 t3=new Window4();
        t1.setName("窗口一");
        t2.setName("窗口二");
        t3.setName("窗口三");
        t1.start();
        t2.start();
        t3.start();

    }
}
class Window4 extends Thread{
    private static int ticket=100;
    @Override
    public void run() {
        while(true){
               show();
            }
        }
        private static synchronized void show(){//同步监视器:t1,t2,t3-----换成静态方法:同步监视器:Window4.class
            if(ticket>0){
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(Thread.currentThread().getName()+"卖票,票号为:"+ticket);
                ticket--;
            }
        }
}

3.说明

(1)同步方法仍然涉及到同步监视器,只是不需要我们显式的声明。

(2)非静态的同步方法,同步监视器是:this

(3)静态的同步方法,同步监视器是:当前类本身

方式三:Lock锁——JDK5.0新增

public class LockTest {
    public static void main(String[] args) {
      Window w=new Window();
      Thread t1=new Thread(w);
      Thread t2=new Thread(w);
      Thread t3=new Thread(w);
      t1.setName("窗口一");
      t2.setName("窗口二");
      t3.setName("窗口三");
      t1.start();
      t2.start();
      t3.start();
    }
}
class Window implements Runnable{
    private int ticket=100;
    //1.实例化ReentrantLock
    private ReentrantLock lock=new ReentrantLock();

    @Override
    public void run() {
          while(true){
              try{
                  //2.调用锁定方法lock()
                  lock.lock();
                  if(ticket>0){
                      try {
                          Thread.sleep(100);
                      } catch (InterruptedException e) {
                          e.printStackTrace();
                      }
                      System.out.println(Thread.currentThread().getName()+":售票,票号为:"+ticket);
                      ticket--;
                  }else{
                      break;
                  }
              }finally{
                  //3.调用解锁方法unlock()
                   lock.unlock();
              }
          }
    }
}

 Synchronized和Lock的异同

1.相同点:二者都可以解决线程安全问题

2.不同点:synchronized机制在执行完相应的同步代码以后,自动的释放同步监视器。而Lock需要手动的启动同步(lock()),同时结束同步也需要手动的实现(unlock())

3.优先使用顺序:Lock——>同步代码块(已经进入了方法体,分配了相应的资源)——>同步方法(在方法体之外)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值