Java线程安全,什么是类的线程安全
编写线程安全的代码,核心在于状态访问操作的管理,尤其是对共享和变量状态的访问。当多个线程访问一个状态变量,并且其中一个线程执行写操作时,必须采用同步机制来协调这些线程对该变量的访问。无状态对象必须是线程安全的。
如何解决写爬虫IP受阻的问题?立即使用。
如果我们在无状态的对象中增加一个状态时,会出现什么情况呢?
假设我们向servlet添加一个“命中计数器”,以如下方式管理请求的数量:向servlet添加一个long字段,每次处理一个请求,该值就加1。
公共类UnsafeCountingFactorizer实现Servlet {
私有长计数=0;
public long getCount() {
返回计数;
}
@覆盖
公共void服务(ServletRequest arg0,ServletResponse arg1)
引发ServletException,IOException {
//做点什么
数数;
}
}不幸的是,上面的代码不是线程安全的,因为count不是原子操作。实际上,它包含三个独立的操作:读取count的值,将值加1,然后将计算结果写入count。如果线程A读取计数为10,线程B立即读取计数为10,线程A加1写入后为11。线程B已经将计数值读取为10,但是在加上1写入后仍然是11,因此丢失了一个计数。
在并发编程中,这种由于执行顺序不当而导致的不正确结果是一种非常重要的情况,它有一个正式的名称:竞态条件。最常见的竞争条件是“执行前检查”操作,也就是说,下一个操作由可能无效的观察结果决定。
延迟初始化是竞态条件的常见情形:
公共类LazyInitRace {
私有SomeObject实例=null
公共SomeObject getInstance() {
if(instance==null)
instance=new SomeObject();
返回实例;
}
}包含LazyInitRace中的竞态条件:首先线程A判断实例为null,然后线程B判断实例也为null。之后线程A和线程B分别创建对象,这样对象被初始化两次,出现错误。
为了避免静态条件,当一个线程修改一个变量时,需要防止其他线程以某种方式使用该变量,以确保其他线程只能在修改操作完成之前或之后读取和修改状态,而不是在修改过程中。
在UnsafeCountingFactorizer的情况下,线程不安全的原因是count不是原子操作。我们可以使用原子类来确保加法操作是原子的。
这样类就是线程安全的了:
公共类CountingFactorizer实现Servlet {
private final atomic long count=new atomic long(0);
public long getCount() {
返回计数。get();
}
@覆盖
公共void服务(ServletRequest arg0,ServletResponse arg1)
引发ServletException,IOException {
//做点什么
count . incrementandget();
}
}AtomicLong是java.util.concurrent.atomic包中的原子变量类,可以实现原子的自动递增操作,所以是线程安全的。
以上是什么java类是线程安全的细节。更多请关注我们的其他相关文章!
郑重声明:本文由网友发布,不代表盛行IT的观点,版权归原作者所有,仅为传播更多信息之目的,如有侵权请联系,我们将第一时间修改或删除,多谢。