JAVA内置锁:java内置锁的两种体现就是对象锁和类锁,java内置锁是一个互斥锁,同时只能被一个线程拿到,线程进入同步方法时自动获取内置锁,退出方法时,释放内置锁。当一个线程A拿到内置锁,其他线程只能等待A执行完毕释放锁,才能有机会获取内置锁进入同步方法。
对象锁:对象锁是用于对象实例方法,或者一个对象实例上的,每个对象实例只有一把锁,且各个对象实例的锁互不干扰。
类锁:类锁是用于类的静态方法或者一个类的class对象,类锁只有一把。
synchronized:用来修饰方法或者代码块,保证同一时刻只有一个线程执行被修饰的方法或代码块。
内置锁和synchronized的关系:被synchronized修饰的方法或者代码块,java会自动给这个方法或代码块加上内置锁,线程必须拿到这个锁才能访问被synchronized修饰的方法或代码块。
下面从synchronized的使用实例来加深对象锁和类锁的映像。
持有对象锁的synchronized实例:
package com.zw; /** * 对象锁: * 用于对象实例的方法,每一个JAVA的实例对象都有且只有一个内置锁,线程进入同步方法或同步代码块时自动获取锁。 * 类锁: * 用于静态方法,每个Class类只有一个类锁 * * 对象锁和类锁可同时出现,互不干扰 * @author Administrator * */ public class TestSynchronized { public static void main(String[] args) { final TestSynchronized testSynchronized = new TestSynchronized(); Thread t = new Thread(new Runnable() { @Override public void run() { // TODO Auto-generated method stub testSynchronized.test1(); } }); Thread t2 = new Thread(new Runnable() { @Override public void run() { // TODO Auto-generated method stub testSynchronized.test2(); } }); t.start(); t2.start(); } public synchronized void test1() { int i = 5; while(i-- > 0) { try { Thread.sleep(1 * 1000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("t " + i); } } public void test2() { synchronized (this) { int i = 5; while(i-- > 0) { try { Thread.sleep(1 * 1000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("m " + i); } } } }
输出结果:
t 4
t 3
t 2
t 1
t 0
m 4
m 3
m 2
m 1
m 0
上面的实例中同时启动两个线程,分别调用不同的被synchronized修饰的方法。从输出结果中可以看出两个线程的执行顺序是线程t先执行完毕后t2才开始执行,这是因为当前只创建了一个实例对象testSynchronized,每个实例对象只有一把锁,同步方法的执行前提是线程必须得拿到内置锁。t先拿到了对象实例的内置锁,t2只能等t执行结束才能开始调用被 synchronized修饰的方法。
持有类锁的synchronized实例:
package com.zw; /** * 对象锁: * 用于对象实例的方法,每一个JAVA的实例对象都有且只有一个内置锁,线程进入同步方法或同步代码块时自动获取锁。 * 类锁: * 用于静态方法,每个Class类只有一个类锁 * * 对象锁和类锁可同时出现,互不干扰 * @author Administrator * */ public class TestSynchronized { public static void main(String[] args) { final TestSynchronized testSynchronized = new TestSynchronized(); Thread t = new Thread(new Runnable() { @Override public void run() { // TODO Auto-generated method stub TestSynchronized.test1(); } }); Thread t2 = new Thread(new Runnable() { @Override public void run() { // TODO Auto-generated method stub testSynchronized.test2(); } }); t.start(); t2.start(); } public static synchronized void test1() { int i = 5; while(i-- > 0) { try { Thread.sleep(1 * 1000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("t " + i); } } public void test2() { synchronized (TestSynchronized.class) { int i = 5; while(i-- > 0) { try { Thread.sleep(1 * 1000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("m " + i); } } } }
输出结果:
t 4
t 3
t 2
t 1
t 0
m 4
m 3
m 2
m 1
m 0
从输出结果可以看出启动两个线程,还是t先执行,t2后执行,这是因为类锁只有一把,t先拿到,t2只能等待t结束。
同时持有类锁和对象锁的synchronized实例:
package com.zw; /** * 对象锁: * 用于对象实例的方法,每一个JAVA的实例对象都有且只有一个内置锁,线程进入同步方法或同步代码块时自动获取锁。 * 类锁: * 用于静态方法,每个Class类只有一个类锁 * * 对象锁和类锁可同时出现,互不干扰 * @author Administrator * */ public class TestSynchronized { public static void main(String[] args) { final TestSynchronized testSynchronized = new TestSynchronized(); Thread t = new Thread(new Runnable() { @Override public void run() { // TODO Auto-generated method stub TestSynchronized.test1(); } }); Thread t2 = new Thread(new Runnable() { @Override public void run() { // TODO Auto-generated method stub testSynchronized.test2(); } }); t.start(); t2.start(); } public static synchronized void test1() { int i = 5; while(i-- > 0) { try { Thread.sleep(1 * 1000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("t " + i); } } public void test2() { synchronized (this) { int i = 5; while(i-- > 0) { try { Thread.sleep(1 * 1000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("m " + i); } } } }
输出结果:
t 4
m 4
t 3
m 3
t 2
m 2
t 1
m 1
t 0
m 0
从输出结果可以看出两个线程交替执行,也就说明类锁和对象锁可同时出现,互不影响。
synchronized修饰方法和修饰代码块的区别
synchronized修饰方法,因为同一时刻只能有一个线程访问同步方法,其他线程必须排队等候,若是持有锁的线程没有退出方法,其他线程都无法访问同步方法,就会造成死锁。为了缓解这个问题(只是缓解,没有根本解决),修饰代码块的方式就出现了,这种方式可以避免锁住整个方法,只需锁住重要的部分就行。