...
Code Block |
---|
|
class SocketReader implements Runnable { // Thread-safe class
private final Socket socket;
private final BufferedReader in;
private volatile boolean done = false;
private final Object lock = new Object();
private boolean isRunning = false; // Reduces the need to synchronize time consuming operations
public static class MultipleUseException extends RuntimeException {};
public SocketReader() throws IOException {
this.socket = new Socket("somehost", 25);
this.in = new BufferedReader(new InputStreamReader(this.socket.getInputStream()));
}
// Only one thread can use the socket at a particular time
public void run() {
try {
synchronized (lock) {
if execute(isRunning) {
throw new MultipleUseException();
}
isRunning = true;
}
execute();
} catch (IOException ie) {
// Forward to handler
}
}
public void execute() throws IOException {
String string;
while (!done && (string = in.readLine()) != null) {
// Blocks until end of stream (null)
}
}
public void shutdown() {
done = true;
}
public static void main(String[] args) throws IOException, InterruptedException {
SocketReader reader = new SocketReader();
Thread thread = new Thread(reader);
thread.start();
Thread.sleep(1000);
reader.shutdown();
}
}
|
Note that the Runnable
object prevents itself from being run in multiple threads using an isRunning
flag. If one thread tries to invoke run()
after another thread has already done so, an unchecked exception is thrown.
Noncompliant Code Example (blocking Noncompliant Code Example (blocking IO, interruptible)
This noncompliant code example uses thread interruption to indicate that it is safe to shutdown the thread, as suggested in CON13-J. Ensure that threads are stopped cleanly. However, this is not useful because the thread is blocked on some network IO as a consequence of using the readLine()
method. Network I/O is not responsive to thread interruption.
Code Block |
---|
|
class SocketReader implements Runnable { // Thread-safe class
// ...
public void execute() throws IOException {
String string;
while (!Thread.interrupted() && (string = in.readLine()) != null) {
// Blocks until end of stream (null)
}
}
public static void main(String[] args) throws IOException, InterruptedException {
SocketReader reader = new SocketReader();
Thread thread = new Thread(reader);
thread.start();
Thread.sleep(1000);
thread.interrupt();
}
}
|
...
Code Block |
---|
|
class SocketReader implements Runnable {
private final SocketChannel sc;
private final Object lock = new Object();
private boolean isRunning = false;
private class MultipleUseException extends RuntimeException {};
public SocketReader() throws IOException {
sc = SocketChannel.open(new InetSocketAddress("somehost", 25));
}
public void run() {
synchronized (lock) {;
public SocketReader() ifthrows (isRunning)IOException {
sc throw = SocketChannel.open(new MultipleUseException();
InetSocketAddress("somehost", 25));
}
public void isRunning = true;
}
run() {
ByteBuffer buf = ByteBuffer.allocate(1024);
try {
synchronized (lock) {
while (!Thread.interrupted()) {
sc.read(buf);
// ...
}
}
} catch (IOException ie) {
// Forward to handler
}
}
public static void main(String[] args) throws IOException, InterruptedException {
SocketReader reader = new SocketReader();
Thread thread = new Thread(reader);
thread.start();
Thread.sleep(1000);
thread.interrupt();
}
}
|
...
Code Block |
---|
|
class DBConnector implements Runnable {
final String query;
DBConnector(String query) {
this.query = query;
}
public void run() {
Connection con;
try {
// Username and password are hard coded for brevity
con = DriverManager.getConnection("jdbc:driver:name", "username","password");
Statement stmt = con.createStatement();
ResultSet rs = stmt.executeQuery(query);
} catch (SQLException e) {
// Forward to handler
}
// ...
}
public static void main(String[] args) throws InterruptedException {
DBConnector connector = new DBConnector(/* "suitable query */");
Thread thread = new Thread(connector);
thread.start();
Thread.sleep(5000);
thread.interrupt();
}
}
|
...