高并发编程_Java线程基础 4.sychronized同步机制
之前在介绍线程的时候,讲到同一个进程内的多个线程,可以共享资源。这一特点,在为多线程编程带来方便的同时,也带来了一个不可忽视,在开发中经常遇到的问题,那就是多线程并发情况下,访问共享资源的安全问题。
Java语言提供了专门的机制以解决这个问题,那就是sychronized同步机制。使用sychronized关键字,修饰代码块。修饰方法、修饰类,为同步代码块、同步方法或者class对象添加锁,所有线程在访问的时候,首先需要获取锁资源,然后才可以访问共享资源。在sychronized修改的代码中,此时的并发线程(只是宏观上的体现,而在微观上依然是单线程运行)则变成单线程串行运行,通过这种机制,可实现对共享资源的安全访问。当然同时也存在一个问题,那就是性能问题。因为在sychronized代码中,线程的串行执行,其他线程只能处于等待状态,只有当前获得锁资源的线程执行完成,其他竞争获得锁资源的线程才能继续执行,降低了系统的吞吐率,影响程序性能。并且同步在使用不当的情况下,会造成死锁,所以需慎用。
1.sychronized作用域
sychronized同步锁可分为两种类型,四种表现形式:
对象锁:对单个实例对象的独享内存的部分区域加锁
类锁:对整个类的共享内存的部分区域加锁
1.1 sychronized作用于非静态方法上
当sychronized作用于非静态方法上时,锁住的是当前实例对象,每个实例都拥有一把锁,调用任何一个sychronized修饰的方法时,都需要获得该实例的锁。
对于同一个实例对象来说,所有采用synchronized修饰的同步方法都会被锁定,任何时候,实例对象内的同步方法在同一时间只能被一个线程访问,因为在调用任何同步方法时,都需要获取势力对象的锁。其他非同步方法不受此限制,同一个实例对象,在访问同步方法时,仍然可以访问其他非同步方法。
package com.ryw.patd;
public class SychronizedMethod {
public static void main(String[] args) {
SyncDemo syncDemo1 = new SyncDemo();
new Thread(new Runnable() {
@Override
public void run() {
syncDemo1.m1();
}
}, "thread1").start();
new Thread(new Runnable() {
@Override
public void run() {
syncDemo1.m2();
}
}, "thread2").start();
}
}
class SyncDemo {
synchronized void m1() {
for (int i = 0; i < 5; i++) {
try {
Thread.sleep(100);
System.out.println("m1-->" + Thread.currentThread().getName() + "-->" + i);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
synchronized void m2() {
for (int i = 0; i < 5; i++) {
try {
Thread.sleep(100);
System.out.println("m2-->" + Thread.currentThread().getName() + "-->" + i);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
运行结果
exec1-->thread1-->0
exec1-->thread1-->1
exec1-->thread1-->2
exec1-->thread1-->3
exec1-->thread1-->4
exec1-->thread2-->0
exec1-->thread2-->1
exec1-->thread2-->2
exec1-->thread2-->3
exec1-->thread2-->4
对于不同实例对象来说,所有被sychronized修饰的同步方法之间不会相互干扰。即实例对象A在访问同步方法A的时候,实例对象B仍然可以访问同步方法A。
package com.ryw.patd;
public class SychronizedMethod {
public static void main(String[] args) {
SyncDemo syncDemo1 = new SyncDemo();
SyncDemo syncDemo2 = new SyncDemo();
new Thread(new Runnable() {
@Override
public void run() {
syncDemo1.m1();
}
}, "thread1").start();
new Thread(new Runnable() {
@Override
public void run() {
syncDemo2.m2();
}
}, "thread2").start();
}
}
class SyncDemo {
synchronized void m1() {
for (int i = 0; i < 5; i++) {
try {
Thread.sleep(100);
System.out.println("m1-->" + Thread.currentThread().getName() + "-->" + i);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
synchronized void m2() {
for (int i = 0; i < 5; i++) {
try {
Thread.sleep(100);
System.out.println("m2-->" + Thread.currentThread().getName() + "-->" + i);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
输出结果:
m1-->thread1-->0
m2-->thread2-->0
m1-->thread1-->1
m2-->thread2-->1
m1-->thread1-->2
m2-->thread2-->2
m1-->thread1-->3
m2-->thread2-->3
m1-->thread1-->4
m2-->thread2-->4
1.2 sychronized作用于静态方法上
当sychronized作用于静态方法上时,锁住的是当前类对象(class),所有实例对象共用同一把锁。
当实例对象A访问同步静态方法A时,实例对象B仍然无法访问同步静态方法A。此时实例对象B处于等待对象,等待实例对象A访问结束,然后实例对象B才可以访问同步静态方法A。所有实例对象共用同一把锁,需要等待起一个实例对象执行完成,释放掉锁资源,其他实例对象方可执行。
package com.ryw.patd;
public class SychronizedMethod {
public static void main(String[] args) {
SyncDemo syncDemo1 = new SyncDemo();
SyncDemo syncDemo2 = new SyncDemo();
new Thread(new Runnable() {
@Override
public void run() {
syncDemo1.m1();
}
}, "thread1").start();
new Thread(new Runnable() {
@Override
public void run() {
syncDemo2.m2();
}
}, "thread2").start();
}
}
class SyncDemo {
synchronized static void m1() {
for (int i = 0; i < 5; i++) {
try {
Thread.sleep(1000);
System.out.println("m1-->" + Thread.currentThread().getName() + "-->" + i);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
synchronized static void m2() {
for (int i = 0; i < 5; i++) {
try {
Thread.sleep(1000);
System.out.println("m2-->" + Thread.currentThread().getName() + "-->" + i);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
输出结果:
m1-->thread1-->0
m1-->thread1-->1
m1-->thread1-->2
m1-->thread1-->3
m1-->thread1-->4
m2-->thread2-->0
m2-->thread2-->1
m2-->thread2-->2
m2-->thread2-->3
m2-->thread2-->4
1.3 sychronized作用于普通代码块上
当sychronized作用于普通代码块上时,锁住的是当前实例对象。其他未加锁的代码不受同步机制的影响。原理同作用于非静态方法上。
对于同一个实例对象来说,所有采用synchronized修饰的同步代码块都会被锁定,任何时候,实例对象内的同步代码块在同一时间只能被一个线程访问,因为在调用任何同步代码块时,都需要获取实例对象的锁。
对于不同实例对象来说,所有被sychronized修饰的同步代码块之间不会相互干扰。即实例对象A在访问同步代码块A的时候,实例对象B仍然可以访问同步代码块A。
案例参考作用于非静态方法上的案例,此处不再写案例。
1.4 sychronized作用于静态代码块上
当sychronized作用于静态代码块时,锁住的是当前类对象。所有实例对象共用同一把锁。多个实例对象执行时,需要等待其他实例对象释放锁资源。
此次线程相关介绍就到这里,如有描述不当地方,请指正。