如何解决多线程并发访问一个资源的安全性问题?

时间:2023-02-06 13:09:16

原子操作所谓原子操作是指不会被线程调度机制打断的操作;这种操作一旦开始,就一直运行到结束,中间不会有任何 context switch (切[1] 换到另一个线程)。
关于我对原子操作的理解:原子操作就类似于化学中的原子为不可分割的单位,也就是如果把需要操作的代码块能够顺序执行中间不为被干扰。
这样就不会出现线程不安全情况(案例中的购票系统出现负数的情况),这种原子操作思想还是挺有用的,在这提提自己也不了解=-=。
解决方案:保证打印编号和操作必须同步执行:System.out.println(Thread.currentThread().getName()+”—卖出的票”+tickets–);
也就是上述代码中ticket–与输出同步执行,不能因为某个线程输出后就休眠而不执行减减操作。


方式一、同步代码块:
语法:

        synchronize(同步锁){ 需要同步操作的代码 }

案例:

package com.test;


//线程安全 
public class Main {  
    public static void main(String[] args){  
        SaleThread saleThread=new SaleThread();  
        new Thread(saleThread,"线程一").start();  
        new Thread(saleThread,"线程二").start();  
        new Thread(saleThread,"线程三").start();  
        new Thread(saleThread,"线程四").start();  
    }  
}  
class SaleThread implements Runnable{  
    private int tickets=10;  
    public void run(){  
        //synchronized (this) {
            while(tickets>0){  
                try{  
                    Thread.sleep(10);  
                }catch(InterruptedException e){  
                    e.printStackTrace();  
                }  
                System.out.println(Thread.currentThread().getName()+"---卖出的票"+tickets--);  
           // } 
        } 
    }  
}  

*输出结果:
线程二—卖出的票10
线程一—卖出的票9
线程四—卖出的票8
线程三—卖出的票7
线程二—卖出的票5
线程一—卖出的票6
线程三—卖出的票4
线程四—卖出的票3
线程一—卖出的票1
线程二—卖出的票2
线程三—卖出的票-1
线程四—卖出的票0*
分析:上述结果中出现负数和0情况(如果数据量大还会出现重复情况)。


方式二、同步方法
使用synchronized修饰的方法就叫同步方法,表示a线程在执行该方法的时候其他线程只能等待。
代码:

        synchronized public void run(){  
            while(tickets>0){  
                try{  
                    Thread.sleep(10);  
                }catch(InterruptedException e){  
                    e.printStackTrace();  
                }  
                System.out.println(Thread.currentThread().getName()+"---卖出的票"+tickets--);  
        }
    **问题来了上述代码中synchronized中的同步锁是谁?**
    对于非static方法同步锁就是this
    对于static方法,我们使用当前方法所在类的字节码对象(当前类名.class)

方式三、同步锁-锁机制lock
为了保证每个线程都能正常执行原子操作,java引入了线程同步机制。
同步监听对象/同步锁/同步监听器/互斥锁(a进去b被排斥,保证只有一个进程执行)
对象的同步锁只是一个概念,可以想象为在对象上标记一个锁。
java程序运行使用任何对象作为同步监听对象,但是一般的,我们试验当前并发访问的共同资源作为同步监听对象。
注意:在任何时候,最多允许一个线程拥有同步锁,谁拿到锁就进入代码块,其他线程只能等待。