java在不同情况下锁对象探索

来源:互联网 发布:virtualbox mac好用么 编辑:程序博客网 时间:2024/05/16 05:39

先从最简单的开始

***************************************

分割线   demo1

***************************************

public static void main(String args[]){   final  Class1 c1 = new Class1();    Thread t1 = new Thread(new Runnable() {        @Override        public void run() {            c1.test1();        }    });    Thread t2 = new Thread(new Runnable() {        @Override        public void run() {            c1.test1();        }    });    t1.start();    t2.start();}
public class Class1 {    public synchronized void test1(){        System.out.println("线程"+Thread.currentThread().getName()+"进入test1");        System.out.println("线程"+Thread.currentThread().getName()+"即将休眠两秒");        try {            Thread.sleep(2000);        } catch (InterruptedException e) {            e.printStackTrace();        }        System.out.println("当前线程"+Thread.currentThread().getName()+"休眠完成,释放锁");
        System.out.println(".........................我是一个方法结束的分割线.................................");
}}
打印结果如下
线程Thread-0进入test1线程Thread-0即将休眠两秒当前线程Thread-0休眠完成,释放锁.........................我是一个方法结束的分割线.................................线程Thread-1进入test1线程Thread-1即将休眠两秒当前线程Thread-1休眠完成,释放锁.........................我是一个方法结束的分割线.................................

***************************************

分割线   demo2

***************************************

大家对于demo1的打印结果应该没有什么疑惑,加了同步锁了,第一个线程要在里面待上两秒
那么第二个线程自然需要等待,ok,我们把demo1的代码稍作修改
public static void main(String args[]){   final  Class1 c1 = new Class1();    Thread t1 = new Thread(new Runnable() {        @Override        public void run() {            c1.test1();        }    });    Thread t2 = new Thread(new Runnable() {        @Override        public void run() {            c1.test2();        }    });    t1.start();    t2.start();}

public class Class1 {    public synchronized void test1(){        System.out.println("线程"+Thread.currentThread().getName()+"进入test1");        System.out.println("线程"+Thread.currentThread().getName()+"即将休眠两秒");        try {            Thread.sleep(2000);        } catch (InterruptedException e) {            e.printStackTrace();        }        System.out.println("当前线程"+Thread.currentThread().getName()+"休眠完成,释放锁");
System.out.println(".........................我是一个方法结束的分割线.................................");    }    public synchronized void test2(){        System.out.println("线程"+Thread.currentThread().getName()+"进入test2");        System.out.println("线程"+Thread.currentThread().getName()+"即将休眠两秒");        try {            Thread.sleep(2000);        } catch (InterruptedException e) {            e.printStackTrace();        }        System.out.println("当前线程"+Thread.currentThread().getName()+"休眠完成,释放锁");
System.out.println(".........................我是一个方法结束的分割线.................................");
}}
打印结果如下
线程Thread-0进入test1线程Thread-0即将休眠两秒当前线程Thread-0休眠完成,释放锁
.........................我是一个方法结束的分割线.................................
线程Thread-1进入test2
线程Thread-1即将休眠两秒
当前线程Thread-1休眠完成,释放锁
.........................我是一个方法结束的分割线.................................
这个结果也是没有什么疑问的,同一个对象里面,两个方法都加了锁了,
用两个不同的线程进行访问,自然第二个线程要等第一个线程出来之后再进去。

***************************************

分割线   demo3

***************************************

继续修改,

把demo2中的test2方法修改如下

public void test2() {    synchronized (this) {        System.out.println("线程" + Thread.currentThread().getName() + "进入test2");        System.out.println("线程" + Thread.currentThread().getName() + "即将休眠两秒");        try {            Thread.sleep(2000);        } catch (InterruptedException e) {            e.printStackTrace();        }        System.out.println("当前线程" + Thread.currentThread().getName() + "休眠完成,释放锁");        System.out.println(".........................我是一个方法结束的分割线.................................");    }}

