Think about this you have a map of sockets and every time a message received to server you will get message and related socket !
this operation done with OS(linux , windows , unix , mac-OS , ...) kernel !
so you can handle a million connection just in one thread !
we call this None-Blocking sockets which means they never block your thread to read or write or any other operation such as accept and ... !
java has a package to handle this ! java.nio.*
how it's work ?
- A Thread to handle IO operations
- A Selector to select which sockets has operation and what type of operation
- ByteBuffer to handle read and write instead of using socket.stream in blocking-socket
also you can use multiple thread and selectors (each selector has its own thread)
look at this example : 
NoneBlockingServer.java :
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.util.Iterator;
public class NoneBlockingServer {
public static void main(String[] args) throws Exception
{
    runServer("localhost" , 5050);
}
private final static void runServer(String host , int port)throws Exception {
    Selector selector = Selector.open();
    ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
    serverSocketChannel.bind(new InetSocketAddress(host, port));
    serverSocketChannel.configureBlocking(false); //config to be a none-blocking serve-socket
    SelectionKey selectionKey = serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
    //register to selector for operation ACCEPT !
    //also you can use selectionKey for some other stuffs !
    while (true) {
        int numberOfReadSockets = selector.select();
        //it will wait until a socket(s) be ready for some io operation
        //or other threads call selector.wakeup()
        if(numberOfReadSockets==0){
            //maybe selector.wakeup() called
            //do some sync operations here !
            continue; // continue selecting !
        }
        Iterator<SelectionKey> keys = selector.selectedKeys().iterator();
        while (keys.hasNext())
        {
            SelectionKey key = keys.next();
            keys.remove(); //remove selected key from current selection !
            //handle selected key
            if(key.isValid() && key.isReadable())
            {
                //it means this socket is valid and has data to read
                SocketChannel socketChannel =
                        (SocketChannel) key.channel();
                ByteBuffer buffer = ByteBuffer.allocate(100); // allocate 100 bytes for buffer
                //maybe you must use an allocated buffer for each connection
                // instead of allocate for each operation
                int read = socketChannel.read(buffer);
                if(read<0)
                {
                    //need to close channel !
                    socketChannel.close(); // explicitly remove from selector
                    System.out.println("CONNECTION CLOSED");
                    continue; //socket closed and other operations will skip
                }else
                {
                    buffer.flip(); // you need to learn work with ByteBuffers
                    byte[] bytes = new byte[buffer.remaining()];
                    buffer.get(bytes);
                    //maybe convert it to String
                    String msg = new String(bytes);
                    //use msg !
                    System.out.println("MESSAGE : "+msg);
                    key.interestOps(SelectionKey.OP_READ|SelectionKey.OP_WRITE);
                    //set interestOps to WRIT and READ to write hello back message !
                    key.attach(ByteBuffer.wrap("Hello Client !".getBytes("UTF-8")));
                    //wrap a array of bytes using wrap and attach it to selectionKey
                }
            }
            if(key.isValid() && key.isWritable())
            {
                //it means this socket is valid and have space to write data !
                SocketChannel socketChannel =
                        (SocketChannel) key.channel();
                //you must represent data you want to write to this socket
                //maybe attached to selection key !
                ByteBuffer dataToWrite = (ByteBuffer) key.attachment();
                //key.attachment here to help u have some meta data about each socket
                //use it smart !
                int write = socketChannel.write(dataToWrite);
                if(write<0)
                {
                    //so means some error occurs better to close it !
                    socketChannel.close();
                    System.out.println("CONNECTION CLOSED !"); //log
                    continue;//as socket closed we will skip next operations !
                }else if(!dataToWrite.hasRemaining())
                {
                    //so all data putted to buffer !
                    key.interestOps(SelectionKey.OP_READ); // just need to read !
                }
            }
            if(key.isValid() && key.isAcceptable())
            {
                ServerSocketChannel server =
                        (ServerSocketChannel) key.channel();//just server channels has accept operation
                SocketChannel socketChannel = server.accept(); //accept it !
                socketChannel.configureBlocking(false); // config none-blocking mode
                socketChannel.register(selector , SelectionKey.OP_READ);
                //also you can register for multiple operation using | operation
                //for example register for both read and write SelectionKey.READ|SelectionKey.WRITE
                //also you can change is late using key.interestOps(int ops)
                System.out.println("NEW CONNECTION"); //log
            }
            //there is another type of key,  key.isConnectable()
            //google it !
        }
    }
}
}
and here is BlockingClient.java : 
import java.net.InetSocketAddress;
import java.net.Socket;
public class BlockingClient {
//using blocking sockets !
public static void main(String[] args)throws Exception
{
    Socket socket = new Socket();
    socket.connect(new InetSocketAddress("localhost" , 5050));
    socket.getOutputStream()
            .write("Hello Server".getBytes("UTF-8"));
    byte[] buffer = new byte[100];
    int len  = socket.getInputStream().read(buffer);
    System.out.println(new String(buffer , 0 , len , "UTF-8"));
    socket.close();
}
}
at this example we send Hello Server message from Blocking Client to None-Blocking Server and server will response Hello Client message !
just run !
Good luck