1、java并发编程实践(1)

来源:互联网 发布:贷贷兴隆网络贷款 编辑:程序博客网 时间:2024/05/18 02:13

1、什么是线程安全?
java中共享数据存储在主存储区,当多个线程同时访问同一个资源时,线程会将这些共享资源赋值到自己的工作内存中,当执行完之后,将资源重新写入主存区,如果多个线程操作同一个共享资源而没有出现不可预知的错误,即认为是线程安全的。
2、原子性、竞态条件?
原子性:一个或多个操作,执行过程中不会被中断,全部执行成功,要么全都不执行。
竞态条件:并发环境中由于不恰当的执行时序而出现不正确的结果。常见的竞态条件:“先检查后执行”、“读-修改-写”
先检查后执行: if(obj==null) obj=new Object(); 当线程A,B并发执行该程序时,线程A判断为null,创建obj对象过程中,
线程B判断对象也为null,也创建obj对象,此时创建了两个obj对象,并不是我们所期望的到得;
读-修改-写:线程A和线程B同时操作共享变量value,线程A读取value修改它的值时,线程B也读取它的值并修改,线程A和B又将value写入内存,此时Value的值可能不是线程A的期望值;
**基本类型存在原子变量类:**AtomicInteger、AtomicBoolean、AtomicLong这些类里面的方法能保证变量的某些操作是原子性的;
eg: AtomicInteger a = new AtomicInteger(0);
a.getAndIncrement();等价于a++ 能保证是原子操作;
3、内置锁/互斥锁(synchronized)
内置锁相当于一种互斥体,最多只能有一个线程持有当前对象的内置锁;当线程A持有锁时,线程B试图去获取锁,那么线程B必须等待或者阻塞,知道线程A释放它持有的锁。内置锁容易出现“死锁”问题,比如当线程A持有资源A的锁,线程B持有资源B的锁,而线程A想要访问资源B,线程B又想访问资源A,A,B线程不会释放所知道获取其他资源,从而导致线程A,B都进入等待或阻塞状态,构成死锁。解决死锁的方法:可以使用ReentranLock的轮询锁,该锁能够在未获得资源时,会释放线程持有的锁然后在下次尝试再一次获取。
4、锁重入
当一个线程试图获取一个被其它线程持有的锁时,该线程会进入一个阻塞状态,内置锁是可重入的,如果一个线程试图获取一个当前他已经持有的锁,那么请求就会成功。重入的意义:eg子类重写父类的synchronized方法,在子类的方法中调用父类的synchronized方法,如果锁不可重入,那么就会出现死锁的情况。
5、内存可见性
当一个线程改变了某个对象的状态后,其它线程能看到该对象状态发生的状态变化。同步能够保证内存可见性。
6、重排序
处理器或者编译器对不存在数据依赖性的指令操作进行重新排序。
数据依赖性:如果两个操作访问同一个变量,其中一个操作为写操作,那么我们认为这个操作之间存在依赖性,存在数据依赖性的操作,编译器或处理器不会对其进行重排序;
7、volatile变量
关键字volatile修饰的变量具有内存可见性,变量被volatile修饰后,编译器或处理器不会将对该变量上的操作与其他内存操作一起重排序。volatile不能保证操作的原子性。(简单理解,volatile变量在主存中,线程的有自己的工作内存,volatile变量不会被复制到工作内存,线程对其的操作直接在主存中进行,从而保证每个线程都能看到最新的值)。
8、线程封闭
访问共享变量需要同步,避免使用同步的方式就是不共享数据,这种技术称为线程封闭。ad-hoc线程封闭,栈封闭,ThreadLocal封闭。
9、ThreadLocal类
内部实现类似Map,将线程与共享变量副本储存在缓存区,每个线程都拥有一份变量副本,使共享变量被独自占有而不被共享,这样就确保了线程安全性。
ThreadLocal底层实现:每一个线程创建ThreadLocal对象,每个线程拥有一个threadLocalMap属性,ThreadLocal类有个内部类ThreadLocalMap,键key=”当前线程创建的threadlocal实例”,值value=“共享变量副本”,ThreadLocalMap内部Entry对象实现了WeakReference弱引用。

原创粉丝点击