线程安全的问题的——同步与互斥
2014-02-28 20:33
204 查看
线程安全问题产生的原因:
1,多个线程在操作共享的数据。
2,操作共享数据的线程代码有多条。
当一个线程在执行操作共享数据的多条代码过程中,其他线程参与了运算。
就会导致线程安全问题的产生。
还是以上一篇博客中讲的出售火车票问题来讲
下面是运行结果:
从上面可以看出程序出现了负数,这就是当一个线程在执行操作共享数据的多条代码过程中,其他线程参与了运算而导致的线程安全问题。
那么怎么解决这种线程安全问题呢?
就是将多条操作共享数据的线程代码封装起来,当有线程在执行这些代码的时候,其他线程不可以参与运算。必须要当前线程把这些代码都执行完毕后,其他线程才可以参与运算。
在java中,需要用同步代码块来解决这个问题
同步代码块的格式:
synchronized(对象)
{
需要被同步的代码 ;
}
使用同步后的代码如下:
让后再运行就没有了上面出现负数的情况。
java中同步还有另外一种实现方式,那就是同步函数。
同步代码块和同步函数的区别:
同步函数的锁是固定的this
同步代码块的锁是任意的对象。
在实际开发中建议使用同步代码块。
在前面的一篇博客单例模式说到懒汉式在多线程中可能无法保证对象的唯一性,解决代码如下:
可以看到上面的代码中有两处出现了if(s==null),其实这么实现非常有必要,假如某一瞬间线程A和线程B都在调用getInstance()方法,此时s的对象为null,都能通过s==null的判断,由于实现了synchronized加锁机制,线程A进入了锁定的代码中执行实例创建代码,线程B进入排队等待状态,必须等线程A执行完毕后才可以进入锁定的代码,但当A执行完毕时,线程B不再进行s==null的判断,并不知道实例依已经创建,将继续创建实例,导致产生多个单例对象。因此需要再进行一次判断,这种方式被称为双重检查锁定。
总结:
同步的好处:解决了线程的安全问题。
同步的弊端:相对降低了效率,因为同步外的线程的都会判断同步锁。
同步的前提:同步中必须的多个线程只能使用同一个锁。也就是说方法或同步的代码块只能被一个线程执行。
这种同步锁的方式称为隐式锁,在JDK 1.5之后将同步和锁封装成了对象并将操作锁的隐式方式定义到了该对象中,将隐式动作变成了显式动作。
那就是Lock,lock的作用就是用来替代synchronized,修改后的代码如下:
1,多个线程在操作共享的数据。
2,操作共享数据的线程代码有多条。
当一个线程在执行操作共享数据的多条代码过程中,其他线程参与了运算。
就会导致线程安全问题的产生。
还是以上一篇博客中讲的出售火车票问题来讲
class Ticket implements Runnable { private int num = 100; Object obj = new Object(); public void run() { while(true) { if(num>0) { try{Thread.sleep(10);}catch (InterruptedException e){} System.out.println(Thread.currentThread().getName()+".....sale...."+num--); } } } }
下面是运行结果:
从上面可以看出程序出现了负数,这就是当一个线程在执行操作共享数据的多条代码过程中,其他线程参与了运算而导致的线程安全问题。
那么怎么解决这种线程安全问题呢?
就是将多条操作共享数据的线程代码封装起来,当有线程在执行这些代码的时候,其他线程不可以参与运算。必须要当前线程把这些代码都执行完毕后,其他线程才可以参与运算。
在java中,需要用同步代码块来解决这个问题
同步代码块的格式:
synchronized(对象)
{
需要被同步的代码 ;
}
使用同步后的代码如下:
class Ticket implements Runnable { private int num = 100; Object obj = new Object(); public void run() { while(true) { synchronized(obj) { if(num>0) { try{Thread.sleep(10);}catch (InterruptedException e){} System.out.println(Thread.currentThread().getName()+".....sale...."+num--); } } } } }
让后再运行就没有了上面出现负数的情况。
java中同步还有另外一种实现方式,那就是同步函数。
同步代码块和同步函数的区别:
同步函数的锁是固定的this
同步代码块的锁是任意的对象。
在实际开发中建议使用同步代码块。
在前面的一篇博客单例模式说到懒汉式在多线程中可能无法保证对象的唯一性,解决代码如下:
class Single { private static Single s = null; private Single(){} public static Single getInstance() { if(s==null) { synchronized(Single.class) { if(s==null) s = new Single(); } } return s; } }
可以看到上面的代码中有两处出现了if(s==null),其实这么实现非常有必要,假如某一瞬间线程A和线程B都在调用getInstance()方法,此时s的对象为null,都能通过s==null的判断,由于实现了synchronized加锁机制,线程A进入了锁定的代码中执行实例创建代码,线程B进入排队等待状态,必须等线程A执行完毕后才可以进入锁定的代码,但当A执行完毕时,线程B不再进行s==null的判断,并不知道实例依已经创建,将继续创建实例,导致产生多个单例对象。因此需要再进行一次判断,这种方式被称为双重检查锁定。
总结:
同步的好处:解决了线程的安全问题。
同步的弊端:相对降低了效率,因为同步外的线程的都会判断同步锁。
同步的前提:同步中必须的多个线程只能使用同一个锁。也就是说方法或同步的代码块只能被一个线程执行。
这种同步锁的方式称为隐式锁,在JDK 1.5之后将同步和锁封装成了对象并将操作锁的隐式方式定义到了该对象中,将隐式动作变成了显式动作。
那就是Lock,lock的作用就是用来替代synchronized,修改后的代码如下:
class Ticket implements Runnable { private int num = 100; Lock l=new ReentrantLock(); public void run() { while(true) { l.lock(); if(num>0) { try{Thread.sleep(10);}catch (InterruptedException e){} System.out.println(Thread.currentThread().getName()+".....sale...."+num--); } l.unlock(); } } }
相关文章推荐
- 进程间同步互斥经典问题(一)生产者-消费者问题
- 生产者-消费者问题实现 (linux下C同步信号量和互斥信号量的应用)
- iOS (objective-c) 中的多线程互斥同步问题
- 杂项: 线程的同步于互斥,生产者问题,读写者问题实现
- 线程安全问题(银行取钱问题)、同步代码块、同步方法
- 进程、线程知识点总结和同步(消费者生产者,读者写者三类问题)、互斥、异步、并发、并行、死锁、活锁的总结
- Java使用同步解决线程安全问题的弊端
- 【操作系统】进程(线程)间同步互斥问题——熟睡的理发师问题
- 同步和互斥的一些问题(死锁,优先级逆转)
- 三个同步与互斥问题之哲学家就餐
- Java总结(十)—实现Runnable接口创建线程,线程安全同步,死锁(哲学家进餐问题),读写锁
- 操作系统初探-同步中的互斥问题
- iOS (objective-c) 中的多线程互斥同步问题
- 同步和互斥的问题
- 操作系统--同步与互斥问题
- 同步和互斥的一些问题(死锁,优先级逆转)
- 同步互斥问题及解决方法
- GNU/Linux中解决多线程互斥同步问题
- 进程的同步与互斥-哲学家问题
- 操作系统 同步互斥问题之 理发师问题