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

先从最简单的开始

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

分割线   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:任意对象都可以作为锁。






















  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值