内置锁:每个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();
- }
- }
控制台输出:Java代码
- test开始..
- test开始..
- test结束..
- test结束..
很明显,synchronized修饰普通方法的时候,锁住的是对象的实例,代码示例中,testSync 和testSync2分别都是TestSynchronized对象的实例,他们两个都可以同时进入synchronized修饰的普通方法,所以得出,synchronized修饰普通方法的时候,锁住的是对象的实例。
第二、修饰静态方法
代码示例:
Java代码
- 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();
- }
- }
控制台输出:Java代码
- test开始..
- test结束..
- 线程Thread-0程序运行时间:5000ms
- test开始..
- test结束..
- 线程Thread-1程序运行时间:5000ms
可以看出,synchronized修饰静态方法的时候,起到了锁的作用,线程分别获得锁后才进入静态方法中,但是尽量不要使用synchronized修饰静态方法,因为它锁住的是整个类,也就是说,在整个类中的其他synchronized修饰的方法都会被锁住。
示例代码如下:
Java代码
- 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();
- }
- }
控制台输出:Java代码
- test开始..
- test结束..
- 线程Thread-0程序运行时间:5000ms
- test2开始..
- test2结束..
- 线程Thread-1程序运行时间:5001ms
可以看出,线程Thread-0进入synchronized修饰的静态方法staticOut()的时候,这个类就被锁住了,线程Thread-1无法获得锁,只能等待锁的释放后才能进入方法staticOut2()。所以使用synchronized修饰静态方法需要慎重。
第三、修饰代码块
示例代码:
Java代码
- 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();
- }
- }
控制台输出:Java代码
- test开始..
- test开始..
- test结束..
- test结束..
- 线程Thread-0程序运行时间:5000ms
- 线程Thread-1程序运行时间:5000ms
synchronized修饰代码块时,锁住的是一个对象 synchronized (lock) 即synchronized后面括号里的内容,因为两个对象创建了两个不同对象实例lock,所以两个对象的线程都可以同时进入synchronized修饰代码块。如果想锁住synchronized修饰的代码块,只需要确定synchronized后面括号里锁住同一
对象即可,常用的方法如下:
1、synchronized锁这个类对应的Class对象。
实例代码:
Java代码
- 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();
- }
- }
控制台输出:Java代码
- test开始..
- test结束..
- 线程Thread-0程序运行时间:5001ms
- test开始..
- test结束..
- 线程Thread-1程序运行时间:5002ms
让synchronized锁这个类对应的Class对象这种方法实现了全局锁的效果,和synchronized修饰静态方法一样(static synchronized方法也是相当于全局锁),整个类就被锁住了,所以此方法一样需要慎重使用。
2、创建一个单例对象,锁住的是该单例对象,单例对象只有一个实例。
Java代码
- 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代码
- 指针地址:java.lang.Object@ce407e7
- test开始..
- test结束..
- 线程Thread-0程序运行时间:1000ms
- 指针地址:java.lang.Object@ce407e7
- test开始..
- test结束..
- 线程Thread-1程序运行时间:1001ms
保证了单例对象lock 的实例唯一性,synchronized锁住同一个固定对象,从控制台上可以看出,访问代码块的对象指针地址是一样的。
3、访问该代码块的对象唯一
示例代码:Java代码
- 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();
- }
- }
控制台输出:Java代码
- 指针地址:com.test.test.TestSynchronized@1dca18a4
- test开始..
- test结束..
- 线程Thread-0程序运行时间:1000ms
- 指针地址:com.test.test.TestSynchronized@1dca18a4
- test开始..
- test结束..
- 线程Thread-1程序运行时间:1000ms
synchronized后面括号的this指的是访问该代码块的对象,从控制台上可以看出,访问代码块的对象指针地址是一样的,从而可以得出他们是固定同一对象。