同步块无法正常工作

这项练习直接来自Kathy Seirra和Bert Bates的SCJP

同步代码块

在本练习中,我们将尝试同步一段代码。 在该代码块中,我们将获得对象的锁定,以便其他线程在代码块执行时无法修改它。 我们将创建三个线程,它们都将尝试操作同一个对象。 每个线程将输出一个单个字母100次,然后将该字母递增1。 我们将使用的对象是StringBuffer。

我们可以在String对象上进行同步,但是一旦创建了字符串就无法修改它们,因此我们无法在不生成新的String对象的情况下增加字母。 最终输出应该有100个As,100个B和100个C,所有这些都是不间断的。

  1. 创建一个类并扩展Thread类。
  2. 重写Thread的run()方法。 这是同步代码块的用武之地。
  3. 为了让我们的三个线程对象共享同一个对象,我们需要创建一个在参数中接受StringBuffer对象的构造函数。
  4. synchronized代码块将从步骤3获取StringBuffer对象的锁定。
  5. 在块中,输出StringBuffer 100次,然后在StringBuffer中递增字母。 您可以在第6章中查看有助于此的StringBuffer方法。
  6. 最后,在main()方法中,使用字母A创建一个StringBuffer对象,然后创建我们类的三个实例并启动它们中的所有三个。

我已经为上面的练习编写了以下课程(而不是100我打印10个字符)

class MySyncBlockTest extends Thread { StringBuffer sb; MySyncBlockTest(StringBuffer sb) { this.sb=sb; } public static void main (String args[]) { StringBuffer sb = new StringBuffer("A"); MySyncBlockTest t1 = new MySyncBlockTest(sb); MySyncBlockTest t2 = new MySyncBlockTest(sb); MySyncBlockTest t3 = new MySyncBlockTest(sb); t1.start(); t2.start(); t3.start(); } public void run() { synchronized(this) { for (int i=0; i<10; i++) { System.out.print(sb); } System.out.println(""); if (sb.charAt(0)=='A') sb.setCharAt(0, 'B'); else sb.setCharAt(0, 'C'); } } } 

我期待输出类似于以下(10 As,10 B和10 C),但没有得到它。

 AAAAAAAAAA BBBBBBBBBB CCCCCCCCCC 

相反,我得到了不同的输出,如下所示,因为三个线程有机会在另一个完成之前进入循环。

 AAAAAAAAAAAAAAAAAA ABB ACCCCCCCC 

我的问题是为什么run方法中的synchronized块不起作用?

4. synchronized代码块将从步骤3获取StringBuffer对象的锁定。

好吧,你不是那样做的,不是吗?

 synchronized(this) { 

您正在MySyncBlockTest实例上的锁定,在该实例上MySyncBlockTest run()方法。 那…不会做任何事情。 对该资源没有争议; 每个Thread都有自己的MySyncBlockTest实例。

你应该锁定StringBuffer对象

  synchronized(sb) { for (int i=0; i<10; i++) { System.out.print(sb); } 

我也很困惑。 Brian提供的答案是正确的

 synchronized (this){ 

用于获取实例上的锁定。 当有一个类的单个实例和多个线程访问它时,它会很有用。

我写了以下程序来演示这个:

 package com.threads.chapter9; public class TestSunchronizedBlocksUsingRunnable implements Runnable { StringBuffer s; @Override public void run() { synchronized (this) { for (int i = 1; i <= 100; i++) { System.out.println(i); } char c = s.charAt(0); c++; s.setCharAt(0, c); } } TestSunchronizedBlocksUsingRunnable(StringBuffer s) { this.s = s; } public static void main(String[] args) { StringBuffer s = new StringBuffer("A"); TestSunchronizedBlocksUsingRunnable instance1 = new TestSunchronizedBlocksUsingRunnable(s); Thread thread1 = new Thread(instance1); Thread thread2 = new Thread(instance1); Thread thread3 = new Thread(instance1); thread1.start(); thread2.start(); thread3.start(); } } 

上面的代码将显示相同的输出,但场景完全不同。 所以你在synchronized块中使用的内容非常重要。

您想要的输出,即单个对象的多个线程可能的输出,请尝试此方法

 public class MultiThreading implements Runnable { public static void main(String [] arg) { MultiThreading a=new MultiThreading(20); Thread t0=new Thread(a); // Thread t1=new Thread(a); // Multiple Threads of single object Thread t2=new Thread(a); // t0.start(); t1.start(); t2.start(); } private int count; MultiThreading(int a) {this.count=a; } public void run() { synchronized(this){ String t_name=new String(""); t_name=Thread.currentThread().getName().toString(); for(int i=0;i 
  EXERCISE 9-2 from SCJP: Try this For Synchronozing on stringBuffer Object. It is giving required output. class letterThread extends Thread { StringBuffer putLetter; letterThread(StringBuffer str) { this.putLetter=str; } public void run() { synchronized (putLetter) { if(Thread.currentThread().getName().equals("th2")) { this.putLetter=new StringBuffer("B"); } else if(Thread.currentThread().getName().equals("th3")) { this.putLetter=new StringBuffer("C"); } for(int i=1;i<11;i++) { System.out.print(putLetter+""); } System.out.println(); } } } public class Manager { public static void main(String args[]) { StringBuffer str=new StringBuffer("A"); letterThread th1=new letterThread(str); letterThread th2=new letterThread(str); letterThread th3=new letterThread(str); th1.setName("th1"); th2.setName("th2"); th3.setName("th3"); th1.start(); th2.start(); th3.start(); } } 

来自SCJP7的练习13-2

 public class ThreadSyncronization extends Thread { StringBuffer sBuffer; public ThreadSyncronization(StringBuffer s,String name){ this.sBuffer=s; this.setName(name); } public ThreadSyncronization(){ } /** * @param args */ public static void main(String[] args) { StringBuffer ch = new StringBuffer("A"); Thread t1 = new ThreadSyncronization(ch,"first"); Thread t2 = new ThreadSyncronization(ch,"second"); Thread t3 = new ThreadSyncronization(ch,"third"); t1.start(); t2.start(); t3.start(); } public void run(){ synchronized (sBuffer) { System.out.println(this.getName()); for(int i=0;i<10;i++) { System.out.print(sBuffer+":"+i+" "); try{Thread.sleep(500);} catch(InterruptedException e) {System.out.println(e);} } System.out.println(); // increment char char c = this.sBuffer.charAt(0); this.sBuffer.setCharAt(0, ++c); } } } 

你可以替换

  if (sb.charAt(0)=='A') sb.setCharAt(0, 'B'); else sb.setCharAt(0, 'C'); 

 sb.setCharAt(0, (char) (sb.charAt(0) + 1)); 

package com.practice.ThreadPackage;

ThreadParent类扩展了Thread {

 StringBuffer data; public void run() { synchronized (this.data) { System.out.println(this.getName()); for (int i = 0; i < 10; i++) { System.out.print(this.data.toString()); } System.out.println(); this.data.setCharAt(0, ((char) (this.data.charAt(0) + 1))); } } ThreadParent(StringBuffer obj) { this.data = obj; } 

}

public class ThreadClass {public static void main(String args []){

  StringBuffer str = new StringBuffer("A"); ThreadParent obj = new ThreadParent(str); ThreadParent obj1 = new ThreadParent(str); ThreadParent obj2 = new ThreadParent(str); obj.setName("Thread1"); obj1.setName("Thread2"); obj2.setName("Thread3"); obj.start(); obj1.start(); obj2.start(); } 

}