Synchronized锁注意事项
synchronized关键字,用它来修饰需要同步的方法和需要同步代码块,默认是当前对象作为锁的对象。在修饰类时(或者修饰静态方法),默认是当前类的Class对象作为所的对象故存在着方法锁、对象锁、类锁 这样的概念
在使用到锁的时候有锁方法,锁对象,和锁类,那么Synchronized是如何实现这三种方法的?
对象锁
修饰在方法上,多个线程调用同一个对象的同步方法会阻塞,调用不同对象的同步方法不会阻塞。(java对象的内存地址是否相同)
public synchronized void obj() {
System.out.println("w");
}
修饰代码块,这个this就是指当前对象(类的实例),多个线程调用同一个对象的同步方法会阻塞,调用不同对象的同步方法不会阻塞。(java对象的内存地址是否相同)
public void obj2() {
synchronized (this) {
System.out.println("w");
}
}
public void obj2() {
String str=new String("lock");//在方法体内,调用一次就实例化一次,多线程访问不会阻塞,因为不是同一个对象,锁是不同的
synchronized (str) {
System.out.println("w");
}
}
String str=new String("lock"); //对象放在方法外,调用方法的时候不会新创建一个对象。
public void obj2() {
synchronized (str) {
System.out.println("w");
}
}
Synchronized类锁
Synchronized修饰静态的方法
public static synchronized void obj3() {
System.out.println("w");
}
synchronized (test.class) ,锁的对象是test.class,即test类的锁
public void obj1() {
synchronized (test.class) {
System.out.println("w");
}
}
在一个类中有两方法,分别用synchronized 修饰的静态方法(类锁)和非静态方法(对象锁)。多线程访问两个方法的时候,线程会不会阻塞?
public static synchronized void obj3() {
int i = 5;
while (i-- > 0) {
System.out.println(Thread.currentThread().getName() + " : " + i);
try {
Thread.sleep(500);
} catch (InterruptedException ie) {
}
}
}
public synchronized void obj4() {
int i = 5;
while (i-- > 0) {
System.out.println(Thread.currentThread().getName() + " : " + i);
try {
Thread.sleep(500);
} catch (InterruptedException ie) {
}
}
}
输入如下:
Thread-0 : 4
Thread-1 : 4
Thread-0 : 3
Thread-1 : 3
Thread-1 : 2
Thread-0 : 2
Thread-1 : 1
Thread-0 : 1
Thread-1 : 0
Thread-0 : 0
因此是不会发生阻塞的!!!
总结
1.要满足方法同步(或者代码块同步)就必须保证多线程访问的是同一个对象(在java内存中的地址是否相同)。
2.类锁和对象锁同时存在时,多线程访问时不会阻塞,因为他们不是一个锁。
理论一摞摞,实际在开发中如何使用Synchronized保证线程安全呢?
在项目中的应用方案
注意:我们通常说的并发容器如ConcurrentHashMap等,都说是并发安全的,以为应用了上了这些容器就能保证线程的安全!我想说的是这些容器是能保证其变量的线程安全(也就是增删盖查多线程操作此变量下都是安全的),但是并不能保证业务上的并发安全问题。要想保证业务上的并发问题还是需要使用锁机制来控制业务并发问题!
在java中并发数据的发生基本都是操作一个类中成员变量,因此,我们需要在B类中对A类进行操作,那么我们假如是要控制A对象中的操作并发问题就需要将A的整个对象进行加锁,防止其他线程对同一个A对象进行操作(当然,其他线程对A类的不同对象可以并发操作并不会产生阻塞问题,也不会发生线程安全问题,这个是允许的)。因此使用上面的对象锁即可。也就是在B类中操作的A对象的成员变量,放到A对象中的方法上并加上synchronized即可保证线程安全。这样也能将锁细化。
在项目中,我们就是这样来控制和细化并发问题的!