打印结果

线程Thread-0进入test1线程Thread-0即将休眠两秒当前线程Thread-0休眠完成,释放锁.........................我是一个方法结束的分割线.................................线程Thread-1进入test2线程Thread-1即将休眠两秒当前线程Thread-1休眠完成,释放锁.........................我是一个方法结束的分割线.................................

说明什么?说明了在对象的方法上加上synchronized,则其锁对象,就是this,

下面提供一个反证。

***************************************

分割线   demo4

***************************************

把test2方法修改如下,

public String mObj = "HaHaHa";public void test2() {    synchronized (mObj) {        System.out.println("线程" + Thread.currentThread().getName() + "进入test2");        System.out.println("线程" + Thread.currentThread().getName() + "即将休眠两秒");        try {            Thread.sleep(2000);        } catch (InterruptedException e) {            e.printStackTrace();        }        System.out.println("当前线程" + Thread.currentThread().getName() + "休眠完成,释放锁");        System.out.println(".........................我是一个方法结束的分割线.................................");    }}
则打印结果如下:
线程Thread-0进入test1线程Thread-1进入test2线程Thread-0即将休眠两秒线程Thread-1即将休眠两秒当前线程Thread-1休眠完成,释放锁.........................我是一个方法结束的分割线.................................当前线程Thread-0休眠完成,释放锁.........................我是一个方法结束的分割线.................................
我们看到,线程访问无序了,问什么,因为这次test1和test2用了不同的锁对象了。
同时,我们还可以得出一个结论,那就是对象可以访问的非静态方法上直接加的synchronized的锁对象就是this。

***************************************

分割线   demo5

***************************************

继续修改,如果我们把test2改成静态方法会怎么样?

public static synchronized void test2() {        System.out.println("线程" + Thread.currentThread().getName() + "进入test2");        System.out.println("线程" + Thread.currentThread().getName() + "即将休眠两秒");        try {            Thread.sleep(2000);        } catch (InterruptedException e) {            e.printStackTrace();        }        System.out.println("当前线程" + Thread.currentThread().getName() + "休眠完成,释放锁");        System.out.println(".........................我是一个方法结束的分割线.................................");}

打印结果如下

线程Thread-0进入test1
线程Thread-1进入test2
线程Thread-0即将休眠两秒
线程Thread-1即将休眠两秒
当前线程Thread-1休眠完成,释放锁
.........................我是一个方法结束的分割线.................................
当前线程Thread-0休眠完成,释放锁
.........................我是一个方法结束的分割线.................................


说明什么,说明了静态方法使用的锁对象不是this,

其实想想也对,静态方法内部也无法引用的this,那么,静态方法使用的到底是哪个锁对象?

***************************************

分割线   demo6

***************************************

我们继续探索,先把test1也改成静态方法,那么

public static  synchronized void test1() {    System.out.println("线程" + Thread.currentThread().getName() + "进入test1");    System.out.println("线程" + Thread.currentThread().getName() + "即将休眠两秒");    try {        Thread.sleep(2000);    } catch (InterruptedException e) {        e.printStackTrace();    }    System.out.println("当前线程" + Thread.currentThread().getName() + "休眠完成,释放锁");    System.out.println(".........................我是一个方法结束的分割线.................................");}

线程Thread-0进入test1
线程Thread-0即将休眠两秒
当前线程Thread-0休眠完成,释放锁
.........................我是一个方法结束的分割线.................................
线程Thread-1进入test2
线程Thread-1即将休眠两秒
当前线程Thread-1休眠完成,释放锁
.........................我是一个方法结束的分割线.................................


说明test1和test2都申明为静态方法时,其使用的是同一把锁,那么这把锁到底是哪个对象呢?

***************************************

分割线   demo7

***************************************

继续修改,现在,我们的test1()和test2()方法都是静态方法了。

