Java线程从共享堆栈数组读取和写入

时间:2011-03-12 12:07:09

标签: java multithreading concurrency

此程序的结果应与1或2或3个线程相同。但是,线程1的结果是真实的。我想我正在使用共享和私有变量,我做错了什么?线程必须从堆栈读取一个间隔,然后计算正交模型。如果误差足够小(即在指定的精度内),那么我们就有了解决方案。如果误差仍然太大,则将间隔分成两个,每半个间隔给出一半所需的误差。应用正交 再次等等,直到错误足够小。 主要问题是由于提前终止而产生的 线程。堆栈可能为空,但另一个线程可能即将在其上放置新任务。对此的解决方案是保持“活动”线程的计数,即当前正在处理间隔的线程。然后代码应该只在堆栈为空并且没有活动线程时终止...

请,任何帮助都会非常感激吗?

干杯

 import java.lang.Integer;


class quadtest  {

/* Adaptive Quadrature Code. Finds the value of an integral of a 
   function on a closed interval to a specified accuracy.
*/

  public static void main (String args[]) {

    int nthreads = Integer.parseInt(args[0]);

    double left, right, eps;
    double start_time, time;

    Quad quad =null;
    //Counter counter = new Counter();

    left  = 0.0;
    right = 1.0;
    eps   = 1.0E-11;

    System.out.println("Adaptive Quadrature Program \n");
    System.out.println("eps="+eps+"    n=10000");
    start_time = System.currentTimeMillis();

    //Start threads
    Thread thread_object [] = new Thread[nthreads];

    for(int i=0;i<nthreads;i++){    
        quad = new Quad(left,right,eps,i,nthreads);
        thread_object[i]=new Thread(quad);
    }
    for(int i=0;i<nthreads;i++){    
        thread_object[i].start();
    }
    //Join the threads
    for(int i=0;i<nthreads;i++){
        try{
        thread_object[i].join();
        }catch(InterruptedException x){}
    }

    time = (double) (System.currentTimeMillis()-start_time) / 1000.;
    System.out.println("Result  = "  + quad.getResult() );
    System.out.println("Execution time = "  + time + " seconds ");


    }
}

    import java.lang.Runnable;
import java.util.concurrent.atomic.AtomicInteger;

class Quad implements Runnable{
    //Shared Variables 
    static volatile double [][] stack;
    static volatile boolean first=false;
    static volatile double FinalResult;
    static AtomicInteger threadCounter;
    static AtomicInteger writing;
    static AtomicInteger stackpointer;
    static int nthreads;
    //Constants
    static final int stacksize = 1000;
    static final int il = 0;
    static final int ir = 1;
    static final int ie = 2;
    static final int dims = 3;
    //Private Variables
    private  int tid;
    double left,right,eps;
    private double result;
    private double l,r,ep;


    public Quad(double left, double right, double eps,int tid,int nthreads) {

        this.left = left;
        this.right = right;
        this.eps = eps; 
        this.tid=tid;
        Quad.nthreads = nthreads;
        result = 0.0;
        //Only one thread will do it
        if(first==false){
            first=true;
            stack        =  new double [dims][stacksize];
            threadCounter=  new AtomicInteger(0);
            writing      =  new AtomicInteger(0);
            stackpointer =  new AtomicInteger(1);

            stack[il][stackpointer.get()] = left;
            stack[ir][stackpointer.get()] = right;
            stack[ie][stackpointer.get()] = eps;
            FinalResult=0.0;
         }
    }
    public void run(){
        stackops();
        add();
    }

   public void stackops() {

       double abserror,m, est1, est2;

       while ((stackpointer.get() >= 1)|| threadCounter.get()>0) {


           // Pop next interval off stack.
           synchronized (this){
               threadCounter.incrementAndGet();
               while (writing.get()==1){}
               pop();
           }
           // Compute estimates.
           m    = 0.5 * (l + r);
           est1 = 0.5 * (r - l) * (func(l) + func(r)) ;
           est2 = 0.5 * ((m - l) * (func(l) + func(m)) + (r - m) * 
                 (func(m) + func(r)));
           abserror = Math.abs(est2-est1) / 3.0;


           // Check for desired accuracy: push both halves onto the
           // stack if not accurate enough.        
           if (abserror <= ep) {
               result += est2;
                //System.out.println("ERROR->ID "+tid+"-abserror="+abserror+"-ep="+ep );
                //System.out.flush();
           } else {        
               if (stackpointer.get()+ 2 > stacksize) {
               System.out.println("Stack too small, try stacksize = " 
                          + 2*stacksize);
               }
               //Push into the stack
               synchronized (this){
                    push(m);
                }
             }//end else
             threadCounter.decrementAndGet();
           }//end while
   }//end method

    private synchronized void add(){
        FinalResult +=result;
    }

    private void pop(){
        if(stackpointer.get()>0){
            l   = stack[il][stackpointer.get()];
            r   = stack[ir][stackpointer.get()];
            ep  = stack[ie][stackpointer.get()];
            stackpointer.decrementAndGet();
        }
    }
    private void push (double m){
        writing.set(1);
            if(stackpointer.get()>=-1){
                stackpointer.incrementAndGet();
                stack[il][stackpointer.get()] = l;
                stack[ir][stackpointer.get()] = m;
                stack[ie][stackpointer.get()] = ep * 0.5;

                stackpointer.incrementAndGet();
                stack[il][stackpointer.get()] = m;
                stack[ir][stackpointer.get()] = r;
                stack[ie][stackpointer.get()] = ep * 0.5;
            }
       writing.set(0);
    } 

