在本练习中,我们将尝试同步一段代码。在该代码块中,我们将获得对象的锁定,以便其他线程在代码块执行时无法修改它。我们将创建三个线程,它们都将尝试操作同一个对象。每个线程将输出一个单个字母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 AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA 线程2 BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB 线程1 CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC
问题:如果我使用Stringbuilder或StringBuffer,则无关紧要,因为我使用的是同步块。
但是如果我使用synchronized(this)而不是stringbuffer对象。输出是不可预测的。由于字符串缓冲区已经同步,为什么我们需要自己做呢?
答案 0 :(得分:1)
您将相同的引用传递给所有三个线程。如果他们同步,那么他们都可以同时写入StringBuffer
。如果它们在公共StringBuffer
上同步,则一次只能有一个线程访问它。
答案 1 :(得分:0)
StringBuffer
已同步,但仅适用于每个单独的方法调用。这意味着当您不使用同步时,打印字符串缓冲区值的部分不会在同步块中完成。其中一个线程可以在此期间更新缓冲区。发生这种情况时,打印件将显示新值。您无法真正知道哪个线程会首先更新缓冲区。
此外,线程可能会更改对sb.charAt()
的调用与sb.setCharAt()
的调用之间的值。因此增量本身的结果可能是不可预测的。
注意:在this
上进行同步意味着每个线程正在同一个锁上进行同步,因此它根本就不会同步。