上一篇线程(一)——线程基础中介绍了一些线程相关的基础知识,比如:线程的定义、线程的使用方法、线程的状态、并发和并行的概念等。说道并发呢,我们自然就会想到线程安全这个问题了,这一篇就讲一讲什么是线程安全,以及如何保证线程安全的。
一、什么是线程安全
线程安全指的是在多个线程并发访问一个数据源时,不会出现数据不一致的情况或者数据污染。线程不安全就是,有可能多个线程先后更改数据造成所得到的的数据是脏数据的情况。
比如,票务系统,由于买票的人太多,单线程处理的话效率太慢,所以需要多线程处理。如果系统不是数据安全的就可能出现一票多卖的情况。一张票通过线程A卖出去后,由于数据不同步,紧接着线程B又把这张票卖了一次,甚至还会有其它线程也卖过这张票。到时就会出现N个人拿着同一个座位票的情况。
那么,如何保证线程安全呢?方法是使用synchronized同步机制和同步锁机制(也叫做Lock同步机制)。在本文中只介绍synchronized同步机制。
二、synchronized同步机制。
synchronized是一个修饰词,它可以修饰方法和代码块。被synchronized修饰的方法和代码块分别叫做同步方法和同步代码块。同步方法和同步代码块同一时刻只能由一个线程执行。只有等这个线程执行完了这个同步方法或者同步代码块,其它线程才有机会获得锁,并执行同步方法和同步代码块。注意:这个锁和同步锁机制中的同步锁不是一个概念,等下会详细说明。
学习synchronized同步机制需要先了解几个关键概念:
1、同步方法
同步方法指的是被synchronized修饰词修饰的方法,可以是静态方法也可以是非静态方法,如下:
//静态同步方法
public synchronized static method1() {}
//非静态同步方法
public synchronized method2() {}
2、同步代码块
同步代码块指的是被synchronized修饰词修饰的一段代码,如下:
public void method() {
……
synchronized(this/Test.class) {
}
}
synchronized修改关键字修饰代码块的时候后面会有一个括号,扩号里面用来指明锁,只有拿到这个锁的线程才能进入到同步代码块并执行这些同步代码。这个锁可以是一个实例对象也可以是一个类的class对象。前者我们成为对象锁,后者称为类锁。
3、锁
首先说明一下这个锁和上面提到的同步锁机制中的同步锁不是一个概念。这里的锁准确的说应该叫做monitor,java源码中的注释就是称作monitor的,不知道为什么被叫成了锁。可能是因为一个线程获取了这个monitor后,其它的线程都无法进入到同步方法和同步代码块里面,monitor的作用与锁很类似,所以就称作锁吧。
java中规定,每个对象都有独有的一个monitor。而一个类除了拥有普通的实例对象外,还有一个特殊的对象,那就是Class对象。我们知道一个类只有一个Class对象,Object中关于getClass()方法的注释如下:
/**
* Returns the runtime class of this {@code Object}. The returned
* {@code Class} object is the object that is locked by {@code
* static synchronized} methods of the represented class.
* ......
*/
public final Class<?> getClass() {
return shadow$_klass_;
}
出于Class