?
?内置锁:每个java对象都可以用做一个实现同步的锁,这些锁称为内置锁。线程进入同步代码块或方法的时候会自动获得该锁,在退出同步代码块或方法时会释放该锁。获得内置锁的唯一途径就是进入这个锁的保护的同步代码块或方法。
? 互斥锁:内置锁是一个互斥锁,这就是意味着最多只有一个线程能够获得该锁,当线程A尝试去获得线程B持有的内置锁时,线程A必须等待或者阻塞,直到线程B释放这个锁,如果B线程不释放这个锁,那么A线程将永远等待下去。 ? synchronized既是内置锁也是互斥锁 ? synchronized三种修饰 第一、修饰普通方法 代码示例:class="代码示例:" name="code">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程序运行时间:5000ms?synchronized修饰代码块时,锁住的是一个对象 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程序运行时间:1000ms?synchronized后面括号的this指的是访问该代码块的对象,从控制台上可以看出,访问代码块的对象指针地址是一样的,从而可以得出他们是固定同一对象。