JAVA多线程之synchronized,对象锁,类锁

来源:互联网 发布:正规的淘宝模特兼职 编辑:程序博客网 时间:2024/05/21 09:23

        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() {@Overridepublic void run() {// TODO Auto-generated method stubtestSynchronized.test1();}});Thread t2 = new Thread(new Runnable() {@Overridepublic void run() {// TODO Auto-generated method stubtestSynchronized.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() {@Overridepublic void run() {// TODO Auto-generated method stubTestSynchronized.test1();}});Thread t2 = new Thread(new Runnable() {@Overridepublic void run() {// TODO Auto-generated method stubtestSynchronized.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() {@Overridepublic void run() {// TODO Auto-generated method stubTestSynchronized.test1();}});Thread t2 = new Thread(new Runnable() {@Overridepublic void run() {// TODO Auto-generated method stubtestSynchronized.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修饰方法,因为同一时刻只能有一个线程访问同步方法,其他线程必须排队等候,若是持有锁的线程没有退出方法,其他线程都无法访问同步方法,就会造成死锁。为了缓解这个问题(只是缓解,没有根本解决),修饰代码块的方式就出现了,这种方式可以避免锁住整个方法,只需锁住重要的部分就行。