先了解一下Synchronized的用法。
一、Synchronized的用法
在修饰代码块的时候需要一个reference对象作为锁的对象。
在修饰方法的时候默认是当前对象作为锁的对象。
在修饰类时候默认是当前类的Class对象作为锁的对象。
二、三种锁得区别与用法
1、方法锁(synchronized修饰方法时)
在定义方法时加入 synchronized关键字来声明 synchronized 方法。
synchronized 方法控制对类成员变量的访问:
每个类实例对应一把锁,每个 synchronized 方法都必须获得调用该方法的类实例的锁方能执行,否则所属线程阻塞,方法一旦执行,就独占该锁,直到从该方法返回时才将锁释放,此后被阻塞的线程方能获得该锁,重新进入可执行状态。这种机制确保了同一时刻对于每一个类实例,其所有声明为 synchronized 的成员函数中至多只有一个处于可执行状态,从而有效避免了类成员变量的访问冲突。
看一下代码:
public class TestSynchronizedMethod {
private int count = 0;
public static void main(String[] args) {
TestSynchronizedMethod testSynchronized = new TestSynchronizedMethod();
Thread threadA = new Thread(new Runnable() {
@Override
public void run() {
System.out.println("threadA开始执行");
testSynchronized.methodASynchronized();
System.out.println("threadA结束");
}
});
threadA.start();
Thread threadB = new Thread(new Runnable() {
@Override
public void run() {
System.out.println("threadB开始执行");
testSynchronized.methodBSynchronized();
System.out.println("threadB结束");
}
});
threadB.start();
}
public synchronized void methodASynchronized(){
System.out.println("methodASynchronized获得锁");
for(int i=0;i<5;i++){
count++;
System.out.println("methodASynchronized-count="+count);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
System.out.println("methodASynchronized释放锁");
}
public synchronized void methodBSynchronized(){
System.out.println("methodBSynchronized获得锁");
for(int i=0;i<5;i++){
count++;
System.out.println("methodBSynchronized-count="+count);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
System.out.println("methodBSynchronized释放锁");
}
}
输出结果:
threadA开始执行
methodASynchronized获得锁
methodASynchronized-count=1
threadB开始执行
methodASynchronized-count=2
methodASynchronized-count=3
methodASynchronized-count=4
methodASynchronized-count=5
methodASynchronized释放锁
threadA结束
methodBSynchronized获得锁
methodBSynchronized-count=6
methodBSynchronized-count=7
methodBSynchronized-count=8
methodBSynchronized-count=9
methodBSynchronized-count=10
methodBSynchronized释放锁
threadB结束
从输出结果可以看出,加了同步锁的两个方法,在不同的线程中调用后,先执行的方法会先获得锁,全部执行完毕后才释放锁,之后其他方法继续执行。
2、对象锁(synchronized修饰方法或代码块)
当一个对象中有synchronized method或synchronized block的时候调用此对象的同步方法或进入其同步区域时,就必须先获得对象锁。如果此对象的对象锁已被其他调用者占用,则需要等待此锁被释放。(方法锁也是对象锁中的一种)
线程进入synchronized方法或synchronized 块的时候获取该对象的锁(这个锁由JVM自动获取和释放),如果已经有线程获取了这个对象的锁,则当前线程会等待;synchronized方法或synchronized 块正常返回或者抛异常而终止,JVM会自动释放对象锁。
对象锁的两种方式:
(1)方法锁也是对象锁
public synchronized void methodASynchronized(){
System.out.println("methodASynchronized 我是方法锁也是对象锁");
}
(2)对象锁,代码块形式
public void methodBSynchronized(){
synchronized (this) {
System.out.println("methodBSynchronized 我是对象锁");
}
}
看一下代码:
public class TestSynchronizedObject {
private int count = 0;
public static void main(String[] args) {
TestSynchronizedObject testSynchronized = new TestSynchronizedObject();
Thread threadA = new Thread(new Runnable() {
@Override
public void run() {
System.out.println("threadA开始执行");
testSynchronized.methodASynchronized();
System.out.println("threadA结束");
}
});
threadA.start();
Thread threadB = new Thread(new Runnable() {
@Override
public void run() {
System.out.println("threadB开始执行");
testSynchronized.methodBSynchronizedBlock();
System.out.println("threadB结束");
}
});
threadB.start();
}
public synchronized void methodASynchronized(){
System.out.println("methodASynchronized获得锁");
System.out.println("methodASynchronized 我是方法锁也是对象锁");
for(int i=0;i<5;i++){
count++;
System.out.println("methodASynchronized-count="+count);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
System.out.println("methodASynchronized释放锁");
}
public void methodBSynchronizedBlock(){
synchronized (this) {
System.out.println("methodBSynchronizedBlock获得锁");
System.out.println("methodBSynchronizedBlock 我是对象锁");
for(int i=0;i<5;i++){
count++;
System.out.println("methodBSynchronizedBlock-count="+count);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
System.out.println("methodBSynchronizedBlock释放锁");
}
}
}
运行结果如下:
threadA开始执行
methodASynchronized获得锁
methodASynchronized 我是方法锁也是对象锁
methodASynchronized-count=1
threadB开始执行
methodASynchronized-count=2
methodASynchronized-count=3
methodASynchronized-count=4
methodASynchronized-count=5
methodASynchronized释放锁
threadA结束
methodBSynchronizedBlock获得锁
methodBSynchronizedBlock 我是对象锁
methodBSynchronizedBlock-count=6
methodBSynchronizedBlock-count=7
methodBSynchronizedBlock-count=8
methodBSynchronizedBlock-count=9
methodBSynchronizedBlock-count=10
methodBSynchronizedBlock释放锁
threadB结束
从输出结果可以看出,在同一个对象中,不论是同步方法还是同步块,在不同的线程中调用后,先执行的方法会先获得锁,全部执行完毕后才释放锁,之后其他方法继续执行。
3、类锁(synchronized 修饰静态的方法或代码块)
一个class不论被实例化多少次,它的静态方法或静态变量在内存中都只有一份。所以,一旦一个静态的方法被申明为synchronized。此类所有的实例化对象在调用此方法时共用同一把锁,称之为类锁。
与对象锁不同的是,对象锁是用来控制实例方法之间的同步,类锁是用来控制静态方法(或静态变量互斥体)之间的同步。
类锁的两种方式:
(1)静态方法
public static synchronized void methodASynchronizedClass(){
System.out.println("methodASynchronizedClass我是静态方法");
}
(2)代码块
public void methodBSynchronizedClass(){
synchronized (TestSynchronizedClass.class) {
System.out.println("methodASynchronizedClass我是同步代码块");
}
}
看一下代码:
public class TestSynchronizedClass {
private static int count = 0;
public static void main(String[] args) {
TestSynchronizedClass testSynchronized1 = new TestSynchronizedClass();
TestSynchronizedClass testSynchronized2 = new TestSynchronizedClass();
Thread threadA = new Thread(new Runnable() {
@Override
public void run() {
System.out.println("threadA开始执行");
testSynchronized1.methodASynchronizedClass();
System.out.println("threadA结束");
}
});
threadA.start();
Thread threadB = new Thread(new Runnable() {
@Override
public void run() {
System.out.println("threadB开始执行");
testSynchronized2.methodBSynchronizedClass();
System.out.println("threadB结束");
}
});
threadB.start();
}
public static synchronized void methodASynchronizedClass(){
System.out.println("methodASynchronizedClass获得锁");
System.out.println("methodASynchronizedClass我是静态方法");
for(int i=0;i<5;i++){
count++;
System.out.println("methodASynchronizedClass-count="+count);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
System.out.println("methodASynchronizedClass释放锁");
}
public void methodBSynchronizedClass(){
synchronized (TestSynchronizedClass.class) {
System.out.println("methodBSynchronizedClass获得锁");
System.out.println("methodASynchronizedClass我是同步代码块");
for(int i=0;i<5;i++){
count++;
System.out.println("methodBSynchronizedClass-count="+count);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
System.out.println("methodBSynchronizedClass释放锁");
}
}
}
运行结果如下:
threadA开始执行
methodASynchronizedClass获得锁
methodASynchronizedClass我是静态方法
methodASynchronizedClass-count=1
threadB开始执行
methodASynchronizedClass-count=2
methodASynchronizedClass-count=3
methodASynchronizedClass-count=4
methodASynchronizedClass-count=5
methodASynchronizedClass释放锁
threadA结束
methodBSynchronizedClass获得锁
methodASynchronizedClass我是同步代码块
methodBSynchronizedClass-count=6
methodBSynchronizedClass-count=7
methodBSynchronizedClass-count=8
methodBSynchronizedClass-count=9
methodBSynchronizedClass-count=10
methodBSynchronizedClass释放锁
threadB结束
从输出结果可以看出,在同一个class对象的不同实例中,不论是同步方法还是同步块,在不同的线程中调用后,先执行的方法会先获得类锁,全部执行完毕后才释放锁,之后其他方法继续执行。