Site Search:

LifecycleWebServer.java

LifecycleWebServer.java simulate a web server backed by a thread pool.
ExecutorService is initially created in the running state. The shutdown method initiates a graceful shutdown: no new tasks are accepted but previously submitted tasks are allowed to complete. Tasks submitted to an ExecutorService after it has been shut down are handled by the rejected execution handler.Once all tasks have completed, the ExecutorService transitions to the terminated state. 

The ExecutorService is shut down through a client request by sending the web server a special message. Even after calling exec.shutdown(), you still need to close any open sockets. The main thread won't exit by itself, it is blocking on socket.accept(). The main thread need to throw Exception in order to get out of the loop, with the help of another thread by calling socket.close().

Also notice that All methods on Logger are multi-thread safe:
http://docs.oracle.com/javase/7/docs/api/java/util/logging/Logger.html
The System.out introduced an extra happens before relationship between threads.
https://www.youtube.com/watch?v=yfZEdKljkYM

Server Side:


Concurrency>cat LifecycleWebServer.java 
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.concurrent.*;
import java.util.logging.*;

/**
 * LifecycleWebServer
 * <p/>
 * Web server with shutdown support
 */
public class LifecycleWebServer {
    private final ExecutorService exec = Executors.newCachedThreadPool();
    private static Logger logger;
    private static ServerSocket socket;
    
    public static void main(String...args) throws IOException {
        logger = Logger.getAnonymousLogger();
        logger.addHandler(new StreamHandler(System.out, new SimpleFormatter()));
        logger.setLevel(Level.INFO);

        LifecycleWebServer lcws = new LifecycleWebServer();
        lcws.start();
    }

    public void start() throws IOException {
        socket = new ServerSocket(8090);
        while (!exec.isShutdown()) {
            try {
                final Socket conn = socket.accept();
                exec.execute(() -> handleRequest(conn));
            } catch (RejectedExecutionException e) {
                if (!exec.isShutdown())
                    log("task submission rejected", e);
                else
                    log("task submission rejected during Shutdown", e);
            }
        }
    }

    public void stop() {
        logger.log(Level.INFO, Thread.currentThread().toString() + "stop requested");
        exec.shutdown();
        try {
            exec.awaitTermination(10, TimeUnit.SECONDS);  //dead lock
            logger.log(Level.FINE, Thread.currentThread().toString() + ": exec.isTerminated???" + exec.isTerminated());  //false
            try {
                socket.close();  //close the last listening socket which prevent main thread to make progress
            } catch (IOException e) {
                log("socket close failed", e);
            }
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
            log("interrupted", e);
        }
    }

    private void log(String msg, Exception e) {
        logger.log(Level.WARNING, msg, e);
    }
    
    void handleRequest(Socket connection) {
        Request req = readRequest(connection);
        if (isShutdownRequest(req))
            stop();
        else
            dispatchRequest(req);
        
        try {
            connection.close();
        } catch (IOException e) {
            log("", e);
        }
        
        try {
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
            log("interrupted", e);
        }
        
        logger.log(Level.FINE, Thread.currentThread().toString() + " connection.isClosed()???" + connection.isClosed());
    }

    interface Request {
        public String getMessage();
    }

    private Request readRequest(Socket s) {
        try(BufferedReader br = new BufferedReader(new InputStreamReader(s.getInputStream()))) {
            String line = null;
            while ((line = br.readLine()) != null)
            {
                logger.log(Level.FINE, "?"+line);
                if(line.contains("msg")) {
                    String a = line;
                    return new Request() {
                        public String getMessage() {return a;}
                    };
                }
            }
        } catch (IOException e) {
            log("", e);
        }
        return null;
    }

    private void dispatchRequest(Request r) {
        logger.log(Level.INFO, Thread.currentThread().toString() + " dispatch: " + r.getMessage());
    }

    private boolean isShutdownRequest(Request r) {
        if(r.getMessage().equals("msg: stop"))
            return true;
        return false;
    }
}
Concurrency>javac LifecycleWebServer.java 
Concurrency>
Concurrency>java LifecycleWebServer
Jul 13, 2017 11:29:45 PM LifecycleWebServer dispatchRequest
INFO: Thread[pool-1-thread-1,5,main] dispatch: msg: xxx1
Jul 13, 2017 11:29:45 PM LifecycleWebServer dispatchRequest
INFO: Thread[pool-1-thread-3,5,main] dispatch: msg: xxx3
Jul 13, 2017 11:29:45 PM LifecycleWebServer dispatchRequest
INFO: Thread[pool-1-thread-2,5,main] dispatch: msg: xxx2
Jul 13, 2017 11:29:45 PM LifecycleWebServer dispatchRequest
INFO: Thread[pool-1-thread-4,5,main] dispatch: msg: xxx4
Jul 13, 2017 11:29:45 PM LifecycleWebServer dispatchRequest
INFO: Thread[pool-1-thread-5,5,main] dispatch: msg: xxx5
Jul 13, 2017 11:29:45 PM LifecycleWebServer stop
INFO: Thread[pool-1-thread-6,5,main]stop requested
Jul 13, 2017 11:29:45 PM LifecycleWebServer log
WARNING: task submission rejected during Shutdown
java.util.concurrent.RejectedExecutionException: Task LifecycleWebServer$$Lambda$1/41359092@23fc625e rejected from java.util.concurrent.ThreadPoolExecutor@3f99bd52[Shutting down, pool size = 6, active threads = 6, queued tasks = 0, completed tasks = 0]
at java.util.concurrent.ThreadPoolExecutor$AbortPolicy.rejectedExecution(ThreadPoolExecutor.java:2047)
at java.util.concurrent.ThreadPoolExecutor.reject(ThreadPoolExecutor.java:823)
at java.util.concurrent.ThreadPoolExecutor.execute(ThreadPoolExecutor.java:1369)
at LifecycleWebServer.start(LifecycleWebServer.java:33)

at LifecycleWebServer.main(LifecycleWebServer.java:25)



Client side:



client>date;for i in {1..5}; do echo "msg: xxx$i" | nc localhost 8090; done; echo "msg: stop" | nc localhost 8090;echo "msg: stop" | nc localhost 8090;date
Thu Jul 13 23:29:45 EDT 2017
Thu Jul 13 23:29:57 EDT 2017
client>