线程安全问题的主要诱因
- 存在共享数据(也称临界资源)
- 存在多条线程共同操作这些共享数据
解决问题的根本方法:
同一时刻有且只有一个线程在操作共享数据,其他线程必须等到该线程处理完数据后再对共享数据进行操作
互斥锁的特性
-
互斥性:即在同一时间只允许一个线程持有某个对象锁,通过这个特性来实现多线程的协调机制 ,这样在同一时间只有一个线程对需要同步的代码块(复合操作)进行访问.互斥性又称为操作的原子性.
-
*可见性:8必须确保在锁被释放之前,对共享变量所做的修稿,对于随后获得该所的拧一个线程是可见的(即在获得锁时应当获得最新共享变量的值),否则另一个线程可能是在本地缓存的某一个副本上继续操作,从而额引起不一致.
根据获取的锁的分类
-
获取对象锁
- 1.同步代码块(synchronized(this),sychronized(类实例对象)),锁是小括号中的实例对象
- 2.同步非静态方法(synchronized method),锁是当前对象的实例对象
-
获取类锁
- 1.同步代码块(synchronized(类.class)),锁是小括号()中类对象(Class对象)
- 2.同步静态方法(synchronized static method),锁是当前对象的类对象(Class对象)
代码示例
SyncThread(线程类)
/**
* 不同的线程name属性,执行不同的run()方法
* @author panghu
*/
public class SyncThread implements Runnable {
@Override
public void run() {
String threadName = Thread.currentThread().getName();
//根据前缀判断执行的run()方法
if (threadName.startsWith("A")) {
async();
} else if (threadName.startsWith("B")) {
syncObjectBlock1();
} else if (threadName.startsWith("C")) {
syncObjectMethod1();
} else if (threadName.startsWith("D")) {
syncClassBlock1();
} else if (threadName.startsWith("E")) {
syncClassMethod1();
}
}
/**
* 异步方法,不需要获取锁
*/
private void async(){
try{
System.out.println(Thread.currentThread().getName() + "_Async_Start: "
+ new SimpleDateFormat("HH:mm:ss").format(new Date()));
Thread.sleep(1000);
System.out.println(Thread.currentThread().getName() + "_Async_End: "
+ new SimpleDateFormat("HH:mm:ss").format(new Date()));
}catch (InterruptedException e){
e.printStackTrace();
}
}
/**
* 方法中有 synchronized(this|object) {} 同步代码块
*
* 先进入方法,再获取对象锁
*/
private void syncObjectBlock1(){
System.out.println(Thread.currentThread().getName() + "_SyncObjectBlock1: "
+ new SimpleDateFormat("HH:mm:ss").format(new Date()));
synchronized (this){
try{
System.out.println(Thread.currentThread().getName() + "_SyncObjectBlock1_Start: "
+ new SimpleDateFormat("HH:mm:ss").format(new Date()));
Thread.sleep(1000);
System.out.println(Thread.currentThread().getName() + "_SyncObjectBlock1_End: "
+ new SimpleDateFormat("HH:mm:ss").format(new Date()));
}catch (InterruptedException e){
e.printStackTrace();
}
}
}
/**
* synchronized 修饰非静态方法
* 进入方法之前就需要获取对象锁
*/
private synchronized void syncObjectMethod1() {
System.out.println(Thread.currentThread().getName() + "_SyncObjectMethod1: "
+ new SimpleDateFormat("HH:mm:ss").format(new Date()));
try {
System.out.println(Thread.currentThread().getName() + "_SyncObjectMethod1_Start: "
+ new SimpleDateFormat("HH:mm:ss").format(new Date()));
Thread.sleep(1000);
System.out.println(Thread.currentThread().getName() + "_SyncObjectMethod1_End: "
+ new SimpleDateFormat("HH:mm:ss").format(new Date()));
} catch (InterruptedException e) {
e.printStackTrace();
}
}
/**
* 同步代码块(synchronized(类.class))
*/
private void syncClassBlock1() {
System.out.println(Thread.currentThread().getName() + "_SyncClassBlock1: "
+ new SimpleDateFormat("HH:mm:ss").format(new Date()));
synchronized (SyncThread.class) {
try {
System.out.println(Thread.currentThread().getName() + "_SyncClassBlock1_Start: "
+ new SimpleDateFormat("HH:mm:ss").format(new Date()));
Thread.sleep(1000);
System.out.println(Thread.currentThread().getName() + "_SyncClassBlock1_End: "
+ new SimpleDateFormat("HH:mm:ss").format(new Date()));
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
/**
* 同步静态方法(synchronized static method)
*/
private synchronized static void syncClassMethod1() {
System.out.println(Thread.currentThread().getName() + "_SyncClassMethod1: "
+ new SimpleDateFormat("HH:mm:ss").format(new Date()));
try {
System.out.println(Thread.currentThread().getName() + "_SyncClassMethod1_Start: "
+ new SimpleDateFormat("HH:mm:ss").format(new Date()));
Thread.sleep(1000);
System.out.println(Thread.currentThread().getName() + "_SyncClassMethod1_End: "
+ new SimpleDateFormat("HH:mm:ss").format(new Date()));
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
SyncDemo(测试类)
测试样例一
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");
//B1,B2,C1,C2共用同一个对象
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");
// Thread D_thread1 = new Thread(syncThread, "D_thread1");
// Thread D_thread2 = new Thread(syncThread, "D_thread2");
// Thread E_thread1 = new Thread(syncThread, "E_thread1");
// Thread E_thread2 = new Thread(syncThread, "E_thread2");
A_thread1.start();
A_thread2.start();
B_thread1.start();
B_thread2.start();
C_thread1.start();
C_thread2.start();
// D_thread1.start();
// D_thread2.start();
// E_thread1.start();
// E_thread2.start();
}
}
运行结果
A_thread1_Async_Start: 22:07:38
A_thread2_Async_Start: 22:07:38
C_thread1_SyncObjectMethod1: 22:07:38
B_thread1_SyncObjectBlock1: 22:07:38
B_thread2_SyncObjectBlock1: 22:07:38
C_thread1_SyncObjectMethod1_Start: 22:07:38
A_thread1_Async_End: 22:07:39
A_thread2_Async_End: 22:07:39
C_thread1_SyncObjectMethod1_End: 22:07:39
B_thread2_SyncObjectBlock1_Start: 22:07:39
B_thread2_SyncObjectBlock1_End: 22:07:40
B_thread1_SyncObjectBlock1_Start: 22:07:40
B_thread1_SyncObjectBlock1_End: 22:07:41
C_thread2_SyncObjectMethod1: 22:07:41
C_thread2_SyncObjectMethod1_Start: 22:07:41
C_thread2_SyncObjectMethod1_End: 22:07:42
结论:
线程A1,A2单独运行,不影响其他其他线程的运行,同样的其他线程(B1,B2,C1,C2)同样也不影响它们的单独运行.
当线程(B1,B2,C1,C2)其中一个(假设是C1)线程持有同一对象的对象锁之后,B1,B2,C2需要等C1释放对象锁之后才能进入sychronized修饰的模块
测试样例二
将持有的syncThread对象,改为new SyncThread(),这样每个线程拥有的对象就是不同的
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");
//B1,B2,C1,C2共用同一个对象
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");
// Thread D_thread1 = new Thread(syncThread, "D_thread1");
// Thread D_thread2 = new Thread(syncThread, "D_thread2");
// Thread E_thread1 = new Thread(syncThread, "E_thread1");
// Thread E_thread2 = new Thread(syncThread, "E_thread2");
A_thread1.start();
A_thread2.start();
B_thread1.start();
B_thread2.start();
C_thread1.start();
C_thread2.start();
// D_thread1.start();
// D_thread2.start();
// E_thread1.start();
// E_thread2.start();
}
}
运行结果:
A_thread2_Async_Start: 22:16:37
C_thread2_SyncObjectMethod1: 22:16:37
C_thread1_SyncObjectMethod1: 22:16:37
C_thread2_SyncObjectMethod1_Start: 22:16:37
A_thread1_Async_Start: 22:16:37
B_thread1_SyncObjectBlock1: 22:16:37
B_thread2_SyncObjectBlock1: 22:16:37
C_thread1_SyncObjectMethod1_Start: 22:16:37
B_thread1_SyncObjectBlock1_Start: 22:16:37
B_thread2_SyncObjectBlock1_Start: 22:16:37
A_thread2_Async_End: 22:16:38
A_thread1_Async_End: 22:16:38
C_thread2_SyncObjectMethod1_End: 22:16:38
C_thread1_SyncObjectMethod1_End: 22:16:38
B_thread2_SyncObjectBlock1_End: 22:16:38
B_thread1_SyncObjectBlock1_End: 22:16:38
结论:线程B1,B2,C1,C2所持有的对象锁不同,互不影响
测试样例三
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");
//B1,B2,C1,C2共用同一个对象
// 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");
Thread D_thread1 = new Thread(syncThread, "D_thread1");
Thread D_thread2 = new Thread(syncThread, "D_thread2");
Thread E_thread1 = new Thread(syncThread, "E_thread1");
Thread E_thread2 = new Thread(syncThread, "E_thread2");
A_thread1.start();
A_thread2.start();
// B_thread1.start();
// B_thread2.start();
// C_thread1.start();
// C_thread2.start();
D_thread1.start();
D_thread2.start();
E_thread1.start();
E_thread2.start();
}
}
运行结果:
A_thread1_Async_Start: 22:19:03
D_thread2_SyncClassBlock1: 22:19:03
D_thread1_SyncClassBlock1: 22:19:03
E_thread1_SyncClassMethod1: 22:19:03
A_thread2_Async_Start: 22:19:03
E_thread1_SyncClassMethod1_Start: 22:19:03
A_thread1_Async_End: 22:19:04
A_thread2_Async_End: 22:19:04
E_thread1_SyncClassMethod1_End: 22:19:04
D_thread1_SyncClassBlock1_Start: 22:19:04
D_thread1_SyncClassBlock1_End: 22:19:05
D_thread2_SyncClassBlock1_Start: 22:19:05
D_thread2_SyncClassBlock1_End: 22:19:06
E_thread2_SyncClassMethod1: 22:19:06
E_thread2_SyncClassMethod1_Start: 22:19:06
E_thread2_SyncClassMethod1_End: 22:19:07
结论:当线程(D1,D2,E1,E2)其中一个(假设是E1)线程持有同一对象的对象锁之后,D1,D2,E2需要等E1释放类锁之后才能进入sychronized修饰的模块.
测试样例四
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");
//B1,B2,C1,C2共用同一个对象
// 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");
Thread D_thread1 = new Thread(new SyncThread(), "D_thread1");
Thread D_thread2 = new Thread(new SyncThread(), "D_thread2");
Thread E_thread1 = new Thread(new SyncThread(), "E_thread1");
Thread E_thread2 = new Thread(new SyncThread(), "E_thread2");
A_thread1.start();
A_thread2.start();
// B_thread1.start();
// B_thread2.start();
// C_thread1.start();
// C_thread2.start();
D_thread1.start();
D_thread2.start();
E_thread1.start();
E_thread2.start();
}
}
运行结果
A_thread2_Async_Start: 22:23:59
A_thread1_Async_Start: 22:23:59
D_thread1_SyncClassBlock1: 22:23:59
E_thread1_SyncClassMethod1: 22:23:59
D_thread2_SyncClassBlock1: 22:23:59
E_thread1_SyncClassMethod1_Start: 22:23:59
A_thread2_Async_End: 22:24:00
A_thread1_Async_End: 22:24:00
E_thread1_SyncClassMethod1_End: 22:24:00
D_thread2_SyncClassBlock1_Start: 22:24:00
D_thread2_SyncClassBlock1_End: 22:24:01
D_thread1_SyncClassBlock1_Start: 22:24:01
D_thread1_SyncClassBlock1_End: 22:24:02
E_thread2_SyncClassMethod1: 22:24:02
E_thread2_SyncClassMethod1_Start: 22:24:02
E_thread2_SyncClassMethod1_End: 22:24:03
结论:同上述示例3 的结论,将syncThread
修改为new SyncThread()
获取的依旧是同一个类对象,所以结论相同.
对象锁和类锁的总结
- 1.有线程访问对象的同步代码块时,另外的线程可以访问该对象的非同步代码块;
- 2.若锁住的是同一个对象,一个线程在访问对象的
同步代码块
时,另一个访问对象的同步代码块
的线程会被阻塞; - 3.若锁住的是同一个对象,一个线程在访问对象的
同步方法
时,另一个访问对象的同步方法
线程会被阻塞; - 4.若锁住的是同一个对象,一个线程在访问对象的
同步代码块
时,另一个访问对象的同步方法
的线程会被阻塞,反之亦然
; - 5.同一个类的不同对象的对象锁互不干扰;
- 6.类锁由于也是一种特殊的对象锁,因此和上述的1,2,3,4一致,而由于一个类只有一把对象锁,所以一个类使用类锁将会是同步的;
- 7.类锁和对象锁互补干扰.(忘了演示,😳)
网友评论