请选择 进入手机版 | 继续访问电脑版
本站特色:极好的技术研究氛围!所有技术发帖,必有回复! 做最受欢迎的Java论坛

疯狂Java联盟

 找回密码
 加入联盟
查看: 37|回复: 0

静态同步方法——多线程的陷阱之二

[复制链接]
发表于 2017-12-7 16:44:09 | 显示全部楼层 |阅读模式
本帖最后由 kongyeeku 于 2017-12-7 16:45 编辑

本文节选自《疯狂Java程序员的基本修养》

Java提供了synchronized关键字用于修饰方法,使用synchronized修饰的方法被称为同步方法。当然synchronized关键字除了修饰方法之外,还可以修饰普通代码块,使用synchronized修饰的代码块被成为同步代码块。
根据Java语法规定,任何线程进入同步方法、同步代码块之前,必须先获取同步方法、同步代码块对应的同步监视器。
对于同步代码块而言,程序必须显式为它指定同步监视器;对于同步非静态方法而言,该方法的同步监视器是this——也就是调用该方法的Java对象;对于静态的同步方法而言,该方法的同步监视器不是this,而是该类本身。
下面程序提供了一个静态的同步方法,还提供了一个同步代码块——同步代码块使用this作为同步监视器,也就是这两个同步程序单元并没有使用相同的同步监视器,因此它们可以同时并发执行——相互之间不会有任何影响。程序如下:
程序清单:codes\05\5.8\SynchronizedStatic.java
class SynchronizedStatic implements Runnable
{
         staticboolean staticFlag = true;
         publicstatic synchronized void test0()
         {
                   for(int i = 0; i < 100 ; i++ )
                   {
                            System.out.println("test0" +
                                     Thread.currentThread().getName()+ " " + i);
                   }
         }
         publicvoid test1()
         {
                   synchronized(this)
                   {
                            for(int i = 0; i < 100 ; i++ )
                            {
                                     System.out.println("test1" +
                                               Thread.currentThread().getName()+ " " + i);
                            }
                   }
         }
         publicvoid run()
         {
                   if(staticFlag)
                   {
                            staticFlag=false;
                            test0();
                   }
                   else
                   {
                            staticFlag=true;
                            test1();
                   }
         }
         publicstatic void main(String[] args)
                   throwsException
         {
                   SynchronizedStaticss = new SynchronizedStatic();
                   newThread(ss).start();
                   //保证第一条线程开始运行
                   Thread.sleep(10);
                   newThread(ss).start();
         }
}
上面程序中定义了一个SynchronizedStatic类,该类实现了Runnable接口,因此可作为线程的target来运行。SynchronizedStatic类通过一个staticFlag旗标控制线程使用哪个方法作为线程执行体。
q     staticFlag为真时,程序使用test0()方法作为线程执行体。
q     staticFlag为假时,程序使用test1()方法作为线程执行体。
程序第一次执行SynchronizedStatic对象作为target的线程时,staticFlag初始值为true,因此程序将以test0()方法作为线程执行体,而且程序将会把staticFlag修改为false;这使得第二次执行SynchronizedStatic对象作为target的线程时,程序将以test1()方法作为线程执行体。
程序主方法以SynchronizedStatic对象作为target启动了2条线程,一条将以test0()方法作为线程执行体,另外一条将以test1()方法作为线程执行体。运行上面程序,看到如图5.4所示的运行结果。
5.6.png
图5.4 静态同步方法和以this为同步监视器的同步代码块同时执行
从图5.4可以看出,静态同步方法可以和以this为同步监视器的同步代码块同时执行,当第一条线程(以test0()方法作为线程执行体的线程)进入同步代码块执行以后,该线程获得了对同步监视器(SynchronizedStatic类)的锁定;第二条线程(以test1()方法作为线程执行体的线程)尝试进入同步代码块执行,进入同步代码块之前,该线程必须获得对this引用(也就是ss变量所引用的对象)的锁定,因为第一条线程锁定的是SynchronizedStatic类,而不是ss变量所引用的对象,因此第二条线程完全可以获得对ss变量所引用的对象的锁定,因此系统可以切换到执行第二条线程,正如图5.4中所示的效果。
为了更好地证明静态的同步方法的同步监视器是当前类,我们可以将上面程序中同步代码块的同步监视器该为SynchronizedStatic类,也就是将上面test1()方法定义改为如下形式:
public void test1()
{
         synchronized(SynchronizedStatic.class)
         {
                   for(int i = 0; i < 100 ; i++ )
                   {
                            System.out.println("test1" +
                                     Thread.currentThread().getName()+ " " + i);
                   }
         }
}
将test1()方法改为上面形式之后,该同步代码块的同步监视器也是SynchronizedStatic类,也就是与同步静态方法test0()具有相同的同步监视器。再次运行该程序,将可以看到如图5.5所示效果:
5.7.png
图5.5 静态同步方法和以当前类为同步监视器的同步代码块不能同时执行
从图5.5可以看出,静态同步方法可以和以当前类为同步监视器的同步代码块不能同时执行,当第一条线程(以test0()方法作为线程执行体的线程)进入同步代码块执行以后,该线程获得了对同步监视器(SynchronizedStatic类)的锁定;第二条线程(以test1()方法作为线程执行体的线程)尝试进入同步代码块执行,进入同步代码块之前,该线程必须获得对SynchronizedStatic类的锁定,因为第一条线程已经锁定了SynchronizedStatic类,在第一条线程执行结束之前,它不会释放对SynchronizedStatic类的锁定,因此第二条线程无法获得对SynchronizedStatic类的锁定,因此只有等到第一条线程执行结束时系统可以切换到执行第二条线程,正如图5.5中所示的效果。

您需要登录后才可以回帖 登录 | 加入联盟

本版积分规则

视频、代码、电子书下载
请关注"疯狂图书"公众号
QQ交流1群: 545923995  未满

小黑屋|手机版|Archiver|疯狂Java联盟 ( 粤ICP备11063141号 )

GMT+8, 2017-12-18 20:35 , Processed in 0.334814 second(s), 7 queries , File On.

快速回复 返回顶部 返回列表