概念及解释
Java的内置锁:每个Java对象都可以用做一个实现同步的锁,这些锁成为内置锁。线程进入同步代码块或方法的时候会自动获得该锁,在退出同步代码块或方法时会释放该锁。获得内置锁的唯一途径就是进入这个锁的保护的同步代码块或方法。
重入:某个线程请求一个由其他线程持有的锁时,发出请求的线程会阻塞。如果线程试图获取一个已经由它自己持有的锁,那么他发出的请求就会成功。
1.Java内置锁是一个互斥锁,这就是意味着最多只有一个线程能够获得该锁,当线程A尝试去获得线程B持有的内置锁时,线程A必须等待或者阻塞,知道线程B释放这个锁,如果B线程不释放这个锁,那么A线程将永远等待下去。
2.Java内置锁的区分:对象锁是用于对象实例方法,或者一个对象实例上的,类锁是用于类的静态方法或者一个类的class对象上的。一个对象对应一个对象锁一个类只有一个类锁。类锁只是一个概念上的东西,并不是真实存在的,它只是用来帮助我们理解锁定实例方法和静态方法的区别的
用法
public class SynchronizedTest {
private static int value = 0;
private final Object object = new Object();
/**
* 线程不安全的
*
* @return
* @throws Exception
*/
public int getValue() throws Exception {
value++;
Thread.sleep(100);
return value;
}
/**
* 线程安全的
* 非静态方法加锁
* 方法锁(对象锁的一种)
* @return
* @throws InterruptedException
*/
public synchronized int getValue1() throws InterruptedException {
value++;
Thread.sleep(100);
return value;
}
/**
* 线程安全的
* 静态方法加锁
* 类锁
*
* @return
* @throws InterruptedException
*/
public static synchronized int getValue2() throws InterruptedException {
value++;
Thread.sleep(100);
return value;
}
/**
* 线程安全的
* 对象锁
* @return
* @throws InterruptedException
*/
public int getValue3() throws InterruptedException {
synchronized (object) {
value++;
Thread.sleep(100);
return value;
}
}
/**
* 线程安全的
* 类锁
* @return
* @throws InterruptedException
*/
public int getValue4() throws InterruptedException {
synchronized (Object.class) {
value++;
Thread.sleep(100);
return value;
}
}
public static void main(String[] args) {
final SynchronizedTest synchronizedTest = new SynchronizedTest();
//创建了一个最大容量为5的线程池
ExecutorService executorService = Executors.newFixedThreadPool(5);
for (int i = 1; i < 10; i++) {
executorService.execute(new Runnable() {
@Override
public void run() {
try {
System.out.println(synchronizedTest.getValue());//调用非安全的方法
//System.out.println(synchronizedTest.getValue1());//调用加了方法锁的方法
//System.out.println(synchronizedTest.getValue2());//调用加了类锁的方法
//System.out.println(synchronizedTest.getValue3());//调用对象锁代码块的方法
//System.out.println(synchronizedTest.getValue4());//调用类锁代码块的方法
} catch (Exception e) {
e.printStackTrace();
}
}
});
}
executorService.shutdown();
}
}
测试结果
非线程安全的方法getValue运行结果
其他线程安全的方法运行结果
注意事项
1.一个类的对象锁和另一个类的对象锁是没有关联的,当一个线程获得A类的对象锁时,它同时也可以获得B类的对象锁。
2.一个类中带对象锁的方法和不带锁的方法,可以异步执行,不干扰,不需要等待。
3.一个类中,如果set()方法加了对象锁,get()方法不加对象锁,容易发生数据脏读。可以用类锁或者set、get都加对象锁,解决。
4.同一个类中,多个锁方法相互调用,线程安全。
5.父子类中,锁方法相互调用,线程安全。
6.锁重入,在继承关系中适用。
7.锁方法发生异常时,会自动释放锁。程序继续执行,如果没有响应的处理,会发生业务逻辑的漏洞或者混乱。多见批处理、消息队列等。
8.类中的object成员变量加锁,为任意对象锁。
9.String常量值加锁,容易发生死锁。
10.如果你在一个对象锁方法中,对这个锁对象的引用做了修改,则表示释放了锁对象。如重新new object(),赋值给这个锁对象。但是锁对象的属性或值发生改变,并不发生锁的释放。