前言
上一篇我们介绍Java内存模型来处理有序性,可见性的问题。但是,还有一个原子性的问题,没有处理,那么针对原子性的问题我们该怎么处理呢?我们知道在并发编程中的原子性问题主要原因就是,一条高级语句可能会被分成多个CPU指令,在指令执行完之后发生了线程切换,中间状态被暴露造成原子性问题。
锁
现实生活中,我们用自己的锁来保护自己的财产,买门票来锁定演唱会的座位。
同理,在并发编程的世界里我们同样可以引入锁的概念来锁住需要保护的资源。只有获得了锁的线程才能操作资源。
synchronized
Java自带的锁工具是synchronized,用synchronized修饰的代码就相当于上了锁。上了锁就需要互斥执行。即:同一时刻只能有一个线程执行。
我们将一段需要互斥执行的代码称之为临界区。
例如:
synchronized (this) {
this.a = 100;
System.out.println("*******test2执行");
}
如上程序:代码System.out.println("*******test2执行");被synchronized修饰,故此代码被称之为临界区。而a这是受保护的资源。其关系图如下:
synchronized的运用
synchronized 可以修改方法,修饰代码块。使用如下:
class SynchronizedTest {
public synchronized void test1() {
System.out.println("*******test1执行");
}
public void test2() {
synchronized (this) {
System.out.println("*******test2执行");
}
}
public synchronized static void test3() {
System.out.println("*******test3执行");
}
}
当synchronized修饰实例方法时,锁定的就是当前实例对象this。如方法test1所示。
当synchronized修饰代码块块时,锁定的就是括号里的对象。如方法test2所示。
当synchronized修饰静态方法时,锁定的就是当前类的class对象,如方法test3所示。
锁与受保护资源的关系
在现实生活中,我们可以通过通过一把锁保护多个东西,例如,用一把大门的锁,保护你家里面的所有东西。同样的,你一个给一个东西加上两把锁。但是,在并发编程中,同一个资源只能由一把锁保护,一把锁可以保护多个资源。故,并发编程中,锁与受保护资源的关系是1:N。例如:
public class SynchronizedTest3 {
int a = 0;
int b = 0;
static int c = 0;
/**
* 锁定的是this对象,保护了,a,b两个资源
*/
synchronized void setValue() {
a = 100;
b = 20;
}
/**
* 锁定的是SynchronizedTest3的class对象,
* 保护了资源c
* @return
*/
synchronized static int getValue() {
return SynchronizedTest3.c;
}
}
如上程序,synchronized修饰的setValue方法中有a,b两个资源,因为这两个资源都所属与this对象,所以都可以受到synchronized的保护。
而synchronized修饰的getValue方法中只有资源c,而这个c是一个静态变量,属于SynchronizedTest3类,所以它也可以受到保护。
锁如何保护多个资源
多个资源没关联
如果多个资源没有关联的话,我们可以用多个不同的锁来保护,例如:张三的东西用张三的锁,李四的东西用李四的锁。井水不犯河水。例如:
public class Account {
//取款保护锁
private final Object balLock = new Object();
。。。。。。。。。。。。。。。。。
版权原因,完整文章,请参考如下:Java并发编程(三)---synchonized解决原子性问题