Avatar billede odsgaard Praktikant
06. maj 2009 - 16:26 Der er 7 kommentarer og
1 løsning

Spørgsmål vedr. 'synchronized'

Jeg har et program, der gemmer Sockets i en LinkedList, som så bliver bearbejdet af nogle worker-tråde.

Den efterfølgende kode (fra worker-trådenne) giver mig en NoSuchElementException når jeg prøver at stresse programmet - det opstår når jeg sender flere requests (testprogrammet opretter nogle tråde, der sender et antal requests) på samme tid.

synchronized (socketQueue) {
  try {
    if (socketQueue.isEmpty())
      socketQueue.wait();
  } catch (InterruptedException e) { /* ignored */ }

  socket = (Socket) socketQueue.removeFirst(); <-- Kaster exception
  ... // behandling af socket
}

I mit server program, har jeg følgende kode, der tilføjer indkomne sockets til køen:

synchronized (socketQueue) {
  socketQueue.add(socket);
  socketQueue.notify();
}

Jeg antager, at flere af worker-trådene forsøger at fjerne et element fra socketQueue'en på samme tid. Men når nu jeg bruger synchronized, så burde det vel ikke kunne ske?

Nogle der kan komme med lidt hint om fejlen :)

Hilsen
Brian
Avatar billede arne_v Ekspert
06. maj 2009 - 17:23 #1
Umiddelbart ser koden OK ud.

Men der maa vel vaere et problem et sted.

:-)

Hvis du bare skal bruge det, saa skift til denne her:
  http://java.sun.com/javase/6/docs/api/java/util/concurrent/BlockingQueue.html

Hvis du skal have synkronisering til at virke saa vil jeg hoere om du kan poste noget komplet kode som er til at teste.
Avatar billede odsgaard Praktikant
08. maj 2009 - 22:47 #2
Hej arne

Programmet skal være en del af et større projekt, hvor det skal udvikles i java 1.4 (ved ikke om det måske kan have indflydelse på problemet), så jeg kan ikke bruge dit forslag med BlockingQue -> jeg vil dog gemme det til en anden gang :)

Jeg har prøver at simplificere koden og har her klippet det essentielle sammen, men stadig med samme funktionalitet. Programmet består her af 2 filer: den første er den del der lytter efter HTTP requests og den anden er et program, som tester det første. Når jeg kører det andet program, så er det, at jeg får de omtalte NoSuchElementExceptions.

Hvis parametrene i LoadTest programmet ændres, så den ikke sender så meget, så opstår fejlen ikke.

/Brian



Her er koden:


import java.io.*;
import java.net.*;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class HTTPTest {
private int port = -1;
private int maxHandlerThreads = -1;
private int connectionQueueSize = -1;
private WorkerThread worker = null;

public static void main(String[] args) {
int port = 9876;
int maxHandlerThreads = 15;
int connectionQueueSize = 100;
new HTTPTest(port, maxHandlerThreads, connectionQueueSize).start();
}

public HTTPTest(int port, int maxHandlerThreads, int connectionQueueSize) {
this.port = port;
this.maxHandlerThreads = maxHandlerThreads;
this.connectionQueueSize = connectionQueueSize;
}

public void start() {
worker = new WorkerThread(port, maxHandlerThreads, connectionQueueSize);
worker.start();
}

public void stop() {
worker.terminate();
}

public class WorkerThread extends Thread {
private boolean running = true;
private int connectionQueSize;
private ServerSocket serverSocket = null;
private Socket socket = null;
private LinkedList socketQueue = new LinkedList();
private List handlerThreads = new ArrayList();

public WorkerThread(int port, int maxHandlerThreads,
int connectionQueueSize) {
// Open server socket
try {
  this.serverSocket = new ServerSocket(port);
  this.connectionQueSize = connectionQueueSize;
} catch (IOException e) {
  throw new RuntimeException("Couldn't initialize server socket on port: " + port);
}

// Create handlerthreads
for (int i = 0; i < maxHandlerThreads; i++) {
  final HandlerThread thread = new HandlerThread(socketQueue);
  thread.start();
  handlerThreads.add(thread);
}
}

public void run() {
while (running) {
  socket = null;
  try {
  socket = this.serverSocket.accept();
  } catch (IOException e) {
  System.out.println(e);
}

// pass incoming connection to a handlerthread via the queue
synchronized (socketQueue) {
if (socketQueue.size() >= connectionQueSize) {
  try {
  socket.close();
  } catch (IOException e) {
  System.out.println(e);;
  }
} else {
// add socket to queue for rocessing
  socketQueue.add(socket);
  socketQueue.notify();
  }
}
}
}

public void terminate() {
try {
  // terminate all handlerthreads
  for (Iterator threads = socketQueue.iterator(); threads.hasNext();) {
  ((HandlerThread) threads.next()).terminate();
  }
  this.serverSocket.close();
} catch (IOException e) {
} // Ignored
running = false;
}
}

public class HandlerThread extends Thread {
private boolean running = true;
private LinkedList socketQueue = null;
private Socket socket;

public HandlerThread(LinkedList socketQueue) {
this.socketQueue = socketQueue;
}

public void run() {
while (running) {
socket = null;
// get a socket from the queue
synchronized (socketQueue) {
try {
  if (socketQueue.isEmpty())
  socketQueue.wait();
  } catch (InterruptedException e) { /* ignored */
}
}

try {
  socket = (Socket) socketQueue.removeFirst();
  InputStream is = socket.getInputStream();
  // wait up to 10.000 ms to recieve HTTP GET data
  for (int waitCounter = 0; (waitCounter < 100)
&& (is.available() == 0); waitCounter++) {
  Thread.sleep(100);
  }
  final ByteArrayOutputStream baos = new ByteArrayOutputStream();
  while (is.available() > 0) {
  int data = is.read();
  baos.write(data);
  }
  String response = null;
  if (canHandle(baos.toString())) {
  // send response '202 accepted' to HTTP agent
  response = getResponse(202, "Request accepted");
  } else {
  // send response '405 not allowed' to HTTP agent
  response = getResponse(405, "Request not allowed");
  }

  synchronized (socket) {
  socket.getOutputStream().write(response.getBytes());
  socket.getOutputStream().flush();
  socket.close();
  }
} catch (InterruptedException e) {
  // ignored
} catch (IOException e) {
  System.out.println(e);;
} catch (NoSuchElementException e) {
  System.out.println("NoSuchElementException");
}
synchronized (this) {
  try {
  this.wait(10);
  } catch (InterruptedException e) {/* Ignored */
  }
}
}
}

public void terminate() {
running = false;
synchronized (socket) {
try {
  if (socket != null) {
  socket.getOutputStream().flush();
  socket.close();
  }
} catch (IOException e) {
  System.out.println(e);
}
}
}
public boolean isRunning() {
return running;
}

public String getResponse(int code, String title) {
String response = "HTTP/1.1 " + code + " " + title + "\r\n"
+ "Date: " + new Date() + "\r\n"
+ "Content-Type: text/html\r\n\r\n"
+ "<!DOCTYPE html PUBLIC\"-//W3C//DTD HTML 4.01 TRANSITIONAL//EN\" "
+ "\"http://www.w3.org/TR/html4/loose.dtd\">\r\n"
+ "<HTML>\r\n" + "<HEAD>\r\n" + "<TITLE>" + code + " "
+ title + "</TITLE>\r\n" + "</HEAD>\r\n" + "<BODY>\r\n"
+ "<H1>Status: " + title + "</H1>\r\n" + "</BODY>\r\n"
+ "</HTML>\r\n";
return response;
}

// Check if the request validates according to reg.exp.
private boolean canHandle(String request) {
String s = request.replaceAll("\r", "").split("\n")[0];
String requestString = "GET\\s(/\\w+)*/\\?(\\w+=[a-zA-Z_0-9-]+&){1,4}(\\w+=[a-zA-Z_0-9-]+)\\sHTTP/1.[01]";
Pattern httpPattern = Pattern.compile(requestString);
Matcher httpMatcher = httpPattern.matcher(request);
return httpMatcher.matches();
}
}
}