    public  double getResult(){
        return FinalResult;
    }

    private double func(double x) {

    double q;
    int n;

    n = 10000;
    q = 1000.0;

    for(int i=0;i<n;i++) {
        q -= x;
    }
    if (q == 1.0e10) System.out.println("q = " + q);

    return x * x;

    }
}

3 个答案:

答案 0 :(得分:2)

您的代码实际上没有任何互斥。

  1. 使用synchronized关键字时,必须实际同步所有线程共享的对象。但是,this语句中的synchronized(this){}引用了未共享的Quad个对象。
  2. 由于同样的原因,您对FinalResult的写入未同步。此外,volatile是不必要的。
  3. 您似乎正在尝试使用writing作为自定义旋转循环来防止多个线程同时弹出。你不需要这个 - 你的synchronized块应该已经处理好这个 - 你弄错了。想象一下,一个线程开始执行pop(),在它可以进行第一次写操作之前,它会被重新安排。另外,你也有push的写作,没有保护。如果pop()push()被两个独立的线程同时调用怎么办?
  4. 其他说明:

    1. 如果上述第2-3项工作正常,则stackPointer不需要原子。
    2. 您也可以初始化为static数据定义对象的数据。即:

      class Quad implements Runnable {
          static AtomicInteger threadCounter = new AtomicInteger(0);
          ...
      }
      

答案 1 :(得分:0)

如果没有阅读所有代码但只看了它,似乎这就是JDK 7中首次亮相的join / fork框架旨在解决的问题。见http://blog.quibb.org/2010/03/jsr-166-the-java-forkjoin-framework/

我建议不要乱用线程和同步。 JDK 7版本已经可以下载(并且看起来相当稳定)。另外,还有一个独立的连接/表单版本可以单独下载(参见http://g.oswego.edu/dl/concurrency-interest/)。

答案 2 :(得分:0)

类Quad实现Runnable {     //共享变量     static volatile double [] [] stack;     static volatile boolean first = true;     static Object lock1;     static Object lock2;     static double FinalResult;     static AtomicInteger threadCounter;     static AtomicInteger stackpointer;     static int nthreads;     //常量     static final int stacksize = 1000;     static final int il = 0;     static final int ir = 1;     static final int ie = 2;     static final int dims = 3;     //私有变量     int tid;     双左,右,eps;

private double result;
private double l,r,ep;
private boolean calculate;

public Quad(double left, double right, double eps,int tid,int nthreads) {

this.left  = left;
this.right = right;
this.eps   = eps; 
this.tid   = tid;
Quad.nthreads = nthreads;
result = 0.0;

synchronized(this){
    //Only the first thread will do it
    if(first==true){
    first=false;
    lock1 = new Object();
    lock2 = new Object();
    stack        =  new double [dims][stacksize];
    threadCounter=  new AtomicInteger(0);
    stackpointer =  new AtomicInteger(1);
    stack[il][stackpointer.get()] = left;
    stack[ir][stackpointer.get()] = right;
    stack[ie][stackpointer.get()] = eps;
    FinalResult=0.0;
    System.out.println("I am tid= "+tid );
    }
}
}
public void run(){
    stackops();
    add();
}

public void stackops() {

double abserror,m, est1, est2;
est2=est1=m=abserror=0;

while ((stackpointer.get() >= 1)|| threadCounter.get()>0) {


    // Pop next interval off stack.
    synchronized (lock1){
    pop();
    }
    // Compute estimates.
    if (calculate == true){

    m    = 0.5 * (l + r);
    est1 = 0.5 * (r - l) * (func(l) + func(r)) ;
    est2 = 0.5 * ((m - l) * (func(l) + func(m)) + (r - m) * 
              (func(m) + func(r)));
    abserror = Math.abs(est2-est1) / 3.0;  


    if (abserror <= ep) {
        result += est2;
    } else {           
        //Push into the stack
        synchronized (lock1){
        push(m);
        }  
    }//end else
    threadCounter.decrementAndGet();
    }

   }//end while
System.out.println("I am " + tid+" result = "+result);
}//end method

private void add(){
synchronized(lock1){
    FinalResult +=result;
}
}

private void pop(){
if(stackpointer.get()>0){
    threadCounter.incrementAndGet();
    calculate =true;
    l   = stack[il][stackpointer.get()];
    r   = stack[ir][stackpointer.get()];
    ep  = stack[ie][stackpointer.get()];
    stackpointer.decrementAndGet();
}else{
    calculate =false;
}
}
private void push (double m){

if(stackpointer.get()>=-1){
    stackpointer.incrementAndGet();
    stack[il][stackpointer.get()] = l;
    stack[ir][stackpointer.get()] = m;
    stack[ie][stackpointer.get()] = ep * 0.5;

    stackpointer.incrementAndGet();
    stack[il][stackpointer.get()] = m;
    stack[ir][stackpointer.get()] = r;
    stack[ie][stackpointer.get()] = ep * 0.5;
}
} 

public  double getResult(){
    return FinalResult;
}

private double func(double x) {

double q;
int n;

n = 10000;
q = 1000.0;

for(int i=0;i<n;i++) {
    q -= x;
}
if (q == 1.0e10) System.out.println("q = " + q);

return x * x;

}

}