Java并发编程重要总结(一)
1. 并发编程的目的
解决如何通过同步来避免多个线程在同一时刻访问相同数据的问题。
2. 线程和进程的区别
a.进程是资源分配的单位,线程是资源调度的基本单位
b.进程之间可以并发执行,在同一个进程中多个线程也可以并发执行
c.一个程序至少有一个进程,一个进程至少有一个线程
d.不同的进程使用不同的内存空间,所有线程共享一片内存空间。每个线程都拥有单独的栈来存储本地数据。
3. 同步和异步
为什么需要同步?
多个线程在执行时要和其他线程共享数据,需要同步机制。
同步:正在写的程序可能被另一个程序读到,这些数据是共享数据,必须进行同步存取
异步:当程序调用了一个要长时间执行的方法,不希望别的程序等待方法的返回应该用异步。
4. 什么是线程安全?
当多个线程访问某个类的时候,无论采用何种调度方式,这个类总是表现出正确的行为,那么这个类是线程安全的。
5. 锁机制
sychronized修饰的叫同步代码块,也叫互斥锁。
Java提供内置的同步代码块,分为两部分,一个是作为锁的对象引用(定义放进锁保护的都是什么东西),一个是这个锁保护的代码块(放进锁保护的东西)。
内置锁,理解为互斥锁最多只能有一个线程能持有这种锁,A占有锁时,A想要获取的话必须等待或阻塞,直到B完成释放锁。
获得内置锁的唯一途径就是进入这个锁保护的同步代码块或方法。
内置代码块.
sychronized( 锁<其实就是一个对象> ){
代码块;
}
也就是说: 同步代码块=内置锁 + 代码块
一种常见的加锁约定,将所有的可变状态都封装在对象内部,并通过对象的内置锁对所有访问可变状态的代码路径进行同步,使得在该对象上不会发生并发访问。
其实,加锁的含义不仅仅局限于互斥行为,还包含内存可见性,为了确保所有线程都能看到共享变量的最新值,这是因为:同一个锁上同步接触到的是相同的内存数据。
6. 同步
加锁是对资源而言的,如果我们使用同步呢?
访问共享数据的可变数据时,会需要同步。
如果在单线程内访问数据,不需要同步。
我们为了不让它同步,因此不共享数据
这种技术被称为线程封闭,线程封闭技术的另一种常见应用是JDBC的Connection对象。说到JDBC就说到了连接池。线程从连接池中获得一个Connection对象,并且用该对象来处理请求,使用完毕后再讲对象返还给连接池,大多数请求都是由单个线程采用同步方式来处理,并且在Connnection对象返回之前,连接池不会再将它分配给其他线程,因此,这种连接管理模式在处理请求时隐含的将Connection对象封闭在线程中。
7. 线程封闭
怎么来进行线程封闭呢?
线程封闭的维护可以用ThreadLocal。ThreadLocal提供了get和set等访问接口或方法,这些方法为每个使用该变量的线程都存有一份资源副本,因此,get总是返回当前执行线程在调用set时设置的最新值。
**不可变对象**的三个基本条件(创建后状态不能更改,所有域是final类型,正确创建的对象)
对象中有不可变对象,可变对象和事实不可变对象,不正确的对象发布时正确的对象破坏。
**Java内存模型为不可变对象的共享提供了一种特殊的初始化安全性保证**。一方面可以用同步,另一方面必须满足不可变性的所有需求:状态不可修改,所有域都是final类型,以及正确的构造过程。这种保证还将延伸到被正确创建对象中所有final类型的域。在没有额外同步的情况下,也可以安全地访问final类型的域。然而,如果final类型的域所指向的是可变对象,那么在访问这些域所指向的对象的状态时仍然需要同步。
**可变对象和事实不可变对象这种状态会变化的对象怎么实现安全发布呢?**
对于**事实不可变对象**:在没有额外的同步的情况下,任何线程都可以安全地使用被安全发布的事实不可变对象。
8. 并发程序中共享对象的策略
A.线程封闭
线程封闭的对象只能由一个线程拥有,对象呗封闭在该线程中并且只能由这个线程修改
B.只读共享
共享的对象只能可以由多个线程并发访问,但任何线程都不能修改它。
C.线程安全共享
线程安全的对象在其内部实现同步,因此多个线程可以通过对象的公有接口来进行访问
D.保护对象
被保护的对象只能通过特定锁来访问。
除了共享策略,针对线程并发还可以用同步策略,将数据封装在对象内部,可以将数据的访问限制在对象的方法上,从而更容易确保线程在访问数据时总能持有正确的锁。
Java监视器模式,遵从该模式的对象会把对象的所有可变状态封装起来,由对象内置的锁保护。