Synchronized的使用
1.为什么使用Synchronized
因为在并发编程中,多线程并发操作共享数据会出现数据安全问题。使用
Synchronized 可以保证同一时刻,只有一个线程能够访问同一个对象的方法。
2.Synchronized的应用方式
1)给普通方法加锁,是锁住的当前对象
2)给代码块加锁,锁住的是给定的对象
3)给静态方法加锁,锁的是当前类型的类对象,简称类锁
3.代码示例
应用1)和2)的代码
public class Test_01 {
private int count = 0;
private Object o = new Object();
public void testSync1(){
synchronized(o){
System.out.println(Thread.currentThread().getName()
+ " count = " + count++);
}
}
public void testSync2(){
synchronized(this){
System.out.println(Thread.currentThread().getName()
+ " count = " + count++);
try {
TimeUnit.SECONDS.sleep(3);
System.out.println("3s后"+Thread.currentThread().getName());
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public synchronized void testSync3(){
System.out.println(Thread.currentThread().getName()
+ " count = " + count++);
try {
TimeUnit.SECONDS.sleep(3);
System.out.println("3s后"+Thread.currentThread().getName());
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
final Test_01 t = new Test_01();
new Thread(new Runnable() {
@Override
public void run() {
t.testSync2();
}
}).start();
new Thread(new Runnable() {
@Override
public void run() {
t.testSync3();
}
}).start();
}
}
结果分析:
synchronized(this)和synchronized方法都是锁当前对象。
1.当两个并发线程访问同一个对象object中的这个synchronized(this)同步方法时,一个时间内只能有一个线程得到执行。另一个线程必须等待当前线程执行完这个方法以后才能执行该方法,但是可以访问非同步方法。保证了方法访问的互斥和变量可见性
2.不同对象锁不相互影响。对于上述testSync1,锁住的是对象o。testSync1和testSync2是不同对象的同步方法,可以同时执行。
3.两个线程访问同一对象的不同synchronized方法,互斥。
应用3)的代码
private static int staticCount = 0;
public static synchronized void testSync4(){
System.out.println(Thread.currentThread().getName()
+ " staticCount = " + staticCount++);
try {
TimeUnit.SECONDS.sleep(3);
System.out.println("3s后"+Thread.currentThread().getName());
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
public static void testSync5(){
synchronized(Test_02.class){
System.out.println(Thread.currentThread().getName()
+ " staticCount = " + staticCount++);
}
}
public static void main(String[] args) {
final Test_02 t = new Test_02();
new Thread(new Runnable() {
@Override
public void run() {
t.testSync4();
}
}).start();
new Thread(new Runnable() {
@Override
public void run() {
t.testSync5();
}
}).start();
}
分析结果:
两个线程实例化两个不同的对象,但是访问的方法是静态的,两个线程发生了互斥。同步静态方法是类对象锁。testSync5锁的也是类对象。
其他情况,比如两个线程访问同步静态方法和非同步方法,是不互斥的。
4.可重入锁
可重复可递归调用的锁。同一个线程,递归调用同步代码,锁定的是同一个锁对象,可重入。
代码举例:
synchronized void m1(){ // 锁this
System.out.println("m1 start");
try {
TimeUnit.SECONDS.sleep(2);
} catch (InterruptedException e) {
e.printStackTrace();
}
m2();
System.out.println("m1 end");
}
synchronized void m2(){ // 锁this
System.out.println("m2 start");
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("m2 end");
}
public static void main(String[] args) {
new Thread(new Runnable() {
@Override
public void run() {
new Test_06().m1();
}
}).start();
}
执行结果:
m1 start
m2 start
m2 end
m1 end
m1()中正常调用m2(),可重入。
synchronized 是一种悲观锁,每次执行临界区代码都会产生冲突,当前线程获得锁后,其他线程只能阻塞。