那么如果我们把test2()改为非静态方法,并且稍作修改,会怎么样

public  void test2() {    synchronized (Class1.class){        System.out.println("线程" + Thread.currentThread().getName() + "进入test2");        System.out.println("线程" + Thread.currentThread().getName() + "即将休眠两秒");        try {            Thread.sleep(2000);        } catch (InterruptedException e) {            e.printStackTrace();        }        System.out.println("当前线程" + Thread.currentThread().getName() + "休眠完成,释放锁");        System.out.println(".........................我是一个方法结束的分割线.................................");    }}

打印结果会怎么样呢?

线程Thread-0进入test1
线程Thread-0即将休眠两秒
当前线程Thread-0休眠完成,释放锁
.........................我是一个方法结束的分割线.................................
线程Thread-1进入test2
线程Thread-1即将休眠两秒
当前线程Thread-1休眠完成,释放锁
.........................我是一个方法结束的分割线.................................


好吧,看来我们找到静态方法的锁了,就是字节码对象。


***************************************

分割线   demo8

***************************************

俗话说,一把钥匙开一把锁,

使用synchronized关键字的时候一定要清楚自己使用的到底使用的是哪把锁。

我们进一步修改,

public static   void test1() {    synchronized (Class1.class){        System.out.println("线程" + Thread.currentThread().getName() + "进入test1");        System.out.println("线程" + Thread.currentThread().getName() + "即将休眠两秒");        try {            Thread.sleep(2000);        } catch (InterruptedException e) {            e.printStackTrace();        }        System.out.println("当前线程" + Thread.currentThread().getName() + "休眠完成,释放锁");        System.out.println(".........................我是一个方法结束的分割线.................................");    }}public  void test2() {    synchronized (Class1.class){        System.out.println("线程" + Thread.currentThread().getName() + "进入test2");        System.out.println("线程" + Thread.currentThread().getName() + "即将休眠两秒");        try {            Thread.sleep(2000);        } catch (InterruptedException e) {            e.printStackTrace();        }        System.out.println("当前线程" + Thread.currentThread().getName() + "休眠完成,释放锁");        System.out.println(".........................我是一个方法结束的分割线.................................");    }}
打印结果毫无疑问
线程Thread-0进入test1线程Thread-0即将休眠两秒当前线程Thread-0休眠完成,释放锁.........................我是一个方法结束的分割线.................................线程Thread-1进入test2线程Thread-1即将休眠两秒当前线程Thread-1休眠完成,释放锁.........................我是一个方法结束的分割线.................................

***************************************

分割线   demo9

***************************************

那么,既然如此,平时使用synchronized关键字的时候是否可以使用自定义的锁?当然可以。

大家都知道,java中的字符串其实是String类来封装的,其实就是个对象,

而根据java编程思想-第四版这本书的描述,所有对象都有单一的锁,

所以,可以使用字符串作为锁(其实前面的demo中已经用到了)

我们继续修改demo代码

public static String mObj = "HaHaHa";public   void test1() {    synchronized (mObj){        System.out.println("线程" + Thread.currentThread().getName() + "进入test1");        System.out.println("线程" + Thread.currentThread().getName() + "即将休眠两秒");        try {            Thread.sleep(2000);        } catch (InterruptedException e) {            e.printStackTrace();        }        System.out.println("当前线程" + Thread.currentThread().getName() + "休眠完成,释放锁");        System.out.println(".........................我是一个方法结束的分割线.................................");    }}public  void test2() {    synchronized (mObj){        System.out.println("线程" + Thread.currentThread().getName() + "进入test2");        System.out.println("线程" + Thread.currentThread().getName() + "即将休眠两秒");        try {            Thread.sleep(2000);        } catch (InterruptedException e) {            e.printStackTrace();        }        System.out.println("当前线程" + Thread.currentThread().getName() + "休眠完成,释放锁");        System.out.println(".........................我是一个方法结束的分割线.................................");    }}

