大家好,我正在写一个离散事件仿真引擎,用到了多线程,原型中事件队列用到了PriorityBlockingQueue。但我是写了个类继承他,那么子类的其他成员在访问时还需要加锁保护么?谢谢大家1
代码如下:其中scheduler 类中有一个public Event TempE,对它的访问是不是需要synchronized?package thread1.SynDES;
import java.util.concurrent.PriorityBlockingQueue;
//import java.util.PriorityQueue;
public  class scheduler extends PriorityBlockingQueue<Event> {
//private Pqueue;
public Event TempE;
private static final long serialVersionUID = -4546334051932079472L; public Event dequeue()
{
Event temp=super.poll();
return temp;
}
@Deprecated
public  void dispatch(Event cEvent)
{

}
}////////////////////package thread1.SynDES;import java.util.PriorityQueue;
import java.util.Random;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;public class simulator {
public static int NumOfLp = 10;
public static LogicalProcess LP[] = new LogicalProcess[NumOfLp];
private static int SimulationTime = 0;
private static final int POOL_SIZE = 10;//

// public static Event TempE;
public static int getsimulationtime() {
return SimulationTime;
} public static void main(String[] args) { ExecutorService exe = null;//
exe = Executors.newFixedThreadPool(POOL_SIZE);// �����̳߳�
scheduler List = new scheduler();
int i = 0;
while (i < NumOfLp) {
LP[i] = new LogicalProcess(i, List);
exe.execute(LP[i]);
i++;
System.out.println("we r creating the " + i + "thread");
}
i = 0;
synchronized (List)//we need the syncronizing of list ,even though it is  
{
while (i < NumOfLp) {
// Random LPselector = new Random();
// first push
if (LP[i].firstpush) {
Event temp1;
temp1 = LP[i].Eventgeneration();
List.add(temp1);
System.out.println("we r issue a event from the " + i
+ "thread  to the " + temp1.to.toString()
+ "with timestamp" + temp1.time); }
i++;
}
System.out.println("**********************"+List.size()+"**********************");
System.out.println("at first Queue with"+List.size()+"Elements");
while ((SimulationTime < 500)) {// && (List.peek() != null)
if (List.peek() != null) {
System.out.println("Queue with"+List.size()+"Elements");
List.TempE = List.poll();// in this case the CEL contains

} else {
try {
System.out.println("no more event,sleeping");
Thread.sleep(100);
} catch (InterruptedException e) { }
}
// one socket
System.out.println(" "
+ "==Main==:Poll from Queue with"+List.size()+"Elements"
+ List.TempE.from.toString() + "thread  to the "
+ List.TempE.to.toString() + "with timestamp"
+ List.TempE.time);
synchronized (List.TempE.to) {
/* System.out.println("------" + List.TempE.to.toString()
+ "will be notified");*/
// List.TempE.to.havemessage=true;
List.TempE.to.notify();
} SimulationTime = List.TempE.time;// forwarding the
// simulationtime according
// to the current event
System.out.println("+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++currenttime is" + SimulationTime); if (List.peek() == null)
System.out.println("no more event");
}
}

System.out.println("finish");
System.exit(0);
/*Probe PP = new Probe();
PP.start();*/
} // for each of them.state and message r ignored }
/////////////////////
package thread1.SynDES;import java.util.Random;
public class LogicalProcess implements Runnable{
int LPstate=0;
int name;
public boolean havemessage;
Random LPtargetR;
Random SimTimeIncR;
Random MessR;
boolean firstpush;
scheduler myscheduler;
public LogicalProcess(int name1,scheduler Pscheduler)
{
 LPtargetR=new Random();
 SimTimeIncR=new Random();
 MessR=new Random();
 this.name=name1;
 firstpush=LPtargetR.nextBoolean();
 havemessage=false;
 myscheduler=Pscheduler;
}
public void recieveEvent(Event LPevent){

//System.out.println("-----------" + "Number"+name+"recieving event"); 
LPstate+=LPevent.message;

try{Thread.sleep(LPtargetR.nextInt(500));}catch(InterruptedException e){e.printStackTrace();}
}
    public String toString(){        //return "SimulationTime"+simulator.getsimulationtime()+"{name: " + name + "LPstate: " + LPstate;
     return ((Integer)name).toString();
     }
public Event Eventgeneration( )
{
int LPtargetnum=LPtargetR.nextInt(simulator.NumOfLp);
int SimTimeInc=SimTimeIncR.nextInt(100);//the simetime limit to 500
//System.out.println("SimTimeInc as much as"+SimTimeInc);

int MessContent=MessR.nextInt(100);
Event tempEvent= new Event(this,simulator.LP[LPtargetnum],MessContent,(simulator.getsimulationtime()+SimTimeInc));
System.out.println("-----------Eventgeneration" + "LP scheduling a event to EVENTLIST from the"
+ tempEvent.from.toString() + "thread  to"
+ tempEvent.to.toString() + "with timestamp"
+ tempEvent.time);
return tempEvent;
}
public void run() {
    while(true) {
     // while(havemessage== false)
        synchronized(this) {
          try {
           this.wait();
          } catch(InterruptedException e) {
            throw new RuntimeException(e);
          }          
        }
      
      System.out.println("--------RUN---" + "Number"+name+"thread working at timestamp"+myscheduler.TempE.time);
      recieveEvent(myscheduler.TempE);
     myscheduler.add(this.Eventgeneration());
      //havemessage= false;    
      
    }
  }
}
/////////////////package thread1.SynDES;class Event implements Comparable<Event>{
LogicalProcess from;LogicalProcess to;int message;int time;
public Event(LogicalProcess from,LogicalProcess to,int message,int time )
{
this.from=from;
this.to=to;
this.time=time;
this.message=message;
}
    public int compareTo(Event o) {     Event other = (Event)o;        return (int)this.time-(int)other.time ;     }
}

