Rumah  >  Artikel  >  Java  >  Bagaimana untuk membuat pelayan berbilang benang di Java

Bagaimana untuk membuat pelayan berbilang benang di Java

PHPz
PHPzke hadapan
2023-05-10 15:58:141356semak imbas

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

  • Buat kumpulan benang, dan benang pekerjanya akan memberi perkhidmatan kepada pelanggan

  • Gunakan kumpulan benang siap sedia dalam perpustakaan kelas Java dan pekerjanya benang akan melayani pelanggan

Tetapkan urutan kepada setiap pelanggan

Urutan utama pelayan bertanggungjawab untuk menerima sambungan pelanggan Setiap kali sambungan pelanggan diterima, benang pekerja akan dibuat, yang bertanggungjawab untuknya Komunikasi dengan 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 benang

Pelaksanaan sebelumnya mempunyai kekurangan berikut:

    <.>Pelayan mencipta dan memusnahkan benang pekerja Overhed adalah besar, jika pelayan perlu berkomunikasi dengan ramai pelanggan, dan masa komunikasi dengan setiap pelanggan adalah singkat, maka ada kemungkinan overhed pelayan mencipta benang baru untuk pelanggan adalah lebih besar daripada overhed sebenarnya berkomunikasi dengan pelanggan
  • Selain overhed mencipta dan memusnahkan benang, benang aktif juga menggunakan sumber sistem. Setiap utas akan menduduki jumlah memori tertentu Jika sejumlah besar pelanggan menyambung ke pelayan pada masa yang sama, sejumlah besar utas pekerja mesti dibuat, yang menggunakan sejumlah besar memori dan mungkin menyebabkan ruang memori tidak mencukupi. sistem
  • Beberapa utas pekerja pra-dicipta dalam kumpulan benang, yang secara berterusan mengambil tugas daripada baris gilir kerja dan kemudian melaksanakan tugas itu. Apabila benang pekerja selesai melaksanakan tugas, ia akan terus melaksanakan tugas seterusnya dalam baris gilir kerja

Kolam benang mempunyai kelebihan berikut:

    Mengurangkan keperluan untuk mencipta dan memusnahkan masa benang, setiap benang pekerja boleh digunakan semula sepanjang masa dan boleh melaksanakan pelbagai tugas
  • Bilangan benang dalam kumpulan benang boleh dilaraskan dengan mudah mengikut pembawa sistem kapasiti untuk mengelakkan penggunaan sumber sistem yang berlebihan dan menyebabkan sistem ranap
  • 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();
                    }
                }
            }
        }
    }
  • Pelayan yang dilaksanakan menggunakan kumpulan benang adalah seperti berikut:
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 ini

public 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 benangjava.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:

Kebuntuan
  • Sebarang Aplikasi berbilang benang semuanya berisiko menemui jalan buntu. Situasi paling mudah yang menyebabkan kebuntuan ialah: Benang A memegang kunci objek X dan sedang menunggu kunci objek Y, manakala benang B memegang kunci objek Y dan sedang menunggu kunci objek X. Benang A dan benang B Kedua-duanya tidak melepaskan kunci yang dipegangnya dan menunggu kunci pihak yang satu lagi Ini menyebabkan kedua-dua utas menunggu selama-lamanya, dan jalan buntu berlaku.
Mana-mana program berbilang benang mempunyai risiko kebuntuan, tetapi rangkaian The pool. juga akan menyebabkan satu lagi kebuntuan: dengan mengandaikan bahawa semua benang pekerja dalam kumpulan benang disekat semasa melaksanakan tugas masing-masing, mereka semua menunggu hasil pelaksanaan tugas A tertentu. Tugasan A masih dalam baris gilir kerja Memandangkan tiada utas terbiar, tugas A tidak boleh dilaksanakan. Ini menyebabkan semua benang pekerja dalam kumpulan benang disekat selama-lamanya, dan kebuntuan berlaku.

Sumber sistem tidak mencukupi
  • Jika kumpulan benang Terdapat terdapat sangat banyak utas di dalamnya, dan utas ini akan menggunakan banyak sumber termasuk memori dan sumber sistem lain, sekali gus menjejaskan prestasi sistem dengan serius

Ralat konkurensi
  • Baris gilir kerja kumpulan benang bergantung pada kaedah
  • dan
untuk membolehkan urutan pekerja mendapatkan tugas dalam masa, tetapi kedua-dua kaedah sukar digunakan. Jika tidak dikodkan dengan betul, pemberitahuan boleh hilang, menyebabkan urutan pekerja kekal melahu, mengabaikan tugasan yang perlu diproses dalam baris gilir kerja

wait()notify()

Kebocoran benang
  • 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!

Kenyataan:
Artikel ini dikembalikan pada:yisu.com. Jika ada pelanggaran, sila hubungi admin@php.cn Padam