我们可以通过synchronized块来同步特定的静态或非静态方法要想实现这种需求必须为这些特性的方法定义一个类变量然后将这些方法的代码用synchronized块括起来并将这个类变量作为参数传入synchronized块下面的代码演示了如何同步特定的类方法
package mythread;
publicclass SyncThread extendsThread
{
privatestaticStringsync=;
privateStringmethodType=;
privatestaticvoidmethod(Strings)
{
synchronized(sync)
{
sync=s;
Systemoutprintln(s);
while(true);
}
}
publicvoidmethod()
{
method(method);
}
publicstaticvoidstaticMethod()
{
method(staticMethod);
}
publicvoidrun()
{
if(methodTypeequals(static))
staticMethod();
elseif(methodTypeequals(nonstatic))
method();
}
public SyncThread(StringmethodType)
{
thodType=methodType;
}
publicstaticvoidmain(String[]args)throwsException
{
SyncThread sample=new SyncThread(nonstatic);
SyncThread sample=new SyncThread(static);
samplestart();
samplestart();
}
}
运行结果如下
method
staticMethod
看到上面的运行结果很多读者可能感到惊奇在上面的代码中method和staticMethod方法使用了静态字符串变量sync进行同步这两个方法只能有一个同时执行而这两个方法都会执行行的无限循环语句因此输出结果只能是method和staticMethod其中之一但这个程序将这两个字符串都输出了
出现这种结果的愿意很简单我们看一下行就知道了原来在这一行将sync的值改变了在这里要说一下Java中的String类型String类型和Java中其他的复杂类型不同在使用String型变量时只要给这个变量赋一次值Java就会创建个新的String类型的实例如下面的代码所示
Strings=hello;
Systemoutprintln(shashCode());
s=world;
Systemoutprintln(shashCode());
在上面的代码中第一个s和再次赋值后的s的hashCode的值是不一样的由于创建String类的实例并不需要使用new因此在同步String类型的变量时要注意不要给这个变量赋值否则会使变量无法同步
由于在行已经为sync创建了一个新的实例假设method先执行当method方法执行了行的代码后sync的值就已经不是最初那个值了而method方法锁定的仍然是sync变量最初的那个值而在这时staticMethod正好执行到synchronized(sync)在staticMethod方法中要锁定的这个sync和method方法锁定的sync已经不是一个了因此这两个方法的同步性已经被破坏了
解决以上问题的方法当然是将行去掉在本例中加上这行只是为了说明使用类变量来同步方法时如果在synchronized块中将同步变量的值改变就会破坏方法之间的同步为了彻底避免这种情况发生在定义同步变量时可以使用final关键字如将上面的程序中的行可改成如下形式
privatefinalstaticStringsync=;
使用final关键字后sync只能在定义时为其赋值并且以后不能再修改如果在程序的其他地方给sync赋了值程序就无法编译通过在Eclipse等开发工具中会直接在错误的地方给出提示
我们可以从两个角度来理解synchronized块如果从类方法的角度来理解可以通过类变量来同步相应的方法如果从类变量的角度来理解可以使用synchronized块来保证某个类变量同时只能被一个方法访问不管从哪个角度来理解它们的实质都是一样的就是利用类变量来获得同步锁通过同步锁的互斥性来实现同步
注意在使用synchronized块时应注意synchronized块只能使用对象作为它的参数如果是简单类型的变量(如intcharboolean等)不能使用synchronized来同步