Java线程:synchronized
一、synchronized
1.1 线程安全问题的主要诱因
- 存在共享数据,即临界资源
- 存在多条线程共同操作这些共享数据
所以我们程序代码需要保证同一时刻有且只有一个线程在操作共享数据,而其他的线程必须等待该线程处理完数据后再对共享数据操作
1.2 互斥锁特性
synchronized符合互斥锁特性,特别注意,synchronized锁的不是代码,是对象
-
互斥性:即在同一时间只允许一个线程持有某个对象锁,这种特性来实现多线程的协调机制,同一时间只有一个线程对需要同步的代码块进行访问,具有原子性
-
可见性:必须确保在释放锁之前,对共享变量所做的修改,对于随后获得该锁的另外一个线程是可见的,即获得锁时是获取到最新的共享变量的值
1.3 获取对象锁
- 同步代码块,如synchronized(this)、synchronized(类实例对象),锁是小括号()中的实例对象
- 同步非静态方法,如synchronized method,锁是当前对象的实例对象
SyncThread.java
public class SyncThread implements Runnable {
@Override
public void run() {
String threadName = Thread.currentThread().getName();
if(threadName.startsWith("A"))
async();
if(threadName.startsWith("B"))
syncObjectBlack();
if(threadName.startsWith("C"))
syncObjectMethod();
}
//异步方法
private void async(){
try {
System.out.println(Thread.currentThread().getName()+" async start at"+new SimpleDateFormat("HH:mm:ss").format(new Date()));
Thread.sleep(1000);
System.out.println(Thread.currentThread().getName()+" async end at"+new SimpleDateFormat("HH:mm:ss").format(new Date()));
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//同步代码块synchronized(this/object){}
private void syncObjectBlack(){
System.out.println(Thread.currentThread().getName()+" syncObjectBlack is gonna to start at"+new SimpleDateFormat("HH:mm:ss").format(new Date()));
synchronized (this){
try {
System.out.println(Thread.currentThread().getName()+" syncObjectBlack start at"+new SimpleDateFormat("HH:mm:ss").format(new Date()));
Thread.sleep(1000);
System.out.println(Thread.currentThread().getName()+" syncObjectBlack end at"+new SimpleDateFormat("HH:mm:ss").format(new Date()));
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
//同步非静态方法synchronized method
private synchronized void syncObjectMethod(){
System.out.println(Thread.currentThread().getName()+" syncObjectMethod is gonna to start at"+new SimpleDateFormat("HH:mm:ss").format(new Date()));
try {
System.out.println(Thread.currentThread().getName()+" syncObjectMethod start at"+new SimpleDateFormat("HH:mm:ss").format(new Date()));
Thread.sleep(1000);
System.out.println(Thread.currentThread().getName()+" syncObjectMethod end at"+new SimpleDateFormat("HH:mm:ss").format(new Date()));
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
Test.java
public class Test {
public static void main(String[] args){
SyncThread syncThread = new SyncThread();
Thread thread1 = new Thread(syncThread,"A_Thread_1");
Thread thread2 = new Thread(syncThread,"A_Thread_2");
Thread thread3 = new Thread(syncThread,"B_Thread_1");
Thread thread4 = new Thread(syncThread,"B_Thread_2");
Thread thread5 = new Thread(syncThread,"C_Thread_1");
Thread thread6 = new Thread(syncThread,"C_Thread_2");
thread1.start();
thread2.start();
thread3.start();
thread4.start();
thread5.start();
thread6.start();
}
}
运行结果与解析
1:A类由于是执行异步方法,所以A2先开始后,A1可以没等A2结束也随之开始,甚至比A2提前结束,没有受到其他使用同步锁线程的影响
2:B2准备开始之后B1也准备开始,B1开始之后,B2需要等到B1到结束后,B2才可以开始,即B2需要等待syncThread这个对象的同步锁,由于start开始才是在synchronized里面,外面的准备开始的打印语句是异步执行的
3:C1准备开始后,因为这时候已经进入到方法中,C2必须等到C1结束后,才能准备开始到开始到结束,即一个线程在访问对象的同步方法时,另外一个需要等待其结束
4:下面C2运行的时候,B的两个线程与C1都需要等待C2完成,这是因为同步方法synchronized与同步块synchronized它们锁的是同一个对象
A_Thread_2 async start at 20:31:01
B_Thread_2 syncObjectBlack is gonna to start at 20:31:01
B_Thread_1 syncObjectBlack is gonna to start at 20:31:01
A_Thread_1 async start at 20:31:01
C_Thread_2 syncObjectMethod is gonna to start at 20:31:01
C_Thread_2 syncObjectMethod start at 20:31:01
A_Thread_1 async end at 20:31:02
A_Thread_2 async end at 20:31:02
C_Thread_2 syncObjectMethod end at 20:31:02
B_Thread_1 syncObjectBlack start at 20:31:02
B_Thread_1 syncObjectBlack end at 20:31:03
B_Thread_2 syncObjectBlack start at 20:31:03
B_Thread_2 syncObjectBlack end at 20:31:04
C_Thread_1 syncObjectMethod is gonna to start at 20:31:04
C_Thread_1 syncObjectMethod start at 20:31:04
C_Thread_1 syncObjectMethod end at 20:31:05
1.4 获取类锁
- 同步代码块,如synchronized(类.class),锁是小括号()中的类对象,即Class对象
- 同步静态方法,如syschronized static method,锁是当前对象的类对象,即Class对象
SyncThread.java
public class SyncThread implements Runnable {
@Override
public void run() {
String threadName = Thread.currentThread().getName();
if(threadName.startsWith("A"))
async();
if(threadName.startsWith("B"))
syncObjectBlock();
if(threadName.startsWith("C"))
syncObjectMethod();
if(threadName.startsWith("D"))
syncClassBlock();
if(threadName.startsWith("E"))
synClassMethod();
}
//异步方法
private void async(){
try {
System.out.println(Thread.currentThread().getName()+" async start at "+new SimpleDateFormat("HH:mm:ss").format(new Date()));
Thread.sleep(1000);
System.out.println(Thread.currentThread().getName()+" async end at "+new SimpleDateFormat("HH:mm:ss").format(new Date()));
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//同步代码块synchronized(this/object){}
private void syncObjectBlock(){
System.out.println(Thread.currentThread().getName()+" syncObjectBlock is gonna to start at "+new SimpleDateFormat("HH:mm:ss").format(new Date()));
synchronized (this){
try {
System.out.println(Thread.currentThread().getName()+" syncObjectBlock start at "+new SimpleDateFormat("HH:mm:ss").format(new Date()));
Thread.sleep(1000);
System.out.println(Thread.currentThread().getName()+" syncObjectBlock end at "+new SimpleDateFormat("HH:mm:ss").format(new Date()));
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
//同步非静态方法synchronized method
private synchronized void syncObjectMethod(){
System.out.println(Thread.currentThread().getName()+" syncObjectMethod is gonna to start at "+new SimpleDateFormat("HH:mm:ss").format(new Date()));
try {
System.out.println(Thread.currentThread().getName()+" syncObjectMethod start at "+new SimpleDateFormat("HH:mm:ss").format(new Date()));
Thread.sleep(1000);
System.out.println(Thread.currentThread().getName()+" syncObjectMethod end at "+new SimpleDateFormat("HH:mm:ss").format(new Date()));
} catch (InterruptedException e) {
e.printStackTrace();
}
}
private void syncClassBlock(){
System.out.println(Thread.currentThread().getName()+" syncClassBlock is gonna to start at "+new SimpleDateFormat("HH:mm:ss").format(new Date()));
synchronized (SyncThread.class){
try {
System.out.println(Thread.currentThread().getName()+" syncClassBlock start at "+new SimpleDateFormat("HH:mm:ss").format(new Date()));
Thread.sleep(1000);
System.out.println(Thread.currentThread().getName()+" syncClassBlock end at "+new SimpleDateFormat("HH:mm:ss").format(new Date()));
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
private synchronized static void synClassMethod(){
System.out.println(Thread.currentThread().getName()+" synClassMethod is gonna to start at "+new SimpleDateFormat("HH:mm:ss").format(new Date()));
try {
System.out.println(Thread.currentThread().getName()+" synClassMethod start at "+new SimpleDateFormat("HH:mm:ss").format(new Date()));
Thread.sleep(1000);
System.out.println(Thread.currentThread().getName()+" synClassMethod end at "+new SimpleDateFormat("HH:mm:ss").format(new Date()));
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
1:Test.java:使用相同实例对象
public class Test {
public static void main(String[] args){
SyncThread syncThread = new SyncThread();
Thread thread1 = new Thread(syncThread,"A_Thread_1");
Thread thread2 = new Thread(syncThread,"A_Thread_2");
Thread thread7 = new Thread(syncThread,"D_Thread_1");
Thread thread8 = new Thread(syncThread,"D_Thread_2");
Thread thread9 = new Thread(syncThread,"E_Thread_1");
Thread thread10 = new Thread(syncThread,"E_Thread_2");
thread1.start();
thread2.start();
thread7.start();
thread8.start();
thread9.start();
thread10.start();
}
}
1:测试结果:使用相同实例对象
经过分析,结果跟获取对象锁相同对象的结果是一致的,运行流程一样
E_Thread_2 synClassMethod is gonna to start at 21:11:01
E_Thread_2 synClassMethod start at 21:11:01
D_Thread_1 syncClassBlock is gonna to start at 21:11:01
A_Thread_2 async start at 21:11:01
D_Thread_2 syncClassBlock is gonna to start at 21:11:01
A_Thread_1 async start at 21:11:01
A_Thread_2 async end at 21:11:02
A_Thread_1 async end at 21:11:02
E_Thread_2 synClassMethod end at 21:11:02
D_Thread_2 syncClassBlock start at 21:11:02
D_Thread_2 syncClassBlock end at 21:11:03
D_Thread_1 syncClassBlock start at 21:11:03
D_Thread_1 syncClassBlock end at 21:11:04
E_Thread_1 synClassMethod is gonna to start at 21:11:04
E_Thread_1 synClassMethod start at 21:11:04
E_Thread_1 synClassMethod end at 21:11:05
2:Test:使用不同实例对象
public class Test {
public static void main(String[] args){
SyncThread syncThread = new SyncThread();
Thread thread1 = new Thread(new SyncThread(),"A_Thread_1");
Thread thread2 = new Thread(new SyncThread(),"A_Thread_2");
/*Thread thread3 = new Thread(syncThread,"B_Thread_1");
Thread thread4 = new Thread(syncThread,"B_Thread_2");
Thread thread5 = new Thread(syncThread,"C_Thread_1");
Thread thread6 = new Thread(syncThread,"C_Thread_2");*/
Thread thread7 = new Thread(new SyncThread(),"D_Thread_1");
Thread thread8 = new Thread(new SyncThread(),"D_Thread_2");
Thread thread9 = new Thread(new SyncThread(),"E_Thread_1");
Thread thread10 = new Thread(new SyncThread(),"E_Thread_2");
thread1.start();
thread2.start();
/*thread3.start();
thread4.start();
thread5.start();
thread6.start();*/
thread7.start();
thread8.start();
thread9.start();
thread10.start();
}
}
2:测试结果:使用不同实例对象
1:A1与A2依旧是异步,不同类锁的影响
2:由于是类锁,即使是不同对象,结果跟获取对象锁相同对象的结果是一致的,运行流程一样
A_Thread_2 async start at 21:16:23
E_Thread_2 synClassMethod is gonna to start at 21:16:23
D_Thread_2 syncClassBlock is gonna to start at 21:16:23
D_Thread_1 syncClassBlock is gonna to start at 21:16:23
A_Thread_1 async start at 21:16:23
E_Thread_2 synClassMethod start at 21:16:23
E_Thread_2 synClassMethod end at 21:16:24
A_Thread_1 async end at 21:16:24
A_Thread_2 async end at 21:16:24
D_Thread_1 syncClassBlock start at 21:16:24
D_Thread_1 syncClassBlock end at 21:16:25
D_Thread_2 syncClassBlock start at 21:16:25
D_Thread_2 syncClassBlock end at 21:16:26
E_Thread_1 synClassMethod is gonna to start at 21:16:26
E_Thread_1 synClassMethod start at 21:16:26
E_Thread_1 synClassMethod end at 21:16:27
1.5 对象所与类锁互不干扰
因为一个是类对象,一个是实例对象,所以是不同对象,两者互不干扰
Test.java
public class Test {
public static void main(String[] args){
SyncThread syncThread = new SyncThread();
Thread thread1 = new Thread(new SyncThread(),"A_Thread_1");
Thread thread2 = new Thread(new SyncThread(),"A_Thread_2");
Thread thread3 = new Thread(syncThread,"B_Thread_1");
Thread thread4 = new Thread(syncThread,"B_Thread_2");
Thread thread5 = new Thread(syncThread,"C_Thread_1");
Thread thread6 = new Thread(syncThread,"C_Thread_2");
Thread thread7 = new Thread(new SyncThread(),"D_Thread_1");
Thread thread8 = new Thread(new SyncThread(),"D_Thread_2");
Thread thread9 = new Thread(new SyncThread(),"E_Thread_1");
Thread thread10 = new Thread(new SyncThread(),"E_Thread_2");
thread1.start();
thread2.start();
thread3.start();
thread4.start();
thread5.start();
thread6.start();
thread7.start();
thread8.start();
thread9.start();
thread10.start();
}
}
测试结果:
从运行结果可以看到,第二行为对象锁的C1运行之后,还没结束,第十行为类锁的E2就可以开始,两者异步执行
C_Thread_1 syncObjectMethod is gonna to start at 21:22:22
C_Thread_1 syncObjectMethod start at 21:22:22
A_Thread_2 async start at 21:22:22
D_Thread_1 syncClassBlock is gonna to start at 21:22:22
A_Thread_1 async start at 21:22:22
D_Thread_2 syncClassBlock is gonna to start at 21:22:22
B_Thread_2 syncObjectBlock is gonna to start at 21:22:22
B_Thread_1 syncObjectBlock is gonna to start at 21:22:22
E_Thread_2 synClassMethod is gonna to start at 21:22:22
E_Thread_2 synClassMethod start at 21:22:22
A_Thread_2 async end at 21:22:23
C_Thread_1 syncObjectMethod end at 21:22:23
A_Thread_1 async end at 21:22:23
B_Thread_1 syncObjectBlock start at 21:22:23
E_Thread_2 synClassMethod end at 21:22:23
D_Thread_2 syncClassBlock start at 21:22:23
B_Thread_1 syncObjectBlock end at 21:22:24
B_Thread_2 syncObjectBlock start at 21:22:24
D_Thread_2 syncClassBlock end at 21:22:24
D_Thread_1 syncClassBlock start at 21:22:24
B_Thread_2 syncObjectBlock end at 21:22:25
C_Thread_2 syncObjectMethod is gonna to start at 21:22:25
C_Thread_2 syncObjectMethod start at 21:22:25
D_Thread_1 syncClassBlock end at 21:22:25
E_Thread_1 synClassMethod is gonna to start at 21:22:25
E_Thread_1 synClassMethod start at 21:22:25
C_Thread_2 syncObjectMethod end at 21:22:26
E_Thread_1 synClassMethod end at 21:22:26
1.6 总结
- 有线程访问对象的同步代码块时,另外的线程可以访问该对象的非同步代码块,如上面的A
- 若锁住的是同一个对象,一个线程在访问对象的同步代码块时,另一个访问对象的同步代码块的线程会被阻塞
- 若锁住的是同一个对象,一个线程在访问对象的同步方法时,另一个访问对象的同步方法的线程会被阻塞
- 若锁住的是同一个对象,一个线程在访问对象的同步代码块时,另一个访问对象的同步方法的线程会被阻塞
- 若锁住的是同一个对象,一个线程在访问对象的同步方法时,另一个访问对象的同步代码块的线程会被阻塞
- 同一个类的不同对象的对象锁互不干扰
- 类锁由于也是一种特殊的对象锁,因为表现如上述前5点一致,而由于一个类只有一把对象锁,所以同一类的不同对象使用类锁将会是同步的
- 类锁和对象锁互不干扰