线程同步机制。

---恢复内容开始---

线程安全问题:

概述:场景:电影院有一百张票,现在用一个窗口卖1-100号票,属于单线程问题,无线程安全问题

      现在有两个窗口,分别卖1-50 和51-100号票,属于多线程问题,因为没有访问共享数    

      据,也不会有线程安全问题。但如果两个窗口卖的都是1-100号票,有共享数据,就会

      有安全问题。

代码实现:

public class Sell implements Runnable {
    private int ticket=100;
    @Override
    public void run() {
       while (true){
           if (ticket>0){
               try {
                   Thread.sleep(10);
               } catch (InterruptedException e) {
                   e.printStackTrace();
               }
               System.out.println("正在卖第"+ticket+"张票");
               ticket--;
           }
       }
    }
}
 public static void main(String[] args) {
        Sell sell=new Sell();
        Thread t1=new Thread(sell);
        Thread t2=new Thread(sell);
        Thread t3=new Thread(sell);
        t1.start();
        t2.start();
        t3.start();
        //问题:
        //正在卖第100张票 
        // 正在卖第100张票
        //正在卖第97张票
        //正在卖第0张票
        //正在卖第-1张票
    }

出现问题的原因:重复票:多个线程同时执行。 错误票:大家都进入if语句后,一个线程先执行,已经--,另一个线程后执行,就会产生错误数据。 

解决线程安全问题:

第一种方法:同步代码块。

格式: synchronized (锁对象) { 可能会出现线程安全问题的代码 }

注意:1、锁对象可以使用任意对象,但必须保证多个线程使用同一个锁对象。

   2、锁对象作用:把同步代码块锁住,只让一个线程在同步代码块中执行。

public class Sell implements Runnable {
    private int ticket=100;
    Object obj=new Object();
    @Override
    public void run() {
       while (true){
           synchronized (obj){
               if (ticket>0){
                   try {
                       Thread.sleep(10);
                   } catch (InterruptedException e) {
                       e.printStackTrace();
                   }
                   System.out.println("正在卖第"+ticket+"张票");
                   ticket--;
               }
           }
       }
    }
}

 同步技术原理:

使用了一个锁对象,这个锁对象叫同步锁。

三个线程一起争夺cpu的执行权,谁抢到了就执行run()。

  t1抢到cpu执行权,执行run(),遇到synchronized时,检查是否有锁对象,发现有

,就会获取锁对象,继续执行代码。

  t2抢到了cpu执行权,执行run(),遇到synchronized时,发现没有锁对象,会等到

  t1归还锁对象后,获取锁对象,才能继续执行。

第二种方法:同步方法。

使用步骤:

  1、把访问共享数据的代码提取出来,放到一个方法中。

  2、在方法上加synchronized修饰符。

使用格式: 修饰符 synchronized 返回值类型  方法名 (){可能出问题的代码}

锁对象:this。

public class Sell implements Runnable {
private int ticket=100;
Object obj=new Object();
@Override
public void run() {
while (true){
sell();
}
}
public synchronized void sell(){
if (ticket>0){
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("正在卖第"+ticket+"张票");
ticket--;
}
}
}

第三种方法:静态方法。

上面的方法加上  static。

锁对象:this是创建对象后产生的,静态方法优先于对象,静态方法的锁对象是本类的class属性。

第四种方法: Lock 锁。

java.util.concurrent.Locks.Lock 接口。

Lock 提供了比synchronized更广泛的操作。

Lock中的方法:void Lock()获取锁。 void unLock() 释放锁。

实现类: java.util.concurrent.Locks.ReentrantLock  implements Lock.

使用步骤:

  1、在成员位置创建一个ReentrantLock对象

  2、可能会出现安全问题的代码前调用Lock接口中的 Lock方法获取锁。

  3、......后调用  unLock方法   释放锁。

public class Sell implements Runnable {
    private int ticket=100;
    Lock lock=new ReentrantLock();
    @Override
    public void run() {
       while (true){
           lock.lock();
           if (ticket>0) {
               try {
                   Thread.sleep(10);
                   System.out.println("正在卖第" + ticket + "张票");
                   ticket--;
               } catch (InterruptedException e) {
                   e.printStackTrace();
               }finally {
                   lock.unlock();
               }
           }
       }
    }

}
原文地址:https://www.cnblogs.com/zhangyuhao/p/10799585.html