java并发编程实践学习(1)线程安全

编写线程安全的代码就是管理对线程中的数据
共享是指一个变量可以被多个线程访问
可变是指变量的值可以在其生命周期内改变


一.线程安全性**

线程安全是指多个线程在访问一个类时,如果不需要额外的同步,这个类的行为仍然是正确的。

1 无状态的对象永远是线程安全的

@ThreadSafe
public class StatelessFactorizer implements servlet{
    public void service (ServletRequest req,ServletResponse res){
    BigInteger i = extractFromrequest(req);
    BigInteger[] factors = factor(i);
    encodeIntoResponse(res,factors);
    }
}

它像大多数的servlet一样没有状态,它不包含域对象也没有引用其他的域对象。

2 原子性

原子性是指单独的,不可分割的操作。
原子操作是线程安全的。
介绍原子变量前首先介绍原子操作,假设有操作A和B,如果从执行A的角度看,当其他线程执行B时,要么全部执行完成,要么一点都没有执行,这样A和B互为原子操作。
原子操作是线程安全的。
例如,”读-改-写”操作是一个整体,我们需要它们作为一个整体执行,而在多线程的环境中它们可能被分割开来,

2.1 竞争条件

指多个线程或者进程在读写一个共享数据时结果依赖于它们指令执行的相对时序,即要想得到正确的结果,要依赖于幸运的时序。
最常见的竞争条件是“检查再运行”,使用一个潜在的过期值作为决定下一步操作的依据。

2.2 复合操作

不满足原子操作要求的操作被称为复合操作
JDK的Java.util.concurrent.atomic包中包括了原子变量类,这些类用来实现数字和对象引用的原子状态转换。z
为了保证线程安全,操作必须原子的执行,所以就引出了java内置的原子性机制—

2.3 锁

原子变量自身是线程安全的,但是如果一个不变约束涉及多个变量时,变量间不是彼此独立的,无论这些变量是否是原子变量都不能确保线程安全,而需要在同一个原子操作中更新这些相互关联的状态变量才能确保线程安全。

2.3.1 内部锁

java提供了强制原子性的内置锁机制:synchronized块。
一个synchronized块有俩部分:锁对象的引用和这个锁 保护的代码块。
synchronized方法的锁是该方法所在的对象本身,静态的synchronized方法的锁是从Class对象上获取的锁。
执行线程在静茹synchronized块之前自动获得锁,放弃对synchronized块事自动释放锁。
内部锁在java中扮演了互斥锁的角色,即至多只有一个线程可以拥有锁,没有获取到锁的线程只能等待或阻塞直到锁被释放,因此同步块可以线程安全

2.3.2 重进入

内部锁是可重进入的
可重入是指对于同一个线程,它可以重新获得已有它占用的锁。
可重入性意味着锁的请求是基于”每线程”而不是基于”每调用”,它是通过为锁关联一个请求计数器和一个占有它的线程来实现。当未被占有时,计数器的值为零,认为未被占有。当 线程请求一个未被占有的锁时,jvm将记录锁的拥有者并将计数器置为1。如果同一个线程再次请求这个锁,计数器递增,每次退出同步块计数器递减,直到计数器为零锁被释放。

2.4 用锁来保护状态

仅用synchronized块包装复合类型操作是不够的,当用同步来访问变量时每次访问都需要用同一个锁。
并不是所有的变量都需要锁保护, 只有那些被多个线程访问的可变数据需要上锁。
但是过多的同步会导致活跃度或者性能的问题。

2.5 活跃度与性能

如果同步快过大会导致程序的并发度下降导致性能的下降,所以那些不跨线程共享的代码和变量就不需要同步,但是如果同步快划分的过于琐碎也不合理,因为上锁和释放也会占用一部分资源。
有些耗时的操作比如:网络或控制台的I/O,难以快速完成执行这些操作期间不要占有锁。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章