内置锁:每个java对象都可以用做一个实现同步的锁,这些锁称为内置锁。线程进入同步代码块或方法的时候会自动获得该锁,在退出同步代码块或方法时会释放该锁。获得内置锁的唯一途径就是进入这个锁的保护的同步代码块或方法。
互斥锁:内置锁是一个互斥锁,这就是意味着最多只有一个线程能够获得该锁,当线程A尝试去获得线程B持有的内置锁时,线程A必须等待或者阻塞,直到线程B释放这个锁,如果B线程不释放这个锁,那么A线程将永远等待下去。
synchronized既是内置锁也是互斥锁
synchronized三种修饰
第一、修饰普通方法
代码示例:
public class TestSynchronized { public synchronized void out() throws InterruptedException { System.out.println("test开始.."); try { Thread.sleep(5000L); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("test结束.."); } public static void main(String[] args) { TestSynchronized testSync = new TestSynchronized(); TestSynchronized testSync2 = new TestSynchronized(); new Thread(() -> { try { testSync.out(); } catch (InterruptedException e) { e.printStackTrace(); } }).start(); new Thread(() -> { try { testSync2.out(); } catch (InterruptedException e) { e.printStackTrace(); } }).start(); } }控制台输出:
test开始.. test开始.. test结束.. test结束..很明显,synchronized修饰普通方法的时候,锁住的是对象的实例,代码示例中,testSync 和testSync2分别都是TestSynchronized对象的实例,他们两个都可以同时进入synchronized修饰的普通方法,所以得出,synchronized修饰普通方法的时候,锁住的是对象的实例。
第二、修饰静态方法
代码示例:
public class TestSynchronized { public static synchronized void staticOut() throws InterruptedException { long startTime = System.currentTimeMillis(); System.out.println("test开始.."); try { Thread.sleep(5000L); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("test结束.."); long endTime = System.currentTimeMillis(); System.out.println("线程"+Thread.currentThread().getName() + "程序运行时间:" + (endTime - startTime) + "ms"); } public static void main(String[] args) { TestSynchronized testSync = new TestSynchronized(); TestSynchronized testSync2 = new TestSynchronized(); new Thread(() -> { try { testSync.staticOut(); } catch (InterruptedException e) { e.printStackTrace(); } }).start(); new Thread(() -> { try { testSync2.staticOut(); } catch (InterruptedException e) { e.printStackTrace(); } }).start(); } }控制台输出:
test开始.. test结束.. 线程Thread-0程序运行时间:5000ms test开始.. test结束.. 线程Thread-1程序运行时间:5000ms可以看出,synchronized修饰静态方法的时候,起到了锁的作用,线程分别获得锁后才进入静态方法中,但是尽量不要使用synchronized修饰静态方法,因为它锁住的是整个类,也就是说,在整个类中的其他synchronized修饰的方法都会被锁住。
示例代码如下:
public class TestSynchronized { public static synchronized void staticOut() throws InterruptedException { long startTime = System.currentTimeMillis(); System.out.println("test开始.."); try { Thread.sleep(5000L); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("test结束.."); long endTime = System.currentTimeMillis(); System.out.println("线程"+Thread.currentThread().getName() + "程序运行时间:" + (endTime - startTime) + "ms"); } public static synchronized void staticOut2() throws InterruptedException { long startTime = System.currentTimeMillis(); System.out.println("test2开始.."); try { Thread.sleep(5000L); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("test2结束.."); long endTime = System.currentTimeMillis(); System.out.println("线程"+Thread.currentThread().getName() + "程序运行时间:" + (endTime - startTime) + "ms"); } public static void main(String[] args) { TestSynchronized testSync = new TestSynchronized(); TestSynchronized testSync2 = new TestSynchronized(); new Thread(() -> { try { testSync.staticOut(); } catch (InterruptedException e) { e.printStackTrace(); } }).start(); new Thread(() -> { try { testSync2.staticOut2(); } catch (InterruptedException e) { e.printStackTrace(); } }).start(); } }控制台输出:
test开始.. test结束.. 线程Thread-0程序运行时间:5000ms test2开始.. test2结束.. 线程Thread-1程序运行时间:5001ms可以看出,线程Thread-0进入synchronized修饰的静态方法staticOut()的时候,这个类就被锁住了,线程Thread-1无法获得锁,只能等待锁的释放后才能进入方法staticOut2()。所以使用synchronized修饰静态方法需要慎重。
第三、修饰代码块
示例代码:
public class TestSynchronized { private Object lock = new Object(); public void lockOut(){ synchronized(lock){ long startTime = System.currentTimeMillis(); System.out.println("test开始.."); try { Thread.sleep(5000L); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("test结束.."); long endTime = System.currentTimeMillis(); System.out.println("线程"+Thread.currentThread().getName() + "程序运行时间:" + (endTime - startTime) + "ms"); } } public static void main(String[] args) { TestSynchronized testSync = new TestSynchronized(); TestSynchronized testSync2 = new TestSynchronized(); new Thread(() -> { testSync.lockOut(); }).start(); new Thread(() -> { testSync2.lockOut(); }).start(); } }控制台输出:
test开始.. test开始.. test结束.. test结束.. 线程Thread-0程序运行时间:5000ms 线程Thread-1程序运行时间:5000mssynchronized修饰代码块时,锁住的是一个对象 synchronized (lock) 即synchronized后面括号里的内容,因为两个对象创建了两个不同对象实例lock,所以两个对象的线程都可以同时进入synchronized修饰代码块。如果想锁住synchronized修饰的代码块,只需要确定synchronized后面括号里锁住同一
对象即可,常用的方法如下:
1、synchronized锁这个类对应的Class对象。
实例代码:
public class TestSynchronized { // private Object lock = new Object(); public void lockOut(){ synchronized(TestSynchronized.class){ long startTime = System.currentTimeMillis(); System.out.println("test开始.."); try { Thread.sleep(5000L); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("test结束.."); long endTime = System.currentTimeMillis(); System.out.println("线程"+Thread.currentThread().getName() + "程序运行时间:" + (endTime - startTime) + "ms"); } } public static void main(String[] args) { TestSynchronized testSync = new TestSynchronized(); TestSynchronized testSync2 = new TestSynchronized(); new Thread(() -> { testSync.lockOut(); }).start(); new Thread(() -> { testSync2.lockOut(); }).start(); } }控制台输出:
test开始.. test结束.. 线程Thread-0程序运行时间:5001ms test开始.. test结束.. 线程Thread-1程序运行时间:5002ms让synchronized锁这个类对应的Class对象这种方法实现了全局锁的效果,和synchronized修饰静态方法一样(static synchronized方法也是相当于全局锁),整个类就被锁住了,所以此方法一样需要慎重使用。
2、创建一个单例对象,锁住的是该单例对象,单例对象只有一个实例。
public class TestSynchronized { private volatile static Object lock = new Object(); public void lockOut(){ synchronized(lock){ System.out.println("指针地址:" + lock.toString()); long startTime = System.currentTimeMillis(); System.out.println("test开始.."); try { Thread.sleep(5000L); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("test结束.."); long endTime = System.currentTimeMillis(); System.out.println("线程"+Thread.currentThread().getName() + "程序运行时间:" + (endTime - startTime) + "ms"); } } public static void main(String[] args) { TestSynchronized testSync = new TestSynchronized(); TestSynchronized testSync2 = new TestSynchronized(); new Thread(() -> { testSync.lockOut(); }).start(); new Thread(() -> { testSync2.lockOut(); }).start(); } }控制台输出:
指针地址:java.lang.Object@ce407e7 test开始.. test结束.. 线程Thread-0程序运行时间:1000ms 指针地址:java.lang.Object@ce407e7 test开始.. test结束.. 线程Thread-1程序运行时间:1001ms保证了单例对象lock 的实例唯一性,synchronized锁住同一个固定对象,从控制台上可以看出,访问代码块的对象指针地址是一样的。
3、访问该代码块的对象唯一
示例代码:
public class TestSynchronized { // private volatile static Object lock = new Object(); public void lockOut(){ synchronized(this){ System.out.println("指针地址:" + this.toString()); long startTime = System.currentTimeMillis(); System.out.println("test开始.."); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("test结束.."); long endTime = System.currentTimeMillis(); System.out.println("线程"+Thread.currentThread().getName() + "程序运行时间:" + (endTime - startTime) + "ms"); } } public static void main(String[] args) { TestSynchronized testSync = new TestSynchronized(); new Thread(() -> { testSync.lockOut(); }).start(); new Thread(() -> { testSync.lockOut(); }).start(); } }控制台输出:
指针地址:com.test.test.TestSynchronized@1dca18a4 test开始.. test结束.. 线程Thread-0程序运行时间:1000ms 指针地址:com.test.test.TestSynchronized@1dca18a4 test开始.. test结束.. 线程Thread-1程序运行时间:1000mssynchronized后面括号的this指的是访问该代码块的对象,从控制台上可以看出,访问代码块的对象指针地址是一样的,从而可以得出他们是固定同一对象。
相关推荐
这份资源旨在详细讲解 Java 中的 Locks 框架,特别关注 ReentrantLock 的使用和原理。...通过这份资源,您将获得关于 Locks 框架和 ReentrantLock 的深入理解,从基本用法到高级功能,从对比分析到最佳实践。
众所周知,synchronized锁是JAVA的关键字,按理说是JAVA语言内置的特性,那为什么还要使用Lock呢 我们先说一说synchronized,当一个方法或者代码块被synchronized修饰,并执行到此方法或者代码块时,获取到锁并执行...
这份资源旨在介绍 Java Locks 框架中的 Lock 接口及其相关内容。Lock 接口是 Locks 框架的核心,提供...通过这份资源,您将获得关于 Locks 框架中 Lock 接口的深入理解,从基本用法到高级功能,从可重入性到条件变量。
您将了解线程安全的实现、死锁的避免策略、线程池的使用方法、线程上下文切换的原因与优化、线程同步与互斥的区别、volatile关键字的作用、synchronized关键字的用法等。同时,我们还探讨了多线程编程中 通过研究和...
本资源致力于向您介绍 ...介绍如何使用 synchronized 关键字来实现线程的同步和阻塞。 线程间通信: 详解线程间通信的方法,包括 wait、notify 和 notifyAll 方法的使用。讲解如何通过这些方法实现线程的协作和同步。
│ 高并发编程第一阶段19讲、结合jconsole,jstack以及汇编指令认识synchronized关键字.mp4 │ 高并发编程第一阶段20讲、同步代码块以及同步方法之间的区别和关系.mp4 │ 高并发编程第一阶段21讲、通过实验分析...
如何使用方法的返回值? 166 7.4.4 使用return结束方法 166 7.5 方法重载(overload):给汽车加速添个限制 168 7.5.1 什么是方法的签名 168 7.5.2 什么是重载?为什么要重载? 168 7.5.3 给汽车加个重载的方法...
如何使用方法的返回值? 166 7.4.4 使用return结束方法 166 7.5 方法重载(overload):给汽车加速添个限制 168 7.5.1 什么是方法的签名 168 7.5.2 什么是重载?为什么要重载? 168 7.5.3 给汽车加个重载的方法...