简体   繁体   中英

Using Thread.join() method

I am new to multi-threaded programming. I am getting unexpected behavior when using the join method. Sometimes both the threads give the same result, sometimes only one result is displayed and sometime the correct result is displayed. What am I doing wrong?

public class Client {
    public static void main(String args[]) throws Exception {
        String model = args[0];
        String property = args[1];
        String parameters = args[2];
        String wsdlPaths = args[3];
        int numServices = Integer.parseInt(args[4]);
        String[] parameter;
        getParameters(parameters, parameter);
        String[] wsdl;
        getWSDL(wsdlPaths, wsdl);       
        Thread[] t  = new Thread[numServices];
        ClientHelper[] nch = new ClientHelper[numServices];
        TestStub[] stub = new TestStub[numServices];
        TestStub.Experiment[] request = new TestStub.Experiment[numServices];
        TestStub.ExperimentResponse[] response = new TestStub.ExperimentResponse[numServices];            
        for (int i = 0; i < numServices; i++) {     
            stub[i] = new TestStub(wsdl[i]);
            request[i] = new TestStub.Experiment();
            request[i].setArgs0(model);
            request[i].setArgs1(property);
            request[i].setArgs2(parameter[i]);
            nch[i] = new ClientHelper(stub[i], request[i]);
            t[i] = new Thread(nch[i]);          
            t[i].start(); // When I moved this statement to the next loop just before the join method, the program behaved like a single threaded program and was working correctly.
        }
        for (int i = 0; i < numServices; i++) { 
            try {
                t[i].join();
            } catch (InterruptedException e) {
            }
            System.out.println(t[i].getName());
            response[i] = nch[i].response;
            System.out.println("Response " + i + " : " + response[i].get_return());                
        }
    }
}


public class ClientHelper implements Runnable {
    TestStub stub;
    TestStub.Experiment request;
    TestStub.ExperimentResponse response;

    public ClientHelper(TestStub stub, TestStub.Experiment request){
        this.stub = stub;
        this.request = request;
    }

    public void run() {
        try {
            response = stub.Experiment(request);
        }
        catch (Exception e) {
        }
    }
}

Since the posted code will not compile, and it might simply be too large to deduce the root cause, I'll take my chances and hint on what might be going wrong.

Your code contains two for loops:

for (int i = 0; i < numServices; i++) { 
    ...
    t[i].start();
}

for (int i = 0; i < numServices; i++) { 
    t[i].join();
    ...
}

Your observation has deduced that

for (int i = 0; i < numServices; i++) { 
    t[i].start();
    t[i].join();
    ...
}

does not cause problems.

Well, that is because you are now serializing the process, and there is no concurrent execution at all. The only thing I can deduce from this, is that you are sharing state across threads, possibly in the TestStub or TestStub.Experiment arguments to the constructor of the Runnable. I can't state what is being shared unless there is code to execute, but this is most certainly the cause.

Update

After considering the statement stub.Experiment(request); , I would also suggest verifying if the (generated) web service proxy and the web service client framework are threadsafe as well. They might be publishing object references that might be used across threads and hence require explicit synchronization using appropriate locks. Sadly, there is no easy way to determine whether a library and set of generated classes are thread-safe.

All your requests are using the same model and property. If they have some state - you'll get this behavior.

request[i].setArgs0(model);
request[i].setArgs1(property);
    t[i].join(); 

This does not do what you think it does! It "pauses" the main method! And it lets the main method wait for t[i] to finish. After that the main method resumes that loop.

Rather than using Threads directly it is usually better to use a thread pool like ExecutorService.

final String[] parameter=  getParameters(parameters);
final String[] wsdl = getWSDL(wsdlPaths);
ExecutorService executor = Executors.newCachedThreadPool();
List<Future<TestStub.ExperimentResponse>> futures = new ArrayList<Future<TestStub.ExperimentResponse>>();
for (int j = 0; j < numServices; j++) {
    final int i = j;
    futures.add(executor.submit(new Callable<TestStub.ExperimentResponse>() {
        @Override
        public TestStub.ExperimentResponse call() throws Exception {
            TestStub stub = new TestStub(wsdl[i]);
            TestStub.Experiment request = new TestStub.Experiment();
            request.setArgs0(model);
            request.setArgs1(property);
            request.setArgs2(parameter[i]);
            return stub.Experiment(request);
        }
    }));
}
for (Future<ExperimentResponse> future : futures) {
    TestStub.ExperimentResponse response = future.get();
    System.out.println("Response: " + response.get_return());
}
executor.shutdown();

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM