Contoh tipikal pelayan berutas tunggal adalah seperti berikut:
while (true) { Socket socket = null; try { // 接收客户连接 socket = serverSocket.accept(); // 从socket中获得输入流与输出流,与客户通信 ... } catch(IOException e) { e.printStackTrace() } finally { try { if(socket != null) { // 断开连接 socket.close(); } catch (IOException e) { e.printStackTrace(); } } } }
Pelayan menerima sambungan klien, berkomunikasi dengan klien, memutuskan sambungan selepas komunikasi selesai, dan kemudian menerima sambungan klien seterusnya. Jika terdapat Berbilang permintaan sambungan pelanggan mesti dibariskan oleh pelanggan ini. Jika anda membuat pelanggan menunggu terlalu lama, tapak web anda akan kehilangan kredibiliti, yang akan mengurangkan trafik.
Prestasi concurrency biasanya digunakan untuk mengukur keupayaan pelayan untuk bertindak balas kepada berbilang pelanggan pada masa yang sama Pelayan dengan prestasi concurrency yang baik mesti memenuhi dua syarat:
Jadi. dapat Menerima dan memproses berbilang sambungan pelanggan pada masa yang sama
Untuk setiap pelanggan, respons pantas akan diberikan
Gunakan berbilang rangkaian untuk melayani berbilang pelanggan pada masa yang sama Pelanggan menyediakan perkhidmatan, yang merupakan cara yang paling biasa untuk meningkatkan prestasi penyelarasan pelayan secara amnya terdapat tiga cara:
Tetapkan urutan pekerja kepada setiap pelanggan
public class EchoServer { private int port = 8000; private ServerSocket serverSocket; public EchoServer() throws IOException { serverSocket = new ServerSocket(port); System.out.println("服务器启动"); } public void service() { while(true) { Socket socket = null; try { // 接教客户连接 socket = serverSocket.accept(); // 创建一个工作线程 Thread workThread = new Thread(new Handler(socket)); // 启动工作线程 workThread.start(); } catch (IOException e) { e.printStackTrace(); } } } public static void main(String args[])throws TOException { new EchoServer().service(); } // 负责与单个客户的通信 class Handler implements Runnable { private Socket socket; pub1ic Handler(Socket socket) { this.socket = socket; } private PrintWriter getWriter(Socket socket) throws IOException {...} private BufferedReader getReader(Socket socket) throws IOException {...} public String echo(String msg) {...} public void run() { try { System.out.println("New connection accepted" + socket.getInetAddress() + ":" + socket.getPort()); BufferedReader br = getReader(socket); PrintWriter pw = getWriter(socket); String msg = null; // 接收和发送数据,直到通信结束 while ((msg = br.readLine()) != null) { System.out.println("from "+ socket.getInetAddress() + ":" + socket.getPort() + ">" + msg); pw.println(echo(msg)); if (msg.equals("bye")) break; } } catch (IOException e) { e.printStackTrace(); } finally { try { // 断开连接 if(socket != nulll) socket.close(); } catch (IOException e) { e,printStackTrace(); } } } } }Buat kumpulan benangPelaksanaan sebelumnya mempunyai kekurangan berikut:
Kolam benang mempunyai kelebihan berikut:
public class ThreadPool extends ThreadGroup { // 线程池是否关闭 private boolean isClosed = false; // 表示工作队列 private LinkedList<Runnable> workQueue; // 表示线程池ID private static int threadPoolID; // 表示工作线程ID // poolSize 指定线程池中的工作线程数目 public ThreadPool(int poolSize) { super("ThreadPool-"+ (threadPoolID++)); setDaemon(true); // 创建工作队列 workQueue = new LinkedList<Runnable>(); for (int i = 0; i < poolSize; i++) { // 创建并启动工作线程 new WorkThread().start(); } } /** * 向工作队列中加入一个新任务,由工作线程去执行任务 */ public synchronized void execute(Runnable tank) { // 线程池被关则抛出IllegalStateException异常 if(isClosed) { throw new IllegalStateException(); } if(task != null) { workQueue.add(task); // 唤醒正在getTask()方法中等待任务的工作线限 notify(); } } /** * 从工作队列中取出一个任务,工作线程会调用此方法 */ protected synchronized Runnable getTask() throws InterruptedException { while(workQueue,size() == 0) { if (isClosed) return null; wait(); // 如果工作队列中没有任务,就等待任务 } return workQueue.removeFirst(); } /** * 关闭线程池 */ public synchronized void close() { if(!isClosed) { isClosed = true; // 清空工作队列 workQueue.clear(); // 中断所有的工作线程,该方法继承自ThreadGroup类 interrupt(); } } /** * 等待工作线程把所有任务执行完 */ public void join() { synchronized (this) { isClosed = true; // 唤醒还在getTask()方法中等待任务的工作线程 notifyAll(); } Thread[] threads = new Thread[activeCount()]; // enumerate()方法继承自ThreadGroup类获得线程组中当前所有活着的工作线程 int count = enumerate(threads); // 等待所有工作线程运行结束 for(int i = 0; i < count; i++) { try { // 等待工作线程运行结束 threads[i].join(); } catch((InterruptedException ex) {} } } /** * 内部类:工作线程 */ private class WorkThread extends Thread { public WorkThread() { // 加入当前 ThreadPool 线程组 super(ThreadPool.this, "WorkThread-" + (threadID++)); } public void run() { // isInterrupted()方法承自Thread类,判断线程是否被中断 while (!isInterrupted()) { Runnable task = null; try { // 取出任务 task = getTask(); } catch(InterruptedException ex) {} // 如果 getTask() 返回 nu11 或者线程执行 getTask() 时被中断,则结束此线程 if(task != null) return; // 运行任务,异常在catch代码块中被捕获 try { task.run(); } catch(Throwable t) { t.printStackTrace(); } } } } }
publlc class EchoServer { private int port = 8000; private ServerSocket serverSocket; private ThreadPool threadPool; // 线程港 private final int POOL_SIZE = 4; // 单个CPU时线程池中工作线程的数目 public EchoServer() throws IOException { serverSocket = new ServerSocket(port); // 创建线程池 // Runtime 的 availableProcessors() 方法返回当前系统的CPU的数目 // 系统的CPU越多,线程池中工作线程的数目也越多 threadPool= new ThreadPool( Runtime.getRuntime().availableProcessors() * POOL_SIZE); System.out.println("服务器启动"); } public void service() { while (true) { Socket socket = null; try { socket = serverSocket.accept(); // 把与客户通信的任务交给线程池 threadPool.execute(new Handler(socket)); } catch(IOException e) { e.printStackTrace(); } } } public static void main(String args[])throws TOException { new EchoServer().service(); } // 负责与单个客户的通信,与上例类似 class Handler implements Runnable {...} }
Menggunakan kumpulan benang yang disediakan. oleh Java
disediakan oleh pakej Pelaksanaan kumpulan benang siap pakai adalah lebih mantap dan lebih berkuasa Untuk pengenalan lebih lanjut kepada kumpulan benang, sila rujuk artikel inipublic class Echoserver { private int port = 8000; private ServerSocket serverSocket; // 线程池 private ExecutorService executorService; // 单个CPU时线程池中工作线程的数目 private final int POOL_SIZE = 4; public EchoServer() throws IOException { serverSocket = new ServerSocket(port); // 创建线程池 // Runtime 的 availableProcessors() 方法返回当前系统的CPU的数目 // 系统的CPU越多,线程池中工作线程的数目也越多 executorService = Executors.newFixedThreadPool( Runtime.getRuntime().availableProcessors() * POOL_SIZE); System.out.println("服务器启动"); } public void service() { while(true) { Socket socket = null; try { socket = serverSocket.accept(); executorService.execute(new Handler(socket)); } catch(IOException e) { e.printStackTrace(); } } } public static void main(String args[])throws TOException { new EchoServer().service(); } // 负责与单个客户的通信,与上例类似 class Handler implements Runnable {...} }Nota mengenai penggunaan kumpulan benang
java.util.concurrent
Walaupun kumpulan benang boleh meningkatkan prestasi konkurensi pelayan dengan sangat baik, tetapi terdapat risiko tertentu dalam menggunakannya, yang boleh membawa kepada masalah berikut dengan mudah:
Kebuntuanwait()
notify()
Untuk kumpulan benang dengan bilangan benang pekerja yang tetap, jika benang pekerja membuang RuntimeException atau Ralat semasa melaksanakan tugas, dan pengecualian atau ralat ini tidak ditangkap, benang pekerja akan ditamatkan secara tidak normal, menyebabkan kumpulan benang untuk hilang selama-lamanya. Jika semua benang pekerja ditamatkan secara tidak normal, kumpulan benang menjadi kosong dan tiada benang pekerja tersedia untuk mengendalikan tugas
Situasi lain yang membawa kepada kebocoran benang ialah benang pekerja disekat semasa melaksanakan tugas, seperti menunggu pengguna memasukkan data, tetapi kerana pengguna belum memasukkan data (mungkin kerana pengguna pergi), ini kerja Benang terus disekat. Benang pekerja sedemikian wujud dalam nama sahaja, dan ia sebenarnya tidak melaksanakan sebarang tugas. Jika semua benang pekerja dalam kumpulan benang berada dalam keadaan tersekat sedemikian, maka kumpulan benang tidak akan dapat mengendalikan tugasan yang baru ditambahkan
Lebihan tugasan
Apabila terdapat sejumlah besar tugasan menunggu untuk dilaksanakan dalam baris gilir kerja, tugasan ini sendiri mungkin menggunakan terlalu banyak sumber sistem dan menyebabkan kekurangan sumber sistem
Ringkasnya, kumpulan benang mungkin membawa pelbagai risiko, Untuk mengelakkannya seboleh-bolehnya, prinsip berikut perlu dipatuhi semasa menggunakan kumpulan benang:
Jika tugasan A perlu menunggu secara serentak untuk keputusan pelaksanaan tugasan B semasa pelaksanaan , maka tugas A tidak sesuai untuk menyertai baris gilir kerja kumpulan benang. Jika anda menambah tugasan seperti tugasan A yang perlu menunggu keputusan pelaksanaan tugas lain ke dalam baris gilir kerja, ia boleh menyebabkan kebuntuan dalam kumpulan benang
Jika pelaksanaan tugas tertentu mungkin disekat dan ia mengambil masa yang lama Jika tugasan disekat, tamat masa harus ditetapkan untuk menghalang benang pekerja daripada menyekat secara kekal dan menyebabkan kebocoran benang
Fahami ciri tugasan dan analisis sama ada tugasan menjalankan operasi IO yang kerap menyekat atau melakukan operasi yang tidak pernah menyekat. Yang pertama menggunakan CPU secara berselang-seli, manakala yang kedua mempunyai penggunaan CPU yang lebih tinggi. Kelaskan tugasan mengikut ciri tugasan, dan kemudian tambahkan jenis tugasan yang berbeza pada baris gilir kerja kumpulan benang yang berbeza Dengan cara ini, setiap kumpulan benang boleh dilaraskan mengikut ciri tugasan
kepada. laraskan saiz kumpulan benang Saiz optimum kumpulan benang bergantung terutamanya pada bilangan CPU yang tersedia pada sistem dan ciri-ciri tugas dalam baris gilir kerja. Jika hanya terdapat satu baris gilir kerja pada sistem dengan N CPU dan kesemuanya adalah tugas pengiraan, maka apabila kumpulan benang mempunyai N atau N+1 benang pekerja, penggunaan CPU maksimum secara amnya akan diperoleh
Jika baris gilir kerja mengandungi tugas yang melakukan operasi IO dan kerap menyekat, saiz kumpulan benang harus melebihi bilangan CPU yang tersedia, kerana tidak semua utas pekerja berfungsi sepanjang masa. Pilih tugas biasa, dan kemudian anggaran nisbah antara masa menunggu (WT) dan masa sebenar yang diduduki oleh CPU untuk pengiraan (ST) semasa pelaksanaan tugas ini: WT/ST. Untuk sistem dengan N CPU, lebih kurang N(1+WT/ST) benang perlu disediakan untuk memastikan CPU digunakan sepenuhnya
untuk mengelakkan lebihan tugasan Pelayan harus mengehadkan keselarasan pelanggan berdasarkan kapasiti sistem. Apabila bilangan sambungan serentak pelanggan melebihi had, pelayan boleh menolak permintaan sambungan dan memberi pelanggan gesaan mesra.
Atas ialah kandungan terperinci Bagaimana untuk membuat pelayan berbilang benang di Java. Untuk maklumat lanjut, sila ikut artikel berkaitan lain di laman web China PHP!