在本练习中,我们将尝试同步代码块。在该代码块中,我们将获得对象的锁定,以便在代码块执行时其他线程无法修改该对象。我们将创建三个线程,所有线程都将尝试操纵同一对象。每个线程将输出单个字母100次,然后将该字母加1。我们将使用的对象是StringBuffer。我们可以在String对象上进行同步,但是一旦创建字符串就无法对其进行修改,因此如果不生成新的String对象,我们将无法递增字母。最终输出应为100 A,100 B和100 C,它们都应连续显示。
创建一个类并扩展Thread类。
重写Thread的run()方法。这是同步的代码块将要去的地方。
为了使三个线程对象共享同一对象,我们将需要创建一个构造函数,该构造函数在参数中接受StringBuffer对象。
同步的代码块将从步骤3获得对StringBuffer对象的锁定。
在该块中,输出StringBuffer 100次,然后递增StringBuffer中的字母。您可以在第5章中找到有关StringBuffer(StringBuilder)的方法,以帮助解决此问题。
最后,在main()方法中,使用字母A创建单个StringBuffer对象,然后创建类的三个实例并启动所有三个实例。
解:
公共类OCJPThread扩展了线程{
StringBuffer sb;
public OCJPThread(StringBuffer sb) {
this.sb =sb;
}
public void run(){
synchronized (sb) {
System.out.println(Thread.currentThread().getName());
for(int i=1;i<=100;i++)
System.out.print(sb);
System.out.println();
sb.setCharAt(0, (char)(sb.charAt(0)+1));
}
}
public static void main(String[] args) {
StringBuffer sb = new StringBuffer("A");
OCJPThread t1 =new OCJPThread(sb);
OCJPThread t2 =new OCJPThread(sb);
OCJPThread t3 =new OCJPThread(sb);
t1.start();
t2.start();
t3.start();
}
}
输出
线程-0
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
线程2
bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
线程1
CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC
问题:如果我使用Stringbuilder或StringBuffer,则无所谓,因为我使用的是同步块。
但是,如果我使用synced(this)而不是stringbuffer对象。输出不可预测。由于字符串缓冲区已经同步,为什么我们需要自己做呢?
最佳答案
StringBuffer
是同步的,但仅在每个单独的方法调用上同步。这意味着当您不使用同步时,打印字符串缓冲区值的部分不会在同步块内完成。在此期间,其他线程之一可以更新缓冲区。发生这种情况时,打印件将显示新值。您真的不知道哪个线程会首先更新缓冲区。
同样,线程可以在对sb.charAt()
的调用和对sb.setCharAt()
的调用之间更改值。因此,增量本身的结果可能是不可预测的。
注意:在this
上进行同步意味着每个线程都在不同的锁上进行同步,因此就好像根本不同步一样。