1. 多线程场景下数据不安全

  1. 直接上代码
public class MyThread 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(Thread.currentThread().getName() + "正在出售第 " + ticket + "票");
                ticket -- ;
            }
        }
    }
}
  1. 启动3个线程去执行任务
public class MainThread {
    public static void main(String[] args) {

        MyThread myThread = new MyThread();
        Thread t1 = new Thread(myThread);
        t1.start();

        Thread t2 = new Thread(myThread);
        t2.start();

        Thread t3 = new Thread(myThread);
        t3.start();
    }
}
  1. 执行结果:
Thread-2正在出售第 4Thread-0正在出售第 4Thread-1正在出售第 4Thread-2正在出售第 3Thread-0正在出售第 3Thread-1正在出售第 3Thread-1正在出售第 2Thread-0正在出售第 2Thread-2正在出售第 2Thread-0正在出售第 1Thread-2正在出售第 1Thread-1正在出售第 1

可以看出,多个线程访问同一份资源,若没有处理好访问控制的话,则很容易出现数据安全的问题
这里就是100张,确实际可以出售300张

  1. 解决方案一: 同步代码块 synchronized,进行加锁
    synchronized详解:
    (1)同步代码块中的锁对象,可以是任意对象
    (2)必须保证所有的线程竞争的是同一个锁对象
    代码优化方案如下:
public class MyThread implements Runnable{

    private int ticket = 100;//共享资源

    Object object = new Object();//锁对象

    @Override
    public void run() {
        while (true){
            synchronized (object){
                if (ticket > 0 ){
                    try {
                        Thread.sleep(10);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println(Thread.currentThread().getName() + "正在出售第 " + ticket + "票");
                    ticket -- ;
                }
            }
        }
    }
}

如此便实现了多个线程之间的访问控制

Thread-1正在出售第 9Thread-1正在出售第 8Thread-1正在出售第 7Thread-1正在出售第 6Thread-1正在出售第 5Thread-1正在出售第 4Thread-1正在出售第 3Thread-1正在出售第 2Thread-1正在出售第 1
  1. 解决方案二: 同步方法(使用synchronized修饰的方法)
    将共享数据的代码放在synchronized修饰的方法中.
public class MyThread implements Runnable{

    private int ticket = 100;//共享资源
    Object object = new Object();//锁对象

    @Override
    public void run() {
        while (true){
            payTicket();
        }
    }
    //同步方法
    public synchronized void payTicket(){
        if (ticket > 0 ){
            try {
                Thread.sleep(10);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName() + "正在出售第 " + ticket + "票");
            ticket -- ;
        }
    }
}

注意: 同步方法的锁对象就是当前线程任务的对象,也就是MyThread = this

  1. 方案三: 静态代码块:
public class MyThread implements Runnable{

    private static int ticket = 100;//共享资源
    Object object = new Object();//锁对象

    @Override
    public void run() {
        while (true){
            payTicket();
        }
    }
    //同步方法
    public static synchronized void payTicket(){
        if (ticket > 0 ){
            try {
                Thread.sleep(10);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName() + "正在出售第 " + ticket + "票");
            ticket -- ;
        }
    }
}

静态方法的锁对象: 本类的字节码对象

你可能感兴趣的:(多线程,&,并发编程实战,安全,java,开发语言)