文章目录
-
- 1. 对象锁
-
- 1.1. 同步方法
- 1.2. 同步代码块(当前类对象锁)
- 1.3. 同步代码块(传入类对象锁)
- 2. 类锁
-
- 2.1. 同步静态方法
- 2.2. 同步代码块(当前类class锁)
多个进程或线程同时(或着说在同一段时间内)访问同一资源会产生并发(线程安全)问题。解决并发问题可以用锁。单机情况下可以直接使用
synchronized
关键字实现的内置锁。
每个
Java
对象都可以用做一个实现同步的锁,这些锁称为内置锁。线程进入同步代码块或方法的时候会自动获得该锁,在退出同步代码块或方法时会释放该锁。获得内置锁的唯一途径就是进入这个锁保护的同步代码块或方法。
Java
内置锁是一个互斥锁,这就意味着最多只有一个线程能够获得该锁,当线程
A
尝试去获得线程
B
持有的内置锁时,线程
A
必须等待或者阻塞,直到线程
B
释放这个锁,如果线程
B
不释放这个锁,那么线程
A
将永远等待下去。
Java
的内置锁基本上可以分为对象锁和类锁,对象锁是用于对象实例方法或者对象实例上的,类锁是用于类的静态方法或者类的
class
对象上的。我们知道,一个类的实例对象可以有很多个,但是每个类只有一个
class
对象,所以不同对象实例的对象锁是互不干扰的,但是每个类只有一个类锁。有一点必须注意的是,其实类锁只是一个概念上的东西,并不是真实存在的,它只是用来帮助我们理解锁定实例方法和静态方法的区别的。
synchronized
只是一个内置锁的加锁机制,当某个方法加上
synchronized
关键字后,就表明要获得该内置锁才能执行,并不能阻止其他线程访问不需要获得该内置锁的方法。
1. 对象锁
1.1. 同步方法
同步方法,是对该对象加锁,其他线程将无法访问需要获取该对象锁的方法和代码块。
public class Test{
public synchronized void print(){
System.out.println("hello world!");
}
}
上面代码表示访问
print()
方法需要获取
Test
实例对象的锁。
1.2. 同步代码块(当前类对象锁)
同步代码块,这种写法也是锁住该类的实例对象,和
1.1.
效果一样,其他线程将无法访问需要获取该对象锁的方法和代码块。
public class Test{
public void print(){
synchronized(this){
System.out.println("hello world!");
}
}
}
上面代码表示访问
print()
方法中的代码块需要获取
Test
实例对象的锁。
1.3. 同步代码块(传入类对象锁)
同步代码块,这种写法锁住传入的对象,不影响需要获取当前类对象锁的方法和代码块的访问。
public class Test{
private byte[] lock = new byte[0];
public void print(){
synchronized(lock){
System.out.println("hello world!");
}
}
public synchronized void print1(){
System.out.println("123456");
}
}
执行
print()
方法里面的同步代码块,会给
byte[]
对象
lock
加锁,注意不是给
Test
的对象加锁,也就是说
Test
对象的其它
synchronized
方法和代码块不会因为
print()
而被锁。
print1()
方法可以正常访问。
2. 类锁
类锁修饰方法和代码块的效果和对象锁是一样的,因为类锁只是一个抽象出来的概念,类锁和对象锁是互不干扰的。同样,线程获得对象锁的同时,也可以获得该类锁,即同时获得两个锁,这是允许的。
2.1. 同步静态方法
因为静态方法是所有对象实例共用的,对应着
synchronized
修饰的静态方法的锁也是唯一的,所以抽象出来个类锁。其他线程将无法访问需要获取该类锁的方法和代码块。
public class Test{
public synchronized static void print(){
System.out.println("hello world!");
}
}
2.2. 同步代码块(当前类class锁)
这种写法,直接锁定当前类的
class
对象,因为每个类只有一个
class
对象,和
2.1.
效果一样。其他线程将无法访问需要获取该类锁的方法和代码块。
public class Test{
public void print(){
synchronized(Test.class){
System.out.println("hello world!");
}
}
}