您的位置:首页 > 其它

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

2018-03-12 15:07 1456 查看
原子操作:所谓原子操作是指不会被线程调度机制打断的操作;这种操作一旦开始,就一直运行到结束,中间不会有任何 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程序运行使用任何对象作为同步监听对象,但是一般的,我们试验当前并发访问的共同资源作为同步监听对象。

注意:在任何时候,最多允许一个线程拥有同步锁,谁拿到锁就进入代码块,其他线程只能等待。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