《java并发编程实战》—— 第二章:线程安全性

本系列为本人在研读相关技术书籍后所总结之精华,希望能对大家有所帮助,有兴趣的可以加我好友,大家共同学习进步!

不同的程序都在单独的进程中运行,操作系统为各个独立的进程分配包括内存在内的各种资源,对大多数操作系统来说,都是以线程为基本调度单位。

第二章、线程安全性

原子操作:
public class P5 {
    public class UnsafeSequence{
        private int value;
        public int getNext(){
            return value++;//改操作不保证原子性
        }
    }
}

虽然操作count++看上去只是一个操作,但其并非原子的,以为它并不会作为一个不可分割的操作来执行,它包含了读取——修改——写入

什么是原子方式?

假定有两个操作A和B,如果从执行A的线程来看,当个另一个线程执行B时,要么将B全部执行完,要么完全不执行B,那么A和B对彼此来说就是原子的。

竞态条件:

由于不正确的时序而出现不正确的结果

最常见的竞态条件:先检查后执行(基于一种可能失效的观察结果判断)

以单例模式中的懒汉模式和饿汉模式为例子,首先单例模式的·核心要点在于只允许创建一个对象,相应地优点便是占用内存资源少。
代码特点:1.私有的从属于类的对象属性 2.私有的构造方法 3.公开的get对象方法

public class P17 {
    private static P17 p17= new P17();//饿汉模式,线程安全
    private P17(){};
    public P17 getP17(){
        return p17;
    }
}

class Lazy{//懒汉模式线程不安全,必须加锁
    private static Lazy lazy;
    private Lazy(){};
    public synchronized Lazy getLazy() {
        if (lazy == null) {
            lazy = new Lazy();
        }
        return lazy;
    }
}

在懒汉模式中

  if (lazy == null) {
            lazy = new Lazy();
        }

便是先检查后执行,若不加锁将可能出现多线程下创建多个实例的情况,这也就违背了单例模式的初衷。

内置锁:

java中提供了一种内置的锁机制来支持原子性:synchronize关键字:
1.同步代码块:包括两部分,锁的对象引用 和 锁保护的代码块

synchronize (lock){
	//由锁保护的内容
}

2.用synchronize修饰的方法:本质上就是横跨整个方法体的同步代码块,锁的对象就是调用方法的对象

锁的获得与释放:

释放锁:1.执行完代码块内容,正常退出 2.抛出异常
获得锁:获得内置锁的唯一途径就是进入这个锁保护的同步代码块或方法

并发环境中的原子性:

一组语句作为一个不可分割的单元被执行

可重入锁:

重入意味着获取锁的操作粒度是“线程”而不是“调用”。

重入的一种实现方法:

为每个锁关联一个获取计数值和一个所有者线程。当计数值为0时,这个锁就被认为是没有被任何线程持有。当线程请求一个未被持有的锁时,JVM将记下锁的持有者,并且将获取计数值置为1.如果同一个线程再次获取这个锁,计数值将递增,当线程退出同步代码块时,计数器会相应地递减。当计数值为0时,这个锁将被释放。

//P21
public class Widget{
    public synchronized void doSomething(){
        //...
    }
}

class loggingWidget extends Widget{
    public synchronized void doSomething() {
        System.out.println(toString()+": calling doSomething");
        super.doSomething();
    }
}

重入进一步提升了加锁行为的封装性,如果没有可重入锁,那么以上代码将产生死锁,重入避免了这种死锁情况的发生。

用锁来保护状态:

当某个变量用锁来保护时,意味着在每次访问这个变量时都需要首先获得锁,这样就确保了在同一时刻只有一个线程可以访问这个变量。当类的不变性条件涉及多个状态变量时,那么还有另一个需求:在不变性条件中的每个变量都必须由同一个锁来保护。因此可以在单个原子操作中访问或更新这些变量,从而确保不变性条件不被破坏。

if ( ! vector.contains(element) )
	vector.add(element) ;

我们知道vector是线程安全的,contains和add方法都是原子方法,但以上操作仍存在竞态条件。虽然synchroniz方法可以确保单个操作的原子性,但如果要把多个操作合并为一个复合操作,还是需要额外的加锁机制。

活跃性与性能

通过缩小同步代码块的作用范围,我们可以确保并发性,又保证效率。但注意同步块不要过小(出现线程安全问题)。不要将本应是原子的操作拆分到多个同步代码块中(在获取与释放锁等操作上都需要一定的开销,因此如果将同步代码块分解得过细会影响性能)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值