synchronized关键字
当synchronized作用在方法上时,锁住的便是对象实例(this);当作用在静态方法时锁住的便是对象对应的Class实例,因为 Class数据存在于永久带,因此静态方法锁相当于该类的一个全局锁;当synchronized作用于某一个对象实例时,锁住的便是对应的代码块。
一 、类锁
当一个synchronized修饰类的静态方法,此时当两个线程访问同一个类中的同一个静态方法或者是不同的静态方法,此时两个线程需要按照获取锁的顺序执行方法。也就是两个线程同时访问类锁是同步的。(后面会讲类锁和对象锁)
package java_lang_Object;
/**
* Created by luckyboy on 2018/7/6.
*/
class MyThread_01 {
public synchronized static void printA(){
System.out.println("线程名"+Thread.currentThread().getName() + ":" + "进入了方法printA()");
try {
Thread.sleep(6000);
System.out.println("线程名"+Thread.currentThread().getName() + ":" +"离开了方法printA()");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public synchronized static void printB(){
System.out.println("线程名"+Thread.currentThread().getName() + ":" + "进入了方法printB()");
try {
Thread.sleep(6000);
System.out.println("线程名"+Thread.currentThread().getName() + ":"+"离开了方法printB()");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
class Mythread1 extends Thread{
@Override
public void run(){//调用类的静态方法
MyThread_01.printA();
}
}
class Mythread2 extends Thread{
@Override
public void run(){//调用类的静态方法
MyThread_01.printB();
}
}
测试类
package java_lang_Object;
/**
* Created by luckyboy on 2018/7/6.
*/
public class SynchronizedTest {
public static void main(String[] args){
Mythread1 thread1 = new Mythread1();
Mythread2 thread2 = new Mythread2();
thread1.start();
thread2.start();
}
}
输出结果
线程名Thread-0:进入了方法printA()
线程名Thread-0:离开了方法printA()
线程名Thread-1:进入了方法printB()
线程名Thread-1:离开了方法printB()
从结果我们可以看到,对于访问我们的Mythread1访问的是printA(),而Mythread2访问的是printB();但是printA()和printB()都是静态方法,一旦Thread-0获得了类锁,那么thread-1就进入了lock blocked pool,此时等待Mythread_01类的类锁。一旦thread-0运行完毕以后,此时JVM就会给thread1锁,此时thread-02会访问方法printB()
二、方法锁和类锁
- 当使用synchronized修饰类的一般方法(),如果两个线程需要进入同一个对象的两个synchronized修饰的方法时或者同一个synchronized方法时,此时两个线程竞争的是对象锁(详细见三);
- 如果两个线程一个访问类的被synchronized修饰的静态(类)方法时,而另一个访问类的被synchronized修饰的一般方法时,这个时候一个线程获取的是类锁,而另一个获取的是类的对象锁(详细见下面的代码)
package java_lang_Object;
/**
* Created by luckyboy on 2018/7/6.
*/
class MyThread_01 {
public synchronized static void printA(){
System.out.println("线程名"+Thread.currentThread().getName() + ":" +
"进入了方法printA()");
System.out.println("线程名"+Thread.currentThread().getName() + ":" +"离开了方法printA()");
}
public synchronized void printC(){
System.out.println("线程名"+Thread.currentThread().getName() + ":" +"进入了方法printC()");
try {
Thread.sleep(6000);
System.out.println("线程名"+Thread.currentThread().getName() + ":"+"离开了方法printC()");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
class Mythread1 extends Thread{
@Override
public void run(){//调用类的静态方法
MyThread_01.printA();
}
}
class Mythread3 extends Thread{
private MyThread_01 thread;
public Mythread3(MyThread_01 thread){
this.thread = thread;
}
@Override
public void run(){//调用类的一般方法
thread.printC();
}
}
测试类
package java_lang_Object;
/**
* Created by luckyboy on 2018/7/6.
*/
public class SynchronizedTest {
public static void main(String[] args){
MyThread_01 mythread_01 = new MyThread_01();
Mythread1 thread1 = new Mythread1();
Mythread3 thread3 = new Mythread3(mythread_01);
thread1.start();
thread3.start();
}
}
输出结果:
//输出case1
线程名Thread-0:进入了方法printA()
线程名Thread-1:进入了方法printC()
线程名Thread-0:离开了方法printA()
线程名Thread-1:离开了方法printC()
//输出case2
线程名Thread-0:进入了方法printA()
线程名Thread-0:离开了方法printA()
线程名Thread-1:进入了方法printC()
线程名Thread-1:离开了方法printC()
//输出case3
线程名Thread-0:进入了方法printA()
线程名Thread-1:进入了方法printC()
线程名Thread-1:离开了方法printC()
线程名Thread-0:离开了方法printA()
结果分析:我们可以看到当thread-0进入到printA(),此时取到了类锁,但是在thread-0还没有离开printA()时;此时thread-1进入了printC(),因此我们可以判断对象锁和类锁不是同一个东西,如果两个锁是同一个东西,那么只要thread-01获得了锁,那么thread-1不可能在thread-0没有退出printA()之前就进入printC()。
三、方法锁和方法锁-访问同一个类的同一个对象
package java_lang_Object;
/**
* Created by luckyboy on 2018/7/6.
*/
class MyThread_01 {
public synchronized void printC(){
System.out.println("线程名"+Thread.currentThread().getName() + ":" +
"进入了方法printC()");
System.out.println("线程名"+Thread.currentThread().getName() + ":"+
"离开了方法printC()");
/*
try {
Thread.sleep(6000);
System.out.println("线程名"+Thread.currentThread().getName() + ":"+
"离开了方法printC()");
} catch (InterruptedException e) {
e.printStackTrace();
}*/
}
public synchronized void printD(){
System.out.println("线程名"+Thread.currentThread().getName() + ":" +
"进入了方法printD()");
System.out.println("线程名"+Thread.currentThread().getName() + ":"+
"离开了方法printD()");
/*
try {
Thread.sleep(6000);
System.out.println("线程名"+Thread.currentThread().getName() + ":"+
"离开了方法printC()");
} catch (InterruptedException e) {
e.printStackTrace();
}*/
}
}
class Mythread3 extends Thread{
private MyThread_01 thread;
public Mythread3(MyThread_01 thread){
this.thread = thread;
}
@Override
public void run(){
thread.printC();
}
}
class Mythread4 extends Thread{
private MyThread_01 thread;
public Mythread4(MyThread_01 thread){
this.thread = thread;
}
@Override
public void run(){
thread.printD();
}
}
测试类
package java_lang_Object;
/**
* Created by luckyboy on 2018/7/6.
*/
public class SynchronizedTest {
public static void main(String[] args){
MyThread_01 mythread_01 = new MyThread_01();
Mythread3 thread3 = new Mythread3(mythread_01);
Mythread4 thread4 = new Mythread4(mythread_01);
thread3.start();
thread4.start();
}
}
输出结果
线程名Thread-0:进入了方法printC()
线程名Thread-0:离开了方法printC()
线程名Thread-1:进入了方法printD()
线程名Thread-1:离开了方法printD()
结果分析:从分析结果我们可以看到,一旦thread-0进入了printC(),此时thread-0获得了MyThread_01类的 mythread_01对象的对象锁,那么thread-1此时只能进入lock blocked pool(锁池)进行等待,一旦thread-0退出printA(),这个时候thread-0将对象锁还给了JVM,然后JVM将对象锁分配给thread-1运行线程
四、方法锁&&方法锁-访问同一个类的不同的对象
测试代码在之前的情况下,改一下就好了
package java_lang_Object;
/**
* Created by luckyboy on 2018/7/6.
*/
public class SynchronizedTest {
public static void main(String[] args){
MyThread_01 mythread_01 = new MyThread_01();
MyThread_01 mythread_02 = new MyThread_01();
Mythread3 thread3 = new Mythread3(mythread_01);
Mythread4 thread4 = new Mythread4(mythread_02);
thread3.start();
thread4.start();
}
}
输出结果
线程名Thread-0:进入了方法printC()
线程名Thread-1:进入了方法printD()
线程名Thread-0:离开了方法printC()
线程名Thread-1:离开了方法printD()