




已阅读5页,还剩6页未读, 继续免费阅读
版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
Java并发编程学习一 对象的共享线程之间对象的共享不仅仅需要有原子性和临界区,还有一个重要方面:内存可见性1 可见性读操作的线程并非可以一直获取到写线程写入的最新值,例如:代码示例 private static boolean ready; private static int number; private static class ReaderThread extends Thread public void run() while (!ready) Thread.yield(); System.out.println(number); public static void main(String args) new ReaderThread().start(); number = 42; ready = true; 以上实例代码可能存在如下问题:一直循环下去,因为读线程可能看不到写线程写入了ready。在读线程中输出0,因为主线程可能对number和ready的赋值顺序进行了改变。这是由于线程之间没有正确使用同步使得数据在多个线程中共享出现错误导致的。常见的问题包括:读取到的数据已经失效没有对64位数据同步读写失效的数据以上述例子来说,当读线程读取ready变量的时候,很可能该变量已经失效。失效的数据通常包括两类数据:值数据引用数据值数据的失效例如一个计数器应用,如果不对count变量进行同步处理的话可能会导致计数不准的问题。如果对象的引用失效可能会导致一些莫名其妙的问题,比如意料之外的异常,被破坏的数据结果,不精确的计算及无限循环等。非原子的64位操作根据Java内存模型的要求,变量的读写操作必须是原子操作。对于非volatile类型的long和double变量,JVM允许将64位读写操作分解为两个32为的操作,这样在多线程环境中共享可变的long和double变量也是不安全的。一般的解决方式可以通过加锁或者声明变量为volatile变量。内置锁可以用于确保某个线程以一种可预测的方式看到另一个线程执行的结果。加锁的含义不仅仅局限于互斥行为,还包括了内存可见性。 Java同时提供了一种较弱的同步机制,即volatile变量。volatile变量不同于加锁机制,加锁机制可以保证原子性和可见性,但是volatile只能保证可见性。volatile变量会确保将变量的更新结果通知到其他线程,被声明volatile变量会有两个效果:不会对该变量重排序;不会缓存在寄存器或者其他处理器不可见的地方。使用volatile变量可以不执行加锁操作,也不会阻塞线程,开销要比同步要低。但是只能确保可见性,不能确保原子性,更不能保证在volatile变量递增操作的原子性,在使用的时候要注意。当满足以下所有条件时才应该使用volatile变量:对变量写入不依赖当前值,或者确保只有单个线程更新变量的值;该变量不被要求与其他变量同步变化;在访问该变量时不需要加锁,因为加上锁就没必要使用volatile了。2 发布与逸出发布一个对象是指使一个对象能在当前作用域之外的代码中使用。一般是将成员变量或静态变量公开。对于一个类的外部方法是不完全由该类规定的方法,包括该类以外定义的方法及由该类定义但可以被改写的方法。当把一个对象传递给一个外部方法,就相当于发布了这个对象。逸出一个对象是指某个不应该发布的对象被发布安全构造对象错误的构造对象方式public ThisEscape(EventSource source) source.registerListener(new EventListener() public void onEvent(Event e) doSomething(e); ); 在EventListener中保留了this的引用,因此会将this引用逸出。 一定不要在构造过程中使this引用逸出。在构造对象的过程中常见的问题:构造函数中将自己的引用传递给一个线程并将该线程启动;构造函数中调用一个可被改写的方法如果构造函数中需要注册事件监听器或者启动线程,一个办法是将构造函数私有化,用工厂模式初始化。例如:private final EventListener listener;private SafeListener() listener = new EventListener() public void onEvent(Event e) doSomething(e); ;public static SafeListener newInstance(EventSource source) SafeListener safe = new SafeListener(); source.registerListener(safe.listener); return safe;3 线程间的变量对于多线程相关的变量,使用的时候有两种策略,一种不希望被共享,使用线程封闭技术将该变量的使用范围限制在一个线程中,如果需要在多个线程之间进行共享,跟据该变量是否可变,需要有不同的共享策略。3.1 线程封闭线程封闭是指仅在单线程内访问数据,将访问的数据封闭在一个线程中。常见场景:JDBC的Connection对象Command模式Java中提供一些机制可用来实现线程封闭,例如局部变量和ThreadLocal。3.1.1 Ad-hoc线程封闭Ad-hoc线程封闭是指维护线程封闭性的职责完全由程序实现来承担。非常脆弱,一般不建议使用,建议使用其他诸如栈封闭和ThreadLocal。3.1.2 栈封闭在栈封闭中,只有通过局部变量才能访问对象。但程序员需要确保该局部引用不会逸出。3.1.3 ThreadLocal类一种更规范的保证线程封闭的方法就是使用ThreadLocal,set入ThreadLocal的变量每个线程都只有一份独立的副本。当某个线程第一次调用Thread.get的时候,就会通过initialValue获取初始值:private ThreadLocal connectionHolder = new ThreadLocal() public Connection initialValue() try return DriverManager.getConnection(DB_URL); catch (SQLException e) throw new RuntimeException(Unable to acquire Connection, e); ; ;public Connection getConnection() return connectionHolder.get();以上代码可以为每个线程维持一个数据库连接。3.2 线程共享的对象3.2.1不可变对象线程安全是不可变对象的固有属性之一 Java中,不可变不代表对象的所有域都声明为final类型,当满足以下条件的时候,对象才是不可变的:对象创建以后其状态不能修改对象的所有域都是final类型对象正确被创建,没有在构造期间this引用逸出不可变对象的内部仍可以使用可变对象来管理他们的状态,例如:Immutablepublic final class ThreeStooges private final Set stooges = new HashSet();public ThreeStooges() stooges.add(Moe); stooges.add(Larry); stooges.add(Curly);public boolean isStooge(String name) return stooges.contains(name);public String getStoogeNames() List stooges = new Vector(); stooges.add(Moe); stooges.add(Larry); stooges.add(Curly); return stooges.toString();虽然用于管理ThreeStooges对象的Set对象时可变的,但是ThreeStooges对象是不可变的。不可变对象可以提供弱原子性。当需要同时更新多个域时,可以将多个域放入一个不可变对象中保持原子性。例如:public class OneValueCache private final BigInteger lastNumber; private final BigInteger lastFactors; public OneValueCache(BigInteger i, BigInteger factors) lastNumber = i; lastFactors = Arrays.copyOf(factors, factors.length); public BigInteger getFactors(BigInteger i) if (lastNumber = null | !lastNumber.equals(i) return null; else return Arrays.copyOf(lastFactors, lastFactors.length); 每次更新Cache都需要更新lastNumber和lastFactors,更新这两个域需要以原子方式执行某个操作。 调用的时候:private volatile OneValueCache cache = new OneValueCache(null, null);public void service(ServletRequest req, ServletResponse resp) BigInteger i = extractFmRequest(req); BigInteger factors = cache.getFactors(i); if (factors = null) factors = factor(i); cache = new OneValueCache(i, factors); encodeIntoResponse(resp, factors);void encodeIntoResponse(ServletResponse resp, BigInteger factors) BigInteger extractFromRequest(ServletRequest req) return new BigInteger(7);BigInteger factor(BigInteger i) / Doesnt really factor return new BigIntegeri;将cache设置为volatile使的更新了该域后其他线程会立即看到新缓存的数据。3.2.2 事实不可变对象事实不可变对象是指那些从技术上看是可变的,但是其状态在发布之后不会在改变的对象。在没有额外同步的情况下,任何线程都可以安全地使用被安全发布的事实不可变对象。3.2.3 可变对象对于可变对象,不仅发布对象的时候需要使用同步,而且每次访问对象同样需要使用同步确保后续操作修改的可见性。3.3 安全发布三类对象不可变对象:可以通过任意机制发布;事实可变对象:必须通过安全方式发布;可变对象:可变对象必须通过安全方式发布,而且必须是线程安全或者进行锁保护。并发共享对象时的策略线程封闭:栈封闭或者ThreadLocal。只读共享:共享的对象包括不可变对象和事实不可变对象。线程安全共享:线程安全对象在其内部实现同步。保护对象:锁保护二 对象的组合主要介绍一些组合模式,使得一个类更容易成为线程安全的类,在维护这些类时不会无意中破坏类的安全性保证。1.设计线程安全的类设计线程安全类的过程中,需要包含以下三个基本要素:找出构成对象状态的所有变量;找出约束状态变量的不变性条件;建立对象状态的并发访问管理策略。对象的状态如果所有的域都是基本类型,则这些域构成对象的全部状态;如果包含其他对象,该对象的状态将包括被引用对象的域。同步策略规定了如何将不变性条件、线程封闭和加锁机制结合起来以维护线程的安全性,并且规定了哪些变量由哪些锁来保护。1.1 收集同步需求*尽量多的使用final域。*final类型的域使用的越多,状态空间就越小,越能简化对象可能状态的分析过程。在单个或多个域上,某一个操作如果存在的无效的状态转换,需要对该操作进行同步。无效的状态转换包括不满足:不变性条件,例如:int变量超出最大最小值。后验条件,例如:counter当前值是17,那么下一个操作结束一定是18.如果某个操作存在无效的状态转换,那么该操作必须是原子的,即需要同步。1.2 分析依赖状态的操作某些方法包含一些先验条件才能执行,例如:不能够从空队列中删除一个值。单线程程序中如果遇到无法满足先验条件的情况可以直接返回失败,但是并发程序中先验条件可能因为其他线程的执行而变成真,因此要一直等待先验条件为真再执行。1.3 分析状态的所有权所有权在Java中只是一个设计中的要素,在语言层面没有明显的变现。所有权意味着控制权,如果发布了某个可变对象的引用,则意味着共享控制权。在定义哪些变量构成对象的状态时,只考虑对象拥有的数据。2.同步的实现同步的实现手段主要有三个: * 对象封闭 * 委托线程安全性给底层状态变量 * 现有类的封装2.1 对象封闭当一个对象封闭在另一个对象中时,由于被访问的对象所有代码路径都是已知的,因此更易于对代码进行分析。被封闭的作用域可以是:一个实例中:作为一个私有成员某个作用域中:作为局部变量线程里:将对象从一个方法传递到另一个方法通过封闭机制确保线程安全GuardedBy(this) private final Set mySet = new HashSet();public synchronized void addPerson(Person p) mySet.add(p);public synchronized boolean containsPerson(Person p) return mySet.contains(p);需要注意的是,在这个实现中,需要Person也是一个线程安全的类,否则一样会线程不安全。也需要注意,不应该让一个本该封闭的对象逸出作用域。Java中很多容器比如ArrayList和HashMap不是线程安全的类,但类库通过Collections.synchronizedList及类似方法产生一个容器的“装饰器”,使其访问变成了线程安全的。2.1.1 Java监视器模式遵循Java监视器模式的对象会把对象的所有可变状态都封装起来,并由自己的内置锁保护。使用监视器模式public final class Counter GuardedBy(this) private long value = 0; public synchronized long getValue() return value; public synchronized long increment() if (value = Long.MAX_VALUE) throw new IllegalStateException(counter overflow); return +value; Counter中封装了一个变量value,对该变量的所有访问都需要通过Counter的方法执行,这些方法都是同步的。Java监视器仅仅是一种编码约定。私有私有锁保护状态public class PrivateLock private final Object myLock = new Object(); GuardedBy(myLock) Widget widget; void someMethod() synchronized (myLock) / Access or modify the state of widget 实现过程中,使用私有锁而不是对象的内置锁可以避免让客户代码错误的得到锁。2.2 线程安全性委托如果一个类是由多个独立且线程安全的状态变量组成,所有操作中都不会破坏已有的限制条件,那么可以将线程安全性委托给底层的状态变量去做。如果某个类含有复合操作,即多个状态变量之间相互影响,仅仅委托给底层的状态变量去做就不足以保证线程安全性,需要在该类中加锁以保证线程安全性。发布底层的状态变量 如果一个状态变量是线程安全的,并且没有任何不变性条件约束他的值,也不存在任何不允许的状态转换,那么就可以安全的发布这个变量。例如Counter,value需要是正值(约束条件)且必须递增(如果发布客户可以减少,存在不允许的状态转换),因此不能安全的发布Counter中的value状态。public class VisualComponent private final List keyListeners = new CopyOnWriteArrayList(); private final List mouseListeners = new CopyOnWriteArrayList(); public void addKeyListener(KeyListener listener) keyListeners.add(listener); public void addMouseListener(MouseListener listener) mouseListeners.add(listener); public void removeKeyListener(KeyListener listener) keyListeners.remove(listener); public void removeMouseListener(MouseListener listener) mouseListeners.remove(liste
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 《跨地域夫妻情感调解与子女抚养权归属合同》
- 2025年广东省深圳市事业单位工勤技能考试题库(含答案)
- 2025年全国特种设备安全管理(A证)理论考试试题(附答案)
- 2025年幼儿园保健医考核试题及答案
- 2025中小学教师编制考试理论基础知识复习题库及答案
- 2025年事业单位工勤技能考试考试题库及答案解析3
- 2025年国际贸易专业考试试题及答案
- 2025年高级职业技术岗位考试试题及答案
- 司龄管理制度
- 蚊虫防范管理制度
- 《传统书画装裱与修复中材料的选择与运用》
- 2024ESC心房颤动管理指南解读
- 防洪排涝工程合同范本有效
- 高血压病基层诊疗指南
- 医院视频监控系统维保方案
- 门诊护士课件教学课件
- 《大学生的人际关系》课件
- 职务侵占罪培训
- 中式烹调师技能等级认定四级理论知识试卷
- DB65-T 4784-2024 冰川范围调查技术规范
- 幼儿园礼仪小天使《借物品》教学课件
评论
0/150
提交评论