我们的多线程环境,在不同的(不可预测的)调度顺序之下,线程争相运行,走走停停,这是一种异步状态。
而同步,虽然叫做同步,但其实是一个一个挨着执行,所以是一种串行。
而由于多个线程对于状态的异步访问可能导致安全问题。
比如:
if( 朋友A不在图书馆 ) {
------------------------------------------- A来到图书馆
去宿舍找A
}
再比如单例:
if( instance == null) {
------------------------------------ 切换到另一个线程,另一个线程创建了一个实例
...new instance;
}
我们不难发现,由于线程的切换,会导致我们不希望被打断的操作出现错误。
这就是我理解的原子性,原子性指的是不可分割,那么我们不希望被打断的(操作不可能被打断,意味着不会切换线程,当前线程对这个原子操作要么就执行完,要么就一点都不执行)
比如:
i++
这个操作在Java中不是原子的,它的执行过程是,先读出i,然后加1,然后写入i
假设在读出之后,写回之前,另一个线程也读取了,那么就可能出现错误。因为如果这是一个计数器,那么就会少记一次
再比如,要得到偶数
i++
--------------------------另一个线程来了
i++
就会得到奇数。
所以,我们的程序要注意,哪些地方是必须要保证一致的,不可变的,需要保证原子操作的,根据定义和功能的不同一定要尽可能仔细。
所以,安全性被被破坏很多时候就是在一个线程应该独占操作的一段区间内,有别的操作插入进来执行了。
所以,我们只能想办法来保证这个县城==线程独占一段操作,即同步。(即将这段操作变为原子的——不可打断的)
__________________________________________________________________________________________________________________________
同步主要实现有synchronized volatile 显示锁 原子变量等
synchronized:
比如:
public synchronized int add(){
return i++;
}
synchronized 关键字有两种用法:synchronized 方法 synchronized 块
方法开始执行,线程就就独占该锁,直到从该方法返回时释放,被阻塞的线程才可以获得锁,进入方法执行。确保了同一时刻对于每一个类实例,其所有声明为 synchronized 的成员函数中至多只有一个处于可运行状态,从而有效避免了类成员变量的访问冲突(只要所有可能访问类成员变量的方法均被声明为 synchronized)
除了类实例,每一个类也对应一把锁,我们也可以将类的静态成员函数声明为 synchronized ,以控制其对类的静态成 员变量的访问。
每个Java对象都可以用作一个视线同步的锁,这些所被称为内置锁。所以当一个线程获取某个对象的锁的时候,其他线程无法获取所有同一个对象的锁(看作是同一个锁)
但是! 可以重入同一个锁,即在一个对象的锁内,可以访问同一个对象的锁的另外的同步代码块
线程进入同步代码块的时候自动获得锁,退出的时候,自动释放锁。
对于同步代码块仍然有:
1 当两个并发线程访问synchronized(obj)同步代码块时,一个时间内只能有一个线程得到执行。另一个线 程必须等待当前线程执行完这个代码块以后才能执行该代码块。
2 当一个线程访问synchronized(obj)同步代码块时,另一个线程仍然可以访问该obj非synchronized 同步代码块。
3 当一个线程访问synchronized(obj)同步代码块时,其他线程对object中所有其它synchronized 同步代码块的访问将被阻塞。
________________________________________________________________________________________________________________________________
volatile:
这是一种稍弱的同步机制,注意,不是说i的声明加上volatile,那么在i++的时候就不会被打断了,它不会执行加锁操作。而应该是用来维护可见性。一个变量随时可能被放在看不见的缓存,所以,这个关键字最大的作用是在读取volatile变量的时候保证是最新的值。
Java的内存机制允许编译器和处理器对操作进行重排序,而且缓存数据。而volatile就会提醒编译器,编译器不会对这个变量的相关操作和其他操作重排序,而且不会放入缓存。、
因为比如
while( 条件 )
中断
当条件变量更新的时候可能是不可见的,无法捕捉到,所以可以声明为volatile。
—————————————————————————————————————————————————————————————————————————————
原子变量
当有多个线程同时执行这些类的实例包含的方法时,具有排他性,即当某个线程进入方法,执行其中的指令时,不会被其他线程打断,而别的线程就像自旋锁一样,一直等到该方法执行完成,才由JVM从等待队列中选择一个另一个线程进入,这只是一种逻辑上的理解。实际上是借助硬件的相关指令来实现的,不会阻塞线程
比如:AtomicLong i = new AtomicLong( 0 );
count.incrementAndGet();
这个递增操作就被同步了(不能被打断了)