解决方案 »

  1.   

    具体来说,对于这个可以阻塞等待的队列,在多线程状态下对其进行访问时,是否会产生读后写,写后写,等需要采用锁和synchronized进行保护的情况?
      

  2.   

    PriorityBlockingQueue 之于PriorityQueue的改进,是否在于线程安全这个范畴呢
      

  3.   

    此回复为自动发出,仅用于显示而已,并无任何其他特殊作用
    楼主【denverbenjamin2000】截止到2008-06-29 21:15:08的历史汇总数据(不包括此帖):
    发帖数:12                 发帖分:990                
    结贴数:9                  结贴分:670                
    未结数:3                  未结分:320                
    结贴率:75.00 %            结分率:67.68 %            
    楼主加油
      

  4.   

    PriorityBlockingQueue 天生就带了线程安全和阻塞等待,这个也有问题。
    如果我只想获取线程安全这个特征就有点麻烦
    因为当我想使用Collections
    static <T> Collection<T> 
     synchronizedCollection(Collection<T> c) 
              返回由指定 collection 支持的同步(线程安全的)collection。 
    static <T> List<T> 
     synchronizedList(List<T> list) 
              返回由指定列表支持的同步(线程安全的)列表。 
    static <K,V> Map<K,V> 
     synchronizedMap(Map<K,V> m) 
              返回由指定映射支持的同步(线程安全的)映射。 
    static <T> Set<T> 
     synchronizedSet(Set<T> s) 
              返回由指定 set 支持的同步(线程安全的)set。 
    static <K,V> SortedMap<K,V> 
     synchronizedSortedMap(SortedMap<K,V> m) 
              返回由指定有序映射支持的同步(线程安全的)有序映射。 
    static <T> SortedSet<T> 
     synchronizedSortedSet(SortedSet<T> s) 
              返回由指定有序 set 支持的同步(线程安全的)有序 set。 
    这些时,发现,根本没有针对queue的。
    请大家帮忙出个主意,谢谢啊
      

  5.   

    换句话说,PriorityBlockingQueue 线程安全性达到了什么程度,读与写操作,写与写操作之间的一致性和独立性有保证么
      

  6.   

    Bloch 给出了描述五类线程安全性的分类方法:不可变、线程安全、有条件线程安全、线程兼容和线程对立。只要明确地记录下线程安全特性,那么您是否使用这种系统都没关系。这种系统有其局限性 -- 各类之间的界线不是百分之百地明确,而且有些情况它没照顾到 -- 但是这套系统是一个很好的起点。这种分类系统的核心是调用者是否可以或者必须用外部同步包围操作(或者一系列操作)。
    那么PriorityBlockingQueue 属于哪个等级呢
      

  7.   

    文档选项未显示需要 JavaScript 的文档选项
    将此页作为电子邮件发送
    级别: 初级
    Brian Goetz ([email protected]), 首席顾问, Quiotix Corp
    2003 年 11 月 15 日
    7月份我们的并发专家 Brian Goetz 将 Hashtable 和 Vector 类描述为“有条件线程安全的”。一个类难道不是线程安全就是线程不安全的吗?不幸的是,线程安全并不是一个非真即假的命题,它的定义出人意料的困难。但是,正如Brian 在本月的 Java 理论与实践中解释的,尽量在 Javadoc 中对类的线程安全性进行归类是非常重要的。请在附带的 讨论论坛中与作者和其他读者分享您关于本文的心得(也可以通过单击本文顶部或底部的 讨论来访问该论坛)。 
    在 Joshua Bloch 的那本出色的 Effective Java Programming Language Guide(参阅 参考资料)一书中,第 52 项的标题为“Document Thread Safety”,在其中他恳请开发人员准确地记录下类对线程安全性有哪些保证。就像 Bloch 书中的大多数建议一样,这也是一个反复提到、但很少实现的非常好的建议(就像 Bloch 在其 Programming Puzzlers 谈话中说的“不要像我兄弟那样写代码”)。 
    有 多少次您在 Javadoc 中查看一个类,并猜测“这个类是线程安全的吗?”。 由于缺少明确的记载,读者可能会对类的线程安全性做出不当的假设。也许他们将非线程安全的类假定为线程安全的(这真的很糟!),或者假设可以在调用一个对 象的方法之前同步对象以得到线程安全性(这可能是正确的,也可能还不够,最差的情况是,可能只会提供虚幻的线程安全性)。不管在什么情况下,最好在文档中 明确写明,在多个线程中共享类的实例时类的行为是怎样的。
    看一个这种问题的一个例子, java.text.SimpleDateFormat 类不是线程安全的,但是在 1.4 JDK 之前这并没有记录在 Javadoc 中。有多少开发人员错误地创建了 SimpleDateFormat 的静态实例,并在多个线程中使用它,同时不知道他们的程序在大负荷下是否能正确运行?不要对您的客户或者同事做这样的事情! 
    在忘记之前写下来(要不就离开公司)
    一定要在第一次编写类的时候记录线程安全性 -- 在编写它的时候访问类线程安全性需求和行为,要比在几个月后您(或者其他人)再回过头来看要容易得多。永远也不会比在编写它时更清楚地了解在一个实现中所 发生的情况。此外,在编写类的时候记录线程安全性,可以使您对于线程安全性的最初想法得以保留,因为维护者希望看到这个记录成为类的说明的一部分。
    如 果线程安全性是类的一个二元属性就好了,您只需要记录类是线程安全还是线程不安全的。但是很不幸,它不是这么简单的。如果类不是线程安全的,是否可以在每 次访问这个类的对象时通过同步使它成为线程安全的呢?是否有操作序列不能允许其他线程的介入,因而不仅需要对基本操作同步,而且对于复合操作也要同步呢? 在包含需要自动执行的一组操作的方法之间是否有状态依赖关系呢?开发人员要在并发应用程序中使用一个类时需要掌握这些信息。回页首定义线程安全性
    明确定义线程安全性出人意料地困难,大多数定义看上去完全是自我循环。快速搜索一下 Google,可以找到以下关于线程安全代码的典型的、但是没有多大帮助的定义(或者可以说是描述):
    • ...可以从多个编程线程中调用,无需线程之间不必要的交互。
    • ...可以同时被多个线程调用,不需要调用一方有任何操作。
    有这样的定义,就不奇怪我们对于线程安全性会感到如此迷惑。这些定义比说“一个类在可以被多个线程安全调用时就是线程安全的”好不了多少,当然,它的意义就是如此,但是它不能帮助我们区分一个线程安全的类与一个线程不安全的类。安全的意义是什么呢?
    实际上,所有线程安全的定义都有某种程序的循环,因为它必须符合类的规格说明 -- 这是对类的功能、其副作用、哪些状态是有效和无效的、不可变量、前置条件、后置条件等等的一种非正式的松散描述(由规格说明给出的对象状态约束只应用于外 部可见的状态,即那些可以通过调用其公共方法和访问其公共字段看到的状态,而不应用于其私有字段中表示的内部状态)。 
    线程安全性
    类 要成为线程安全的,首先必须在单线程环境中有正确的行为。如果一个类实现正确(这是说它符合规格说明的另一种方式),那么没有一种对这个类的对象的操作序 列(读或者写公共字段以及调用公共方法)可以让对象处于无效状态,观察到对象处于无效状态、或者违反类的任何不可变量、前置条件或者后置条件的情况。
    此 外,一个类要成为线程安全的,在被多个线程访问时,不管运行时环境执行这些线程有什么样的时序安排或者交错,它必须仍然有如上所述的正确行为,并且在调用 的代码中没有任何额外的同步。其效果就是,在所有线程看来,对于线程安全对象的操作是以固定的、全局一致的顺序发生的。
    正确性与线程安全性之间的关系非常类似于在描述 ACID(原子性、一致性、独立性和持久性)事务时使用的一致性与独立性之间的关系:从特定线程的角度看,由不同线程所执行的对象操作是先后(虽然顺序不定)而不是并行执行的。
    回页首线程安全程度
    如上面的例子所示,线程安全性不是一个非真即假的命题。 Vector 的方法都是同步的,并且 Vector 明确地设计为在多线程环境中工作。但是它的线程安全性是有限制的,即在某些方法之间有状态依赖(类似地,如果在迭代过程中 Vector 被其他线程修改,那么由 Vector.iterator() 返回的 iterator 会抛出 ConcurrentModificationException )。 
    对于 Java 类中常见的线程安全性级别,没有一种分类系统可被广泛接受,不过重要的是在编写类时尽量记录下它们的线程安全行为。
    Bloch 给出了描述五类线程安全性的分类方法:不可变、线程安全、有条件线程安全、线程兼容和线程对立。只要明确地记录下线程安全特性,那么您是否使用这种系统都没关系。这种系统有其局限性 -- 各类之间的界线不是百分之百地明确,而且有些情况它没照顾到 -- 但是这套系统是一个很好的起点。这种分类系统的核心是调用者是否可以或者必须用外部同步包围操作(或者一系列操作)。下面几节分别描述了线程安全性的这五种类别。回页首不可变
    本栏目的普通读者听到我赞美不可变性的优点时不会感到意外。不可变的对象一定是线程安全的,并且永远也不需要额外的同步。因为一个不可变的对象只要构建正确,其外部可见状态永远也不会改变,永远也不会看到它处于不一致的状态。Java 类库中大多数基本数值类如 Integer 、 String 和 BigInteger 都是不可变的。 回页首线程安全
    线程安全的对象具有在上面“线程安全”一节中描述的属性 -- 由类的规格说明所规定的约束在对象被多个线程访问时仍然有效,不管运行时环境如何排列,线程都不需要任何额外的同步。这种线程安全性保证是很严格的 -- 许多类,如 Hashtable 或者 Vector 都不能满足这种严格的定义。 回页首有条件的线程安全
    我们在 7 月份的文件“ 并发集合类”中讨论了有条件的线程安全。有条件的线程安全类对于单独的操作可以是线程安全的,但是某些操作序列可能需要外部同步。条件线程安全的最常见的例子是遍历由 Hashtable 或者 Vector 或者返回的迭代器 -- 由这些类返回的 fail-fast 迭代器假定在迭代器进行遍历的时候底层集合不会有变化。为了保证其他线程不会在遍历的时候改变集合,进行迭代的线程应该确保它是独占性地访问集合以实现遍历的完整性。通常,独占性的访问是由对锁的同步保证的 -- 并且类的文档应该说明是哪个锁(通常是对象的内部监视器(intrinsic monitor))。 
    如果对一个有条件线程安全类进行记录,那么您应该不仅要记录它是有条件线程安全的,而且还要记录必须防止哪些操作序列的并发访问。用户可以合理地假设其他操作序列不需要任何额外的同步。回页首线程兼容
    线程兼容类不是线程安全的,但是可以通过正确使用同步而在并发环境中安全地使用。这可能意味着用一个 synchronized 块包围每一个方法调用,或者创建一个包装器对象,其中每一个方法都是同步的(就像 Collections.synchronizedList() 一样)。也可能意味着用 synchronized 块包围某些操作序列。为了最大程度地利用线程兼容类,如果所有调用都使用同一个块,那么就不应该要求调用者对该块同步。这样做会使线程兼容的对象作为变量实例包含在其他线程安全的对象中,从而可以利用其所有者对象的同步。 
    许多常见的类是线程兼容的,如集合类 ArrayList 和 HashMap 、 java.text.SimpleDateFormat 、或者 JDBC 类 Connection 和 ResultSet 。 线程对立
    线程对立类是那些不管是否调用了外部同步都不能在并发使用时安全地呈现的类。线程对立很少见,当类修改静态数据,而静态数据会影响在其他线程中执行的其他类的行为,这时通常会出现线程对立。线程对立类的一个例子是调用 System.setOut() 的类。 源文档 <http://www.ibm.com/developerworks/cn/java/j-jtp09263/> 
    System.setOut(ps)重定义了输出流后,如何重定向控制台输出? System.setOut(ps)这一设置的作用域是在哪到哪? 用完后,如何设置回标准输出? import   java.io.*; public   class   TestSetout   { 
          public   static   void   main(String   args[])   { 
                  System.out.println( "ok ");     //这一句可以打印到控制台 
                  print(); 
                  System.out.println( "ok ");     //以下5句就不知道哪去了.也没有打印到文件. 
                  System.out.println( "ok "); 
                  System.out.println( "ok "); 
                  System.out.println( "ok "); 
                  System.out.println( "ok "); 
          } 
          static   void   print(){ 
    FileOutputStream   out   =   null; 
    try{ 
    out   =   new   FileOutputStream( "c:/java/kk.txt "); 
    }catch(FileNotFoundException   e)   { } 
    PrintStream   ps   =   new   PrintStream(out); 
    System.setOut(ps); 
    System.out.println( "lasjflsdjflsdjfisjdfjsdfjsdoifj "); 
    System.out.println( "lasjflsdjflsdjfisjdfjsdfjsdoifj "); 
    System.out.println( "lasjflsdjflsdjfisjdfjsdfjsdoifj "); 
    System.out.println( "lasjflsdjflsdjfisjdfjsdfjsdoifj "); try{ 
            ps.close(); 
            out.close(); 
    }catch(IOException   e){ } 

    }源文档 <http://topic.csdn.net/u/20070823/10/d4d3d38c-79b7-43b7-8497-2f055b42bb9e.html> 
    个人觉得回答你这个问题要是自己想还是要花一点点时间的 看过源码之后发现 
    可以用 
    System.setOut(new   PrintStream(new   FileOutputStream(FileDescriptor.out))); 
    重新定位到标准输出流 setOut实际上执行了 
    public   static   void   setOut(PrintStream   out)   { 
    checkIO(); 
    setOut0(out); 

    而setOut0(out)是本地方法 
    我猜想他的功能之一就是System.out   =   上面的out ps:记得以后多放一点分,这样回答的人就多了:源文档 <http://topic.csdn.net/u/20070823/10/d4d3d38c-79b7-43b7-8497-2f055b42bb9e.html> 我找到更好的办法了,不过还是谢谢你! 在System.setOut(ps)之前,先记下目前的标准输出到哪里. 
    PrintStream   old   =   System.out; 
    这样如果想再设置回来就简单了:System.setOut(old); 源文档 <http://topic.csdn.net/u/20070823/10/d4d3d38c-79b7-43b7-8497-2f055b42bb9e.html>