解决线程安全问题的第一种方案:使用同步代码块,保证安全
格式:
synchronized (锁对象){
可能出现线程安全问题的代码(访问了共享数据的代码)
}
注意:
1.同步代码块中的锁对象,可以使用任意的对象
2.必须保证多个线程使用的锁对象是同一个
3.锁对象作用:把同步代码块锁住,只让一个线程在同步代码块中执行
缺点:程序频繁的判断,获取,释放锁,代码的效率会降低

public class RunnableImp2 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 (Exception e) {
                       e.printStackTrace();
                   }
                   System.out.println(Thread.currentThread().getName() + "-->正在卖第" + ticket + "张票");
                   ticket--;
               }
           }
       }
    }
}

主程序:

package com.itheima.demo01.Exception.Synchronized;

public class Demo01Ticket {
    public static void main(String[] args) {
        RunnableImp2 run = new RunnableImp2();
        Thread t0 = new Thread(run);
        Thread t1 = new Thread(run);
        Thread t2 = new Thread(run);
        t0.start();
        t1.start();
        t2.start();

    }
}

通过同步保护不会出现卖票冲突的问题,如果不使用,会出现3个线程同时卖一张票的情况。

/*
解决线程安全问题的第二种方案:使用同步方法
使用步骤:
1.把访问了共享数据的代码抽取出来,放到一个方法中
2.在方法上添加synchronized修饰符
格式:
修饰符 synchronized 返回值类型 方法名(参数列表){
可能出现线程安全问题的代码(访问了共享数据的代码)
}
注意:
同步方法也会把方法内部的代码锁住只会让一个线程执行
同步方法的锁对象 new synchronized 也就是this

  • */
public class RunnableImp2 implements Runnable {
    //定义一个多个线程共享的资源
    private int ticket = 100;
    //设置线程任务:卖票
    @Override
    public void run() {
    //使用死循环,让卖票操作重复执行
       while(true){
           payTicket();
       }
       //定义一个同步方法
    }
    public synchronized void payTicket() {
        if (ticket > 0) {
            //提高安全问题出现的概率,让程序睡眠
            try {
                Thread.sleep(10);
            } catch (Exception e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName() + "-->正在卖第" + ticket + "张票");
            ticket--;
        }
    }
}

/*

解决线程安全问题的第三种方案:使用Lock锁
Lock 实现提供了比使用 synchronized 方法和语句可获得的更广泛的锁定操作。
Lock接口中的方法:
void Lock()获取锁
void unLock()释放锁
java.util.concurrent.locks.ReentrantLock implements Lock接口

使用步骤:
1.在成员位置创建一个ReentrantLock对象
2.在可能会出现安全问题的代码前调用Lock接口中的方法获取锁
3.在可能会出现安全问题的代码后调用Lock接口中的方法释放锁

  • */
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class RunnableImp2 implements Runnable {
    //定义一个多个线程共享的资源
    private int ticket = 100;
    Lock l = new ReentrantLock();
    //设置线程任务:卖票
    @Override
    public void run() {
    //使用死循环,让卖票操作重复执行
       while(true){
           l.lock();
           if (ticket > 0) {
               //提高安全问题出现的概率,让程序睡眠
               try {
                   Thread.sleep(10);
                   System.out.println(Thread.currentThread().getName() + "-->正在卖第" + ticket + "张票");
                   ticket--;
               } catch (Exception e) {
                   e.printStackTrace();
               }finally {
                   l.unlock();//无论是否出现异常,都把锁释放
               }

           }

       }
    }
}