先从最简单的开始
***************************************
分割线 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休眠完成,释放锁
.........................我是一个方法结束的分割线.................................
也就是说,如上的这种一个相同字符串重复不同变量申明两次,这样也是算一个锁的,
呵呵,这个和字符串在内存中的保存机制有关。
2:对象的静态方法将synchronized加在方法上,则锁对象为字节码文件***************************************
分割线 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
3:任意对象都可以作为锁。