打印结果如下

线程Thread-0进入test1
线程Thread-0即将休眠两秒
当前线程Thread-0休眠完成,释放锁
.........................我是一个方法结束的分割线.................................
线程Thread-1进入test2
线程Thread-1即将休眠两秒
当前线程Thread-1休眠完成,释放锁
.........................我是一个方法结束的分割线.................................


看来,使用字符串作为锁对象,是完全成功的。


***************************************

分割线   demo10

***************************************

另外这里还有一个有趣的现象

public static String mObj = "HaHaHa";public static String mObj2 = "HaHaHa";public   void test1() {    synchronized (mObj){        System.out.println("线程" + Thread.currentThread().getName() + "进入test1");        System.out.println("线程" + Thread.currentThread().getName() + "即将休眠两秒");        try {            Thread.sleep(2000);        } catch (InterruptedException e) {            e.printStackTrace();        }        System.out.println("当前线程" + Thread.currentThread().getName() + "休眠完成,释放锁");        System.out.println(".........................我是一个方法结束的分割线.................................");    }}public  void test2() {    synchronized (mObj2){        System.out.println("线程" + Thread.currentThread().getName() + "进入test2");        System.out.println("线程" + Thread.currentThread().getName() + "即将休眠两秒");        try {            Thread.sleep(2000);        } catch (InterruptedException e) {            e.printStackTrace();        }        System.out.println("当前线程" + Thread.currentThread().getName() + "休眠完成,释放锁");        System.out.println(".........................我是一个方法结束的分割线.................................");    }}
打印结果如下

线程Thread-0进入test1
线程Thread-0即将休眠两秒
当前线程Thread-0休眠完成,释放锁
.........................我是一个方法结束的分割线.................................
线程Thread-1进入test2
线程Thread-1即将休眠两秒
当前线程Thread-1休眠完成,释放锁
.........................我是一个方法结束的分割线.................................


也就是说,如上的这种一个相同字符串重复不同变量申明两次,这样也是算一个锁的,

呵呵,这个和字符串在内存中的保存机制有关。


***************************************

分割线   demo11

***************************************

public static String mObj = "HaHaHa";public static String mObj2 = "HaHaHa1";public   void test1() {    synchronized (mObj){        System.out.println("线程" + Thread.currentThread().getName() + "进入test1");        System.out.println("线程" + Thread.currentThread().getName() + "即将休眠两秒");        try {            Thread.sleep(2000);        } catch (InterruptedException e) {            e.printStackTrace();        }        System.out.println("当前线程" + Thread.currentThread().getName() + "休眠完成,释放锁");        System.out.println(".........................我是一个方法结束的分割线.................................");    }}public  void test2() {    synchronized (mObj2){        System.out.println("线程" + Thread.currentThread().getName() + "进入test2");        System.out.println("线程" + Thread.currentThread().getName() + "即将休眠两秒");        try {            Thread.sleep(2000);        } catch (InterruptedException e) {            e.printStackTrace();        }        System.out.println("当前线程" + Thread.currentThread().getName() + "休眠完成,释放锁");        System.out.println(".........................我是一个方法结束的分割线.................................");    }}

修改了一下,使两个字符串不同,这个时候打印结果如下

线程Thread-1进入test2
线程Thread-0进入test1
线程Thread-1即将休眠两秒
线程Thread-0即将休眠两秒
当前线程Thread-0休眠完成,释放锁
.........................我是一个方法结束的分割线.................................
当前线程Thread-1休眠完成,释放锁
.........................我是一个方法结束的分割线.................................


嗯,这样就不算是同一个锁了,符合预期!


总结一下

1:对象的非静态方法将synchronized加在方法上,则锁对象为this

2:对象的静态方法将synchronized加在方法上,则锁对象为字节码文件

3:任意对象都可以作为锁。