Java/Java 中的 synchronized 关键字

Java/Java 中的 synchronized 关键字

synchronized 关键字是为了解决线程安全问题的。我们先来了解下解决线程安全问题的主要诱因。线程安全问题的主要诱因有两点: - 存在共享数据(也称为临界资源) - 存在多条线程共同操作这些共享数据

要解决线程安全问题,只有一个办法:保证同一时刻有且只有一个线程在操作共享数据,其他线程必须等待该线程处理完数据后再对共享数据进行操作。

因此引出了互斥锁。互斥锁有两个特性: - 互斥性:在同一时间只允许一个线程持有某个对象锁,通过这种特性来实现多线程的协调机制,这样在同一时间只有一个线程对需要同步的代码块进行访问。互斥性也称为操作的原子性 - 可见性:必须确保在锁被释放之前,对共享变量所做的修改,对于随后获得该锁的另一个线程是可见的(即在获得锁是应该获得最新共享变量的值),否则另一个线程可能实在本地缓存的某个副本上继续操作,从而引起不一致 synchronized 就是满足以上两点的互斥锁,synchronized 锁的不是代码,锁的都是对象。根据获取锁的方式可以分为两种: - 获取对象锁。有两种写法: - 同步代码块synchronized(this),synchronized(类实例对象)。锁的都是小括号()中的对象 - 同步非静态方法(synchronized method),锁的是当前类的实例对象 - 获取类琐。有两种写法: - 同步代码块 synchronized(类名.class),锁的是小括号()中的类对象(Class 对象) - 同步静态方法 synchronized static method,锁的是当前类的类对象(Class 对象) 可以看出类琐实际上也是通过对象锁(Class 对象)实现的。但是一个类只有一个 Class 对象,所以同一个类的不同对象使用类琐将是同步的 类琐和对象实例的锁是互不干扰的。 同一个类的不同对象的锁互不干扰。

下面是一个例子

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
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
public class SyncThread implements Runnable {
@Override
public void run() {
String threadName = Thread.currentThread().getName();
if (threadName.startsWith("A")) {
async();
} else if (threadName.startsWith("B")) {
syncObjectBlock1();
} else if (threadName.startsWith("C")) {
syncObjectMethod1();
}
}

//异步方法
private void async() {
try {
System.out.println(Thread.currentThread().getName() + "- Async Start");
Thread.sleep(1000);
System.out.println(Thread.currentThread().getName() + "- Async End");
} catch (InterruptedException e) {
e.printStackTrace();
}
}

/**
* 方法中有 synchronized(this|object){} 同步代码块
*/
private void syncObjectBlock1() {
System.out.println(Thread.currentThread().getName() + "- syncObjectBlock1");
synchronized (this) {
try {
System.out.println(Thread.currentThread().getName() + "- syncObjectBlock1 Start");
Thread.sleep(1000);
System.out.println(Thread.currentThread().getName() + "- syncObjectBlock1 End");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}

/**
* synchronized 修饰非静态方法
*/
private synchronized void syncObjectMethod1() {
System.out.println(Thread.currentThread().getName() + "- syncObjectMethod1");
try {
System.out.println(Thread.currentThread().getName() + "- syncObjectMethod1 Start");
Thread.sleep(1000);
System.out.println(Thread.currentThread().getName() + "- syncObjectMethod1 End");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}

public class SyncDemo {
public static void main(String[] args) {
SyncThread syncThread=new SyncThread();
Thread A_thread1=new Thread(new SyncThread(),"A_thread1");
Thread A_thread2=new Thread(new SyncThread(),"A_thread2");

Thread B_thread1=new Thread(new SyncThread(),"B_thread1");
Thread B_thread2=new Thread(new SyncThread(),"B_thread2");

Thread C_thread1=new Thread(new SyncThread(),"C_thread1");
Thread C_thread2=new Thread(new SyncThread(),"C_thread2");

A_thread1.start();
A_thread2.start();
B_thread1.start();
B_thread2.start();
C_thread1.start();
C_thread2.start();
}
}
上述 6 个线程虽然调用了synchronized代码块或者方法,但是它们之间并没有互斥关系,原因是每个线程锁的不是同一个对象。 把SyncDemo类做如下修改后,就可以实现线程互斥
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public class SyncDemo {
public static void main(String[] args) {
SyncThread syncThread=new SyncThread();
Thread A_thread1=new Thread(syncThread,"A_thread1");
Thread A_thread2=new Thread(syncThread,"A_thread2");

Thread B_thread1=new Thread(syncThread,"B_thread1");
Thread B_thread2=new Thread(syncThread,"B_thread2");

Thread C_thread1=new Thread(syncThread,"C_thread1");
Thread C_thread2=new Thread(syncThread,"C_thread2");

A_thread1.start();
A_thread2.start();
B_thread1.start();
B_thread2.start();
C_thread1.start();
C_thread2.start();
}
}
每个线程使用的都是同一个 Runnable 对象,所以锁的也是同一个对象。 如果synchronized修饰的是静态方法或者SyncThread.class,那么无论传给ThreadRunnable的同一个对象还是不同的对象,线程执行到synchronized代码块或者方法中都会互斥,也就是只有一个线程能够进入synchronized代码块或者方法中。

评论