Análisis de dos ejemplos clásicos de seguridad de subprocesos

Ejemplo 1: venta de entradas y compra de entradas

@Slf4j(topic = "c.CASE1")
public class CASE1 {
    
    
    //随机数
    public static Random random = new Random();
    public static int getRandom(){
    
    
        return random.nextInt(5) + 1;
    }

    public static void main(String[] args) {
    
    
        //开始有2000张票
        TicketWindow ticketWindow = new TicketWindow(2000);
        List<Thread> list = new ArrayList<>();
        //用来存储卖出去多少张票,//竞态条件
        //List<Integer> sellCount = new ArrayList<>();
        List<Integer> sellCount = new Vector<>();
        //2000票分2000次卖出去
        for (int i = 0; i < 2000; i++) {
    
    
            Thread t = new Thread(()->{
    
    
                //竞态态条件
                int sell = ticketWindow.sell(getRandom());
                sellCount.add(sell);
            });
            list.add(t);
            t.start();
        }
        //使得每个线程运行结束
        list.forEach((t)->{
    
    
            try {
    
    
                t.join();
            } catch (InterruptedException e) {
    
    
                e.printStackTrace();
            }
        });
        //统计最终结果
        log.debug("卖出去:" + sellCount.stream().mapToInt(a->a).sum());
        log.debug("剩余票数:" + ticketWindow.getCount());

    }

}

class TicketWindow{
    
    
    private int count;

    public TicketWindow(int count) {
    
    
        this.count = count;
    }

    public int getCount() {
    
    
        return count;
    }

    public void setCount(int count) {
    
    
        this.count = count;
    }

    //卖出去多少张表,如果返回0则表示没卖出去票
    public int sell(int amount){
    
    
        if(amount < count){
    
    
            count = count - amount;
            return amount;
        }else{
    
    
            return 0;
        }
    }
}

Por favor, determine si este ejemplo es seguro para subprocesos.
Ejecutar salida:
Inserte la descripción de la imagen aquí

Respuesta: ¡Existe!
Análisis: determinar si hay pasos seguros para subprocesos

  1. Encuentra la zona crítica:

    int sell = ticketWindow.sell(getRandom());
    sellCount.add(sell);
    
  2. Encuentra variables compartidas

    Las dos variables compartidas count, sellCount, son seguras para subprocesos, solo la variable de recuento, porque el método add de sellCount es seguro para subprocesos. Si se reemplaza con una ArrayList, se producirán problemas de seguridad de subprocesos. La variable de recuento pertenece a la objeto ticketWindow.

Solución :
puede bloquear la variable compartida, utilizando palabras clave synchronized, puede agregar el bloqueo al método de venta

    public synchronized int sell(int amount){
    
    
        if(amount < count){
    
    
            count = count - amount;
            return amount;
        }else{
    
    
            return 0;
        }
    }

Equivalente a

    public int sell(int amount){
    
    
    synchronized(this){
    
    
            if(amount < count){
    
    
            count = count - amount;
            return amount;
        }else{
    
    
            return 0;
        }
    }
 }

Ejemplo 2: Transferir ab entre sí

@Slf4j(topic = "c.CASE2")
public class CASE2 {
    
    

    private static Random random = new Random();
    //返回1-100随机数
    public static  int getRandom(){
    
    
        return random.nextInt(100) + 1;
    }

    public static void main(String[] args) throws InterruptedException {
    
    
        //账户a
        Account a = new Account(1000);
        //账户b
        Account b = new Account(1000);

        Thread t1 = new Thread(()->{
    
    
            for (int i = 0; i < 1000; i++) {
    
    
                a.transfer(b,getRandom());
            }
        });
        Thread t2 = new Thread(()->{
    
    
            for (int i = 0; i < 1000; i++) {
    
    
                b.transfer(a,getRandom());
            }
        });

        //分别启动t1 t2
        t1.start();
        t2.start();

        //等待t1 t2跑完
        t1.join();
        t2.join();

        log.debug("账户a  :" + a.getMoney() );
        log.debug("账户b :"  + b.getMoney());
        log.debug("total:{}",(a.getMoney() +b.getMoney()));
    }
}


class Account{
    
    
    private int money;

    public Account(int money) {
    
    
        this.money = money;
    }

    public int getMoney() {
    
    
        return money;
    }

    public void setMoney(int money) {
    
    
        this.money = money;
    }

    public void transfer(Account target,int amount){
    
    
        //有钱可转
        if(this.money > amount){
    
    
            this.setMoney(this.getMoney() - amount);
            target.setMoney(target.getMoney() + amount);
        }
    }

}

Inserte la descripción de la imagen aquí
¡Se puede ver en el resultado de ejecución que hay un problema de seguridad de subprocesos!
análisis:

  1. Sección crítica

    public void transfer(Account target,int amount){
          
          
        //有钱可转
        synchronized (Account.class){
          
          
            if(this.money > amount){
          
          
                this.setMoney(this.getMoney() - amount);
                target.setMoney(target.getMoney() + amount);
            }
        }
    }
    
  2. Variable compartida

    La diferencia con el ejemplo anterior es que hay dos variables compartidas en la sección crítica, y son this.moneysumas target.money. Entonces, si se agrega un bloqueo a la transferencia, ¿se puede bloquear? ¿Puede resolver el problema de seguridad del subproceso?

     public synchronized void transfer(Account target,int amount){
          
          
         //有钱可转
           if(this.money > amount){
          
          
               this.setMoney(this.getMoney() - amount);
               target.setMoney(target.getMoney() + amount);
           }
     }
    

    ¡No es!
    La razón es: en este momento sincronizado solo puede bloquear la variable compartida del objeto actual, es decir, quien llame a transferencia bloqueará el atributo de dinero de cuyo dinero, por lo que no puede resolver el problema de seguridad del hilo, así que si puede bloquear ambos al mismo tiempo ¿Qué pasa con el objeto?

Solución : Ampliar el alcance de la cerradura, se bloqueen el objeto de bloquear toda la clase, porque ambos this.moneyy target.moneyque pertenecen a la clase de cuenta, por lo que el alcance de bloqueo de la sección crítica puede ser modificado de la siguiente manera:

    public void transfer(Account target,int amount){
    
    
        //有钱可转
        synchronized (Account.class){
    
    
            if(this.money > amount){
    
    
                this.setMoney(this.getMoney() - amount);
                target.setMoney(target.getMoney() + amount);
            }
        }
    }

Vuelva a ejecutar el código:
Inserte la descripción de la imagen aquí

En este momento, se logra nuestro objetivo de seguridad de subprocesos.

Supongo que te gusta

Origin blog.csdn.net/JAYU_37/article/details/111111835
Recomendado
Clasificación