当前位置首页 > 计算机 > 编程与开发语言
搜柄,搜必应! 快速导航 | 使用教程  [会员中心]

JAVA并发编程

文档格式:DOC| 5 页|大小 52KB|积分 15|2022-12-27 发布|文档ID:178019373
第1页
下载文档到电脑,查找使用更方便 还剩页未读,继续阅读>>
1 / 5
此文档下载收益归作者所有 下载文档
  • 版权提示
  • 文本预览
  • 常见问题
  • Java并发编程之之Edited by Mo Dongsong一、 线程安全之可见性线程安全就是在多线程访问的情况下,可以持续进行正确的行为多线程环境中的共享变量不可见性是常见的非线程安全的一种 不少人都错误的认为,一个public变量其他对象都能访问,它就是可见的在单线程环境下,是这样 但在多线程运行环境下,每个线程对变量改变时,其他线程并不一定都能即时看见要保证可见性,必须同步在多CPU的环境中,每个处理器会把修改的共享变量放在寄存器中,而只有写入到内存后,其他处理器才能看见而现代处理器都会重排序执行顺序以提高性能,所以在没有同步的情况下,缓存会改变写入到主内存的次序最终可能导致运行的结果并不是程序原先预先的结果 这里给一个最简单常见的因为变量非可见性的非线程安全的例子 public class NotSafeValue{private int value;public int get(){return value;}public void set(int value) {this.value=value}}在多线程环境下,即使看似set调用在前,get也不一定能拿到最新的值。

    有多种方式来保证共享变量的可变性:(1)加锁使用synchronized 关键字来加锁,这个大家都知道,是最常用的比如上面的例子中,在每个get和set方法都加这个关键字就能保证线程安全了当然java 5.0后,还有显式锁2)使用volatile 关键字在上面的例子中,给value 变量的定义加找个关键字就是线程安全的了从可见性的角度来说,volatile跟使用synchronized是一样的,而且性能优越很多但是,volatile只能保证可见性,不能保证操作的原子性所以不能滥用volatile通常被当作标志状态使用,比如循环的停止标志使用volatile,能保证其他线程对标志的改变都是即时的 (3)使用原子变量 java 5.0后提供了原子变量类型原子变量应该可以说是一种安全的volatile变量,既能保证可见性,还能保证操作的原子性,还有比较好的性能如,可以使用AtomicLong类型的变量在多线程环境下来做自增长的计数变量二、 线程安全之原子性在多线程环境中原子性操作也是经常遇见的比如计数器的实现我们都知道可以实现原子操作的有三种方式:使用synchronized, 使用原子变量和使用显式锁。

    而JDK中也定义了很多线程安全的类型,它们中的方法都是原子性的比如vector和hashtable,还有java.util.concurrent中的集合类但在实际应用中也可能会犯这样的错误:public void add(Object e){If (!vector.contains(e)) vector.add(e);}contains和add都是原子的,但这是一个复合操作,并不能保证整个操作的原子性,还需要额外的锁有人确实犯了这样的错误而很多人都会犯这个错误:public List list= Collection.synchronizedList(new ArrayList());public synchronized void putIfAbsent(Object o){ if (! list.contains(o)) list.add(o);}看起来没有什么问题但是list本身使用的锁并不是putIfAbsent所用的锁所以这个操作不是原子的应该:public void putIfAbsent(Object o){ synchronized(list){ if (! list.contains(o)) list.add(o);}}当然,这是一个‘缺少即加入’实现。

    Java 5.0之后有了并发容器可以不用自己实现这样的功能了三、 线程安全之安全发布先来看一个例子: 有一个正常的类 NiceValuepublic class NiceValue{ private int value; public NiceValue(int value){this.value=value} public void check(){ if(n!=n) throw new Exception(“Wrong Value!”); }}还有一个类使用了类NiceValuepublic class Checker{ public NiceValue niceValue; public void init(){ niceValue=new NiceValue(1); } public void runCheck(){ for(int i=0;i<5;i++){ new Thread(){ public void run(){ init() niceValue.check(); }}.start();} } Public static void main(String args[]){ new Checker().runCheck(); }}在多CPU环境中,多次运行Checker,就会发现有时竟然会抛出异常!通常情况下,看类NiceValue, check方法怎么可能会抛出异常呢。

    但事实确是了这是因为Checker对NiceValue做了不安全的发布使方法check可能读取了过期值具体原因跟变量的可见性是一样的多个线程访问了公共变量niceValue, 使niceValue中的value变量在多线程环境中不可见了对NiceValue来说,它没有线程安全性的问题,但是Checker对它的不正确的发布,导致了线程的安全问题而有关发布的问题,最长遇见的是单实例模式的延迟初始化问题我们都能明白下面的方式是不安全的public class SingletonC{ private static SingletonC instance; public static SingletonC getInstance(){ if(instance==null) instance=new SingletonC(); return instance; }}然后我们大多会通过加锁来保证线程安全当然还有更有效的方式:public class SingletonC{ private static class SingletonCHolder{ public static SingletonC instance=new SingletonC(); } public static SingletonC getInstance(){ return SingletonrCHolder.instance; }}四、 内部锁与显式锁在多线程编程中,synchronized是大家熟悉和经常用的,这也是java的内置锁。

    它可以强制原子性和可见性这没有什么可多说的Java 5.0 提供了新的选择:显式锁ReentrantLockReentrantLock在获取和释放锁方面都提供了与synchronized的语义1、显式锁与内部锁的区别:不能中断正在等待获取内部锁的线程,必须在获取内部锁的代码块中释放内部锁而显式锁是可中断的,可定时的,显式锁必须在finally块中释放使用方式如:Lock lock=new ReentrantLock(); lock.lock(); //这个方法会一直等,不可中断try {… …} finally { lock.unlock(); }if (lock.tryLock()) { //没有获取锁,不等, try{… …}finally{lock.unlock();}}if(lock.tryLock(3,SECONDS)){ //如果没有获取锁,继续尝试3秒钟 try{… …}finally{lock.unlock();}} lock.lockTerrruptibly(); //可中断,抛出InterruptedException 异常try {… …} finally { lock.unlock(); }注意:一定要记得在finally块中释放锁,这通常也是显示锁的一个风险。

    2、性能对比 在java 5.0,显式锁跟内部锁比,有很大的性能提升但是java 6也改善了内部锁的实现算法,使用了类似显式锁的算法内部锁的性能已经很接近显式锁了3、锁的选择Java 6中,内部锁的性能已经非常接近显式锁了而且,内部锁使用比较简洁,而显式锁还存在忘记在finally块中释放的风险如果能用内部锁解决,就尽量用内部锁4、读写锁Java 5.0还提供了一个很特殊的锁:读写锁ReadWriteLock有时会很有用的哦它允许多个线程来同时读取,但是只允许一个线程写很明显,这可能会带来性能的提升例子:public class ReadWriteData{ private final Map map; private final ReadWriteLock lock=new ReentrantReadWriteLock(); private final Lock rLock=lock.readLock(); private final Lock wLock=lock.writeLock(); public ReadWriteData(Map map){ this.map=map; } public void put(K key,V value){ wLock.lock(); try{ map.put(key,value); } finally{wLock.unlock();} } public V get(K key){ rLock.lock(); try{ return map.get(key); } finally{rLock.unlock();} }}五、 并发容器。

    点击阅读更多内容
    卖家[上传人]:hjk269565
    资质:实名认证