import java.net.*;
import java.io.*;

public class LoadTest {
public static void main(String[] args) {
new LoadTest();
}

public LoadTest() {
for (int i = 0; i < 50; i++) {
  new Tester().start();
}
}

private class Tester extends Thread {       
public void run() {
for (int i = 0; i < 10; i++) {
  sendGetRequest("http://localhost:9876/", requestParameters());
  try {
  Thread.sleep(10);
  } catch (InterruptedException e) {/* ignored */
}
}
}

private String requestParameters() {
String request = "";
request += "id=" + random(15);
return request;
}

private String random(int cifre) {
String tmp = "";
while (cifre > 0) {
  tmp += (int) (Math.random() * 10);
  cifre--;
}
return tmp;
}

public String sendGetRequest(String endpoint, String requestParameters) {
String result = null;
if (endpoint.startsWith("http://")) {
  // Send a GET request to the servlet
  try {
  // Construct data
  StringBuffer data = new StringBuffer();
  // Send data
  String urlStr = endpoint;
  if (requestParameters != null && requestParameters.length() > 0){
    urlStr += "?" + requestParameters;
  }
  URL url = new URL(urlStr);
  URLConnection conn = url.openConnection();
  // Get the response
  BufferedReader rd = new BufferedReader(new InputStreamReader(conn.getInputStream()));
  StringBuffer sb = new StringBuffer();
  String line;
  while ((line = rd.readLine()) != null) {
    sb.append(line);
  }
  rd.close();
  result = sb.toString();
  } catch (Exception e) {
  // e.printStackTrace();
  }
}
return result;
}
}
}
Avatar billede arne_v Ekspert
09. maj 2009 - 03:37 #3
Ja - 1.4.2 har ikke java.util.concurrent !

Jeg prøver lige at kigge lidt på koden.
Avatar billede arne_v Ekspert
09. maj 2009 - 03:46 #4
socket = (Socket) socketQueue.removeFirst();

er udenfor

synchronized (socketQueue) {
}

så når du kalder den så kan andre være kommet til fadet !
Avatar billede arne_v Ekspert
09. maj 2009 - 03:48 #5
Iøvrigt behøver WorkerThread vel ikke være en tråd?
Avatar billede arne_v Ekspert
09. maj 2009 - 03:50 #6
Den dag du går på 1.5/1.6/1.7 så er der iøvrigt også klasser med thread pool: ExecutorService
Avatar billede odsgaard Praktikant
11. maj 2009 - 19:41 #7
Takker for dine svar arne. Da jeg er lidt under tidspres med opgaven, så løser jeg den i første omgang ved at fange den exception. Jeg vil do prøve at arbejde lidt videre med det, for at få det til at virke ordentligt :). Nu har jeg i hvertfald lidt at gå ud fra :)


Kan du ikke lige smide et 'svar', så kan du få pointene.

Hilsen
Brian
Avatar billede arne_v Ekspert
11. maj 2009 - 19:51 #8
svar
Avatar billede Ny bruger Nybegynder

Din løsning...

Tilladte BB-code-tags: [b]fed[/b] [i]kursiv[/i] [u]understreget[/u] Web- og emailadresser omdannes automatisk til links. Der sættes "nofollow" på alle links.

Loading billede Opret Preview
Kategori
Kurser inden for grundlæggende programmering

Log ind eller opret profil

Hov!

For at kunne deltage på Computerworld Eksperten skal du være logget ind.

Det er heldigvis nemt at oprette en bruger: Det tager to minutter og du kan vælge at bruge enten e-mail, Facebook eller Google som login.

Du kan også logge ind via nedenstående tjenester