线程互斥是指某一代码同时只能允许一个线程访问
对象锁:对象锁锁的是对象,所以说如果这个锁是对象锁,而且是同一对象下多线程,那么线程间是互斥的。对象锁包含:锁非静态方法/对象/代码块
类锁:类锁锁的是类,如果这个锁是类锁,而且是同一类(可以是不同对象)下多线程,那么线程间是互斥的。类锁包含:锁静态方法/类
类锁和对象锁不会互斥
类锁的作用范围是这个类,如果超过了类(比如两个不同类)就会失效
对象锁的作用范围是这个对象,如果超过了对象(比如不同对象、对象与类之间等)就会失效
1.对象锁
对象锁的范围时对象级别的及仅仅作用于同一个对象,如果是两个不同的对象是不会互斥的
1.1 synchronized修饰同一个类对象的两个非静态方法时互斥
public class NoStaticMethod { public static void main(String[] args) { NoStaticMethod noStaticMethod=new NoStaticMethod(); new Thread(() -> { // new了一个ClassLock对象 noStaticMethod.test1(); }).start(); new Thread(() -> { // new了另一个ClassLock对象 noStaticMethod.test2(); }).start(); // //这两个线程是相同class的不同对象 条件1 // new Thread(() -> { // // new了一个ClassLock对象 // new NoStaticMethod().test1(); // }).start(); // // new Thread(() -> { // // new了另一个ClassLock对象 // new NoStaticMethod().test2(); // }).start(); } public synchronized void test1(){ System.out.println("test1 " + new Date() + " " + Thread.currentThread().getName() + " begin..."); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("test1 " + new Date() + " " + Thread.currentThread().getName() + " end..."); } public synchronized void test2(){ System.out.println("test2 " + new Date() + " " + Thread.currentThread().getName() + " begin..."); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("test2 " + new Date() + " " + Thread.currentThread().getName() + " end..."); } }
上例两个线程同时被synchronized修饰的相同对象的不同方法,锁生效,因为两个线程使用相同的对象锁。
output:
test1 Wed Jan 11 15:18:50 CST 2023 Thread-0 begin...
test1 Wed Jan 11 15:18:51 CST 2023 Thread-0 end...
test2 Wed Jan 11 15:18:51 CST 2023 Thread-1 begin...
test2 Wed Jan 11 15:18:52 CST 2023 Thread-1 end...
从输出可以看出由于sychronzied生效,而且这两个线程是同一对象锁,所以 noStaticMethod.test1()执行完释放掉锁之后noStaticMethod.test2()才获取到锁,执行相应代码
注意上例中开启的两个线程是相同对象,如果把条件1放开,由于是两个不同对象,则不能保证两个线程互斥,此时两个线程是同时进行
此时output:
test2 Thu Jan 12 14:08:00 CST 2023 Thread-1 begin...
test1 Thu Jan 12 14:08:00 CST 2023 Thread-0 begin...
test1 Thu Jan 12 14:08:01 CST 2023 Thread-0 end...
test2 Thu Jan 12 14:08:01 CST 2023 Thread-1 end...
1.2 synchronized分别修饰同一个类对象的非静态方法和当前对象时互斥
public class NoStaticObj { private Object objectLock = new Object(); public static void main(String[] args) { NoStaticObj noStaticObj = new NoStaticObj(); new Thread(() -> { // new了一个ClassLock对象 noStaticObj.test1(); }).start(); new Thread(() -> { // new了另一个ClassLock对象 noStaticObj.test2(); }).start(); } public synchronized void test1() { // synchronized (this) { System.out.println("test1 " + new Date() + " " + Thread.currentThread().getName() + " begin..."); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("test1 " + new Date() + " " + Thread.currentThread().getName() + " end..."); // } } public void test2() { synchronized (this) { System.out.println("test2 " + new Date() + " " + Thread.currentThread().getName() + " begin..."); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("test2 " + new Date() + " " + Thread.currentThread().getName() + " end..."); } } } output: test1 Thu Jan 12 15:03:27 CST 2023 Thread-0 begin... test1 Thu Jan 12 15:03:28 CST 2023 Thread-0 end... test2 Thu Jan 12 15:03:28 CST 2023 Thread-1 begin... test2 Thu Jan 12 15:03:29 CST 2023 Thread-1 end...
从结果看出也是在第一个线程完全执行结束后才开始执行第二个线程
1.3 synchronized修饰同一个类对象时互斥
public class NoStaticObj { private Object objectLock = new Object(); public static void main(String[] args) { NoStaticObj noStaticObj = new NoStaticObj(); new Thread(() -> { // new了一个ClassLock对象 noStaticObj.test1(); }).start(); new Thread(() -> { // new了另一个ClassLock对象 noStaticObj.test2(); }).start(); } public void test1() { synchronized (this) { System.out.println("test1 " + new Date() + " " + Thread.currentThread().getName() + " begin..."); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("test1 " + new Date() + " " + Thread.currentThread().getName() + " end..."); } } public void test2() { synchronized (this) { System.out.println("test2 " + new Date() + " " + Thread.currentThread().getName() + " begin..."); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("test2 " + new Date() + " " + Thread.currentThread().getName() + " end..."); } } } output: test1 Thu Jan 12 15:02:26 CST 2023 Thread-0 begin... test1 Thu Jan 12 15:02:27 CST 2023 Thread-0 end... test2 Thu Jan 12 15:02:27 CST 2023 Thread-1 begin... test2 Thu Jan 12 15:02:28 CST 2023 Thread-1 end...
1.4 synchronized修饰当前类(类锁)和当前类对象不会互斥。类锁和对象锁时相互独立的,互不相斥
public class NoStaticObj { private Object objectLock = new Object(); public static void main(String[] args) { NoStaticObj noStaticObj = new NoStaticObj(); new Thread(() -> { // new了一个ClassLock对象 noStaticObj.test1(); }).start(); new Thread(() -> { // new了另一个ClassLock对象 noStaticObj.test2(); }).start(); } public void test1() { synchronized (NoStaticObj.class) { System.out.println("test1 " + new Date() + " " + Thread.currentThread().getName() + " begin..."); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("test1 " + new Date() + " " + Thread.currentThread().getName() + " end..."); } } public void test2() { synchronized (this) { System.out.println("test2 " + new Date() + " " + Thread.currentThread().getName() + " begin..."); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("test2 " + new Date() + " " + Thread.currentThread().getName() + " end..."); } } }
1.5 synchronized 修饰不同对象/不同对象非静态方法时不会互斥
2. 类锁
类锁的作用范围是类级别,不会因为同一个类的不同对象执行而失效,但如果是两个类的锁则会失效
2.1synchronized修饰同一个类的两个静态方法时互斥(线程互斥是指某一代码同时只能允许一个线程访问)
public class dynamicMethodtest { public static void main(String[] args) { //这两个线程是相同class的不同对象 new Thread(() -> { // new了一个ClassLock对象 new ClassLock().test1(); }).start(); new Thread(() -> { // new了另一个ClassLock对象 new ClassLock().test2(); }).start(); } } class ClassLock{ public synchronized static void test1(){ // synchronized (ClassLock.class) { System.out.println("test1 " + new Date() + " " + Thread.currentThread().getName() + " begin..."); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("test1 " + new Date() + " " + Thread.currentThread().getName() + " end..."); // } } public synchronized static void test2(){ // synchronized (StaticLock2.class) { System.out.println("test2 " + new Date() + " " + Thread.currentThread().getName() + " begin..."); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("test2 " + new Date() + " " + Thread.currentThread().getName() + " end..."); // } } } output: test1 Thu Jan 12 15:34:46 CST 2023 Thread-0 begin... test1 Thu Jan 12 15:34:47 CST 2023 Thread-0 end... test2 Thu Jan 12 15:34:47 CST 2023 Thread-1 begin... test2 Thu Jan 12 15:34:48 CST 2023 Thread-1 end...
2.2 synchronized修饰同一类则会互斥(修饰不同类不会互斥)
public class dynamicMethodtest { public static void main(String[] args) { //这两个线程是相同class的不同对象 new Thread(() -> { // new了一个ClassLock对象 new ClassLock().test1(); }).start(); new Thread(() -> { // new了另一个ClassLock对象 new ClassLock().test2(); }).start(); } } class ClassLock{ public static void test1(){ synchronized (ClassLock.class) { System.out.println("test1 " + new Date() + " " + Thread.currentThread().getName() + " begin..."); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("test1 " + new Date() + " " + Thread.currentThread().getName() + " end..."); } } public static void test2(){ synchronized (ClassLock.class) { System.out.println("test2 " + new Date() + " " + Thread.currentThread().getName() + " begin..."); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("test2 " + new Date() + " " + Thread.currentThread().getName() + " end..."); } } } output: test1 Thu Jan 12 15:31:04 CST 2023 Thread-0 begin... test1 Thu Jan 12 15:31:05 CST 2023 Thread-0 end... test2 Thu Jan 12 15:31:05 CST 2023 Thread-1 begin... test2 Thu Jan 12 15:31:06 CST 2023 Thread-1 end...
如果synchronized修饰的是两个不同类,从如下输出可以看出线程是不会互斥的
public class dynamicMethodtest { public static void main(String[] args) { //这两个线程是相同class的不同对象 new Thread(() -> { // new了一个ClassLock对象 new ClassLock().test1(); }).start(); new Thread(() -> { // new了另一个ClassLock对象 new ClassLock().test2(); }).start(); } } class ClassLock{ public static void test1(){ synchronized (ClassLock.class) { System.out.println("test1 " + new Date() + " " + Thread.currentThread().getName() + " begin..."); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("test1 " + new Date() + " " + Thread.currentThread().getName() + " end..."); } } public static void test2(){ synchronized (StaticLock2.class) { System.out.println("test2 " + new Date() + " " + Thread.currentThread().getName() + " begin..."); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("test2 " + new Date() + " " + Thread.currentThread().getName() + " end..."); } } } output: test2 Thu Jan 12 15:32:36 CST 2023 Thread-1 begin... test1 Thu Jan 12 15:32:36 CST 2023 Thread-0 begin... test1 Thu Jan 12 15:32:37 CST 2023 Thread-0 end... test2 Thu Jan 12 15:32:37 CST 2023 Thread-1 end..
2.3 synchronized修饰同一类的静态方法和当前类时互斥(修饰当前类和另一个类静态方法不互斥)
2.4 synchronized修饰同一个静态对象时互斥(修饰不同静态对象不会互斥)
参考文献:
https://mp.weixin.qq.com/s?__biz=MzAwMDczMjMwOQ==&mid=2247484189&idx=1&sn=b9a5f7d11fc960015649032006cca785&chksm=9ae53ee7ad92b7f191880920306fd568ea396096b0b3c5eae6889aa22eb18c6b6a434439c1db&scene=27