对synchronized的理解

  1. 1. synchronized:同步锁(互斥锁):
  2. 2. 概述
  3. 3. 区别
    1. 3.1. 1. synchronized关键字:
    2. 3.2. 2.synchronized代码块
    3. 3.3. 3. 与Lock的区别:
    4. 3.4. 4. static synchronized:
  4. 4. 案例:
  5. 5. 举个例子:
  6. 6. 结论:

​ 一旦一个包含锁定方法(用synchronized修饰)的线程被CPU调用,其他线程就无法调用相同对象的锁定方法。当一个线程在一个锁定方法内部,所有试图调用该方法的同实例的其他线程必须等待

synchronized:同步锁(互斥锁):

在java语言中,引入了同步锁的概念,每个对象都有一个与之关联的内部锁(排他锁),用以保证共享数据的安全性问题。

关键词synchronized用来给某个方法或某段代码加上一个同步锁。

当调用者调用此方法时,必须获得这把锁才可以调用。

当某个调用者获得这把锁之后,其他调用者就无法获得了。

当调用结束后,调用者释放这把锁,此时其他调用者才可以获得。

这个机制保障了某个同步方法同时只能有一个调用者。

synchronized 和 static synchronized的区别

概述

通过分析这两个用法的分析,我们可以理解java中锁的概念。一个是实例锁(锁在某一个实例对象上,如果该类是单例,那么该锁也具有全局锁的概念),一个是全局锁(该锁针对的是类,无论实例多少个对象,那么线程都共享该锁)。实例锁对应的就是synchronized关键字,而类锁(全局锁)对应的就是static
synchronized(或者是锁在该类的class或者classloader对象上)。

区别

1. synchronized关键字:

synchronized作用是对类的当前实例(对象)加锁。可以使用synchronized关键字来标记一个方法或者代码块,当某个线程调用该对象的synchronized方法或者访问synchronized代码块时,这个线程便获得了该对象的锁,其他线程暂时无法访问这个方法,只有等待这个方法执行完毕或者代码块执行完毕,这个线程才会释放该对象的锁(Java 并发编程)。

2.synchronized代码块

【synchronized(synObject)】使用起来比synchronized方法要灵活得多。因为也许一个方法中只有一部分代码只需要同步,如果此时对整个方法用synchronized进行同步,会影响程序执行效率。而使用synchronized代码块就可以避免这个问题(同步对象或类属性),synchronized代码块可以实现只对需要同步的地方进行同步。

3. 与Lock的区别:

  1. synchronized是Java语言的关键字,因此是内置特性,Lock是一个类(java.util.concurrent.locks包),通过这个类可以实现同步访问;
  2. synchronized不需要用户去手动释放锁,当synchronized方法或者synchronized代码块执行完之后,系统会自动让线程释放对锁的占用。Lock则必须要用户去手动释放锁,如果没有主动释放锁,就有可能导致出现死锁现象。

4. static synchronized:

​ 每个类有一个锁,它可以用来控制对static数据成员的并发访问。访问static synchronized方法占用的是类锁,而访问非static synchronized方法占用的是对象锁。

​ static synchronized控制类的所有实例(对象)的访问(相应代码块)。synchronized相当于 this.synchronized,static synchronized相当于Something.synchronized

案例:

一个日本作者-结成浩的《java多线程设计模式》有这样的一个列子:

1
2
3
4
5
6
7
8
9
10
11
pulbic class Something(){

public synchronized void isSyncA() {}

public synchronized void isSyncB() {}

public static synchronized void cSyncA() {}

public static synchronized void cSyncB() {}

}

​ 那么,假如有Something类的两个实例x与y,那么下列各组方法被多线程同时访问的情况是怎样的?

a. x.isSyncA()与x.isSyncB()

b. x.isSyncA()与y.isSyncA()

c. x.cSyncA()与y.cSyncB()

d. x.isSyncA()与Something.cSyncA()

这里,很清楚的可以判断:

​ 都是对同一个实例(x)的synchronized域访问,因此不能被同时访问。(多线程中访问x的不同synchronized域不能同时访问)

​ 如果在多个线程中访问x.isSyncA(),因为仍然是对同一个实例,且对同一个方法加锁,所以多个线程中也不能同时访问。(多线程中访问x的同一个synchronized域不能同时访问)

​ 是针对不同实例的,因此可以同时被访问(对象锁对于不同的对象实例没有锁的约束)

​ 因为是static synchronized,所以不同实例之间仍然会被限制,相当于Something.isSyncA()与 Something.isSyncB()了,因此不能被同时访问。

​ 书上的答案是可以被同时访问的,答案理由是synchronzied的是实例方法与synchronzied的类方法由于锁(lock)不同的原因。

​ 个人分析也就是synchronized 与static synchronized 相当于两帮派,各自管各自,相互之间就无约束了,可以被同时访问。

举个例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
public class TestSynchronized {
public synchronized void test1() {
int i = 5;
while (i-- > 0) {
System.out.println(Thread.currentThread().getName() + " : " + i);
try {
Thread.sleep(500);
} catch (InterruptedException ie) {
}
}
}

public static synchronized void test2() {
int i = 5;
while (i-- > 0) {
System.out.println(Thread.currentThread().getName() + " : " + i);
try {
Thread.sleep(500);
} catch (InterruptedException ie) {
}
}
}

public static void main(String[] args) {
final TestSynchronized myt2 = new TestSynchronized();
Thread test1 = new Thread(new Runnable() {
public void run() {
myt2.test1();
}
}, "test1");
Thread test2 = new Thread(new Runnable() {
public void run() {
TestSynchronized.test2();
}
}, "test2");
test1.start();
test2.start();
}
}

测试结果:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
test1 : 4

test2 : 4

test1 : 3

test2 : 3

test2 : 2

test1 : 2

test2 : 1

test1 : 1

test1 : 0

test2 : 0

上面代码synchronized同时修饰静态方法和实例方法,但是运行结果是交替进行的,这证明了类锁和对象锁是两个不一样的锁,控制着不同的区域,它们是互不干扰的。同样,线程获得对象锁的同时,也可以获得该类锁,即同时获得两个锁,这是允许的。

结论:

A: synchronized static是某个类的范围,synchronized static cSync{}防止多个线程中多个实例同时访问这个类中的synchronized static 方法。它可以对类的所有对象实例起作用。

B: synchronized 是某实例的范围,synchronized isSync(){}防止多个线程中这一个实例同时访问这个类的synchronized 方法。

其实总结起来很简单。

一个锁的是类对象,一个锁的是实例对象。

若类对象被lock,则类对象的所有同步方法全被lock;

若实例对象被lock,则该实例对象的所有同步方法全被lock。