`

线程模型

阅读更多

所以,我们需要一个确定数量的线程在JVM中运行,这样就需要了解“线程池”(ThreadPool)的概念。线程池在多线程程序设计中是比不可少的,而且初学者不太容易掌握,下面通过对线程池的介绍,结合第3和第4个程序,引出两种常用的线程池模型。

    第一种实现线程池的方法是:创建一个”池“,在”池“中增加要处理的数据对象,然后创建一定数量的线程,这些线程对”池“中的对象进行处理。当”池“是空的时候,每个线程处于等待状态;当往”池“里添加一个对象,通知所有等待的线程来处理(当然一个对象只能有一个线程来处理)。

    第二种方法是:同样创建一个”池“,但是在”池“中放的不是数据对象,而是线程,可以把”池“中的一个个线程比喻成一个个”工人“,当没有任务的时候,”工人“们严阵以待;当给”池“添加一个任务后,”工人“就开始处理并直到处理完成。

    在第3个程序中,定义了List类型的entries作为“池”,这个“池”用来保存需要扫描的端口,List中的元素必须是Object类型,不能用基本数据类型int往池里添加,而需要用使用Integer。在processMethod()方法中,首先就启动一定数量的PortThread线程,同时在while循环中通过entries.add(0, new Integer(port))往“池”里添加对象。在PortThread类的run()方法中通过entry = (Integer)entries.remove(entries.size()-1);取得“池”中的对象,转换成int后传递给Socket构造方法。
    第3个程序如下:
-----------------------------------------------------------------------------------------------------------------------
import java.io.IOException;
import java.net.InetAddress;
import java.net.Socket;
import java.net.UnknownHostException;
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;

public class PortScanner {
    private List entries = Collections.synchronizedList(new LinkedList());  //这个”池“比较特别
    int numofthreads;
    static int port;
    int beginport;
    int endport;
    InetAddress remote = null;
   
    public boolean isFinished(){
        if(port >= endport){
            return true;
        }else{
            return false;
        }
    }
   
    PortScanner(InetAddress addr, int beginport, int endport, int numofthreads){
        this.remote = addr;
        this.beginport = beginport;
        this.endport = endport;
        this.numofthreads = numofthreads;   
    }
   
    public void processMethod(){
        for(int i = 0; i < numofthreads; i++){          //创建一定数量的线程并运行
            Thread t = new PortThread(remote, entries, this);
            t.start();
        }
        
        port = beginport;
        
        while(true){
            if(entries.size() > numofthreads){
                try{
                    Thread.sleep(1000);      //”池“中的内容太多的话就sleep
                }catch(InterruptedException ex){
                    
                }
                continue;
            }
            
            synchronized(entries){
                if(port > endport) break;
                entries.add(0, new Integer(port));  //往”池“里添加对象,需要使用int对应的Integer类
                entries.notifyAll();
                port++;
            }
        }
    }
   
    public static void main(String[] args) {
        String host = null;
        int beginport = 1;
        int endport = 65535;
        int nThreads = 100;
        try{
            host = args[0];
            beginport = Integer.parseInt(args[1]);
            endport = Integer.parseInt(args[2]);
            nThreads = Integer.parseInt(args[3]);
            if(beginport <= 0 || endport >= 65536 || beginport > endport){
                throw new Exception("Port is illegal");
            }
        }catch(Exception e){
            System.out.println("Usage: java PortScannerSingleThread host beginport endport nThreads");
            System.exit(0);
        }
        
        try{
            PortScanner scanner = new PortScanner(InetAddress.getByName(host), beginport, endport, nThreads);
            scanner.processMethod();
        }catch(UnknownHostException ex){
        }   
    }
}

class PortThread extends Thread{
    private InetAddress remote;
    private List entries;
    PortScanner scanner;
   
    PortThread(InetAddress add, List entries, PortScanner scanner){
        this.remote = add;
        this.entries = entries;
        this.scanner = scanner;
    }
            
    public void run(){
        Integer entry;
        while(true){
            synchronized(entries){
                while(entries.size() == 0){
                    if(scanner.isFinished()) return;
                    try{
                        entries.wait();           //”池“里没内容就只能等了
                    }catch(InterruptedException ex){
                    }
                }
                entry = (Integer)entries.remove(entries.size()-1);  //把”池“里的东西拿出来进行处理
            }
            
            Socket s = null;
            
            try{
                s = new Socket(remote, entry.intValue());
                System.out.println("The port of " + entry.toString() + " of the remote " + remote +" is opened.");
            
            }catch(IOException e){
            }finally{
                try{
                    if(s != null) s.close();
                }catch(IOException e){
                    
                }
            }
        }
    }

以上程序需要4个参数,输入java PortScanner 10.1.1.182 1 10000 100运行(第4个参数是线程数),结果前两个程序一样,但是速度比第一个要快,可能比第二个要慢一些。

    第3个程序是把端口作为“池”中的对象,下面我们看第4个实现方式,把“池”里面的对象定义成是线程类,把具体的任务定义成”池“中线程类的参数。第4个程序有2个文件组成,分别是ThreadPool.java和PortScannerByThreadPool.java.
    ThreadPool.java文件内容如下:
-----------------------------------------------------------
import java.util.LinkedList;

public class ThreadPool{
    private final int nThreads;
    private final PoolWorker[] threads;
    private final LinkedList queue;

    public ThreadPool(int nThreads){
        this.nThreads = nThreads;
        queue = new LinkedList();
        threads = new PoolWorker[nThreads];

        for (int i=0; i<nThreads; i++) {
            threads = new PoolWorker();
            threads.start();
        }
    }

    public void execute(Runnable r) {
        synchronized(queue) {
            queue.addLast(r);
            queue.notifyAll();
        }
    }

    private class PoolWorker extends Thread {
        public void run() {
            Runnable r;

            while (true) {
                synchronized(queue) {
                    while (queue.isEmpty()) {
                        try{
                            queue.wait();
                        }catch (InterruptedException ignored){
                        }
                    }

                    r = (Runnable) queue.removeFirst();
                }

                try {
                    r.run();
                }
                catch (RuntimeException e) {
                }
            }
        }
    }
}
------------------------------------------------------------------------------------------------------------------
    在ThreadPool.java文件中定义了2个类:ThreadPool和PoolWorker。ThreadPool类中的nThreads变量表示线程数,PoolWorker数组类型的threads变量表示线程池中的“工人”,这些“工人”的工作就是一直循环处理通过queue.addLast(r)加入到“池”中的任务。
    PortScannerByThreadPool.java文件内容如下:
-------------------------------------------------------------------------------------------------------------------
import java.io.IOException;
import java.net.InetAddress;
import java.net.Socket;

public class PortScannerByThreadPool {
    public static void main(String[] args) {
        String host = null;
        int beginport = 1;
        int endport = 65535;
        int nThreads = 100;
        try{
            host = args[0];
            beginport = Integer.parseInt(args[1]);
            endport = Integer.parseInt(args[2]);
            nThreads = Integer.parseInt(args[3]);
            if(beginport <= 0 || endport >= 65536 || beginport > endport){
                throw new Exception("Port is illegal");
            }
        }catch(Exception e){
            System.out.println("Usage: java PortScannerSingleThread host beginport endport nThreads");
            System.exit(0);
        }
        
        ThreadPool tp = new ThreadPool(nThreads);
        
        for(int i = beginport; i <= endport; i++){
            Scanner ps = new Scanner(host,i);
            tp.execute(ps);
        }
    }
}
class Scanner implements Runnable{
    String host;   
    int port;
        
    Scanner(String host, int port){
        this.host = host;
        this.port = port;
    }
        
    public void run(){
        Socket s = null;
        try{
            s = new Socket(InetAddress.getByName(host),port);
            System.out.println("The port of " + port + " is opened.");
        }catch(IOException ex){
        }finally{
            try{
                if(s != null) s.close();
            }catch(IOException e){
            }
        }
    }
}
---------------------------------------------------------------------------------------------------------------------
    PortScannerByThreadPool是主程序类,处理输入的4个参数(和第3个程序是一样的):主机名、开始端口、结束端口和线程数。Scanner类定义了真正的”任务“。在PortScannerByThreadPool中通过new ThreadPool(nThreads)创建ThreadPool对象,然后在for循环中通过new Scanner(host,i)创建”任务“对象,再通过tp.execute(ps)把”任务“对象添加到”池“中。

    读者可以编译运行第4个程序,得出的结果和前面的是一样的。但是第4和第3个程序之间最大的差别就是:第4个程序会一直运行下去,不会自动结束。在第3个程序中存在一个isFinished()方法,可以用来判断任务是否处理完毕,而第4个程序中没有这样做。请读者自己思考这个问题。

    在第3和第4个程序中,我们可以概括出多线程的模型。第3个程序的线程”池“里装的要处理的对象,第4个程序的线程”池“里装的是”工人“,还需要通过定义”任务“并给把它”派工“给”工人“。我个人比较偏好后者的线程池模型,虽然类的个数多了几个,但逻辑很清晰。不管怎样,第3和第4个程序中关键的部分都大同小异,就是2个synchronized程序块中的内容,如下(第4个程序中的):
synchronized(queue) {
    queue.addLast(r);
    queue.notifyAll();
}

synchronized(queue) {
    while (queue.isEmpty()) {
        try{
            queue.wait();
        }catch (InterruptedException ignored){
        }
    }

    r = (Runnable) queue.removeFirst();
}

    一般拿synchronized用来定义方法或程序块,这样可以在多线程同时访问的情况下,保证在一个时刻只能有一个线程对这部分内容进行访问,避免了数据出错。在第3个程序中通过List entries = Collections.synchronizedList(new LinkedList())来定义”池“,在第4个程序中直接用LinkedList queue,都差不多,只是Collections.synchronizedList()可以保证”池“的同步,其实”池“里的内容访问都是在synchronized定义的程序块中,所以不用Collections.synchronizedList()也是可以的。

    wait()和notifyAll()是很重要的,而且这2个方法是Object基类的方法,所以任何一个类都是可以使用的。这里说明一个可能产生混淆的问题:queue.wait()并不是说queue对象需要进行等待,而是说queue.wait()所在的线程需要进行等待,并且释放对queue的锁,把对queue的访问权交给别的线程。如果读者对这2个方法难以理解,建议参考JDK的文档说明。

    好了,通过以上4个例子的理解,读者应该能对多线程的程序设计有了一定的理解。第3和第4个程序对应线程模型是非常重要的,可以说是多线程程序设计过程中不可或缺的内容。

    如果读者对以上的内容有任何疑问,可以和我联系,qianh@cntmi.com 版权所有,严禁转载

参考资料:
1、《Java Networking Programming, 3rd》written by Elliotte Rusty Harold, Published by O'Reilly,2004  
2、“Thread pools and work queues” written by Brian Goetz, Principal Consultant, Quiotix Corp.
分享到:
评论

相关推荐

    COM线程模型解析COM套间

    COM(Component Object Model)线程模型是微软组件对象模型中处理多线程交互的重要机制,它定义了组件如何在不同的线程上下文中运行和通信。理解COM线程模型对于开发高效且线程安全的COM组件至关重要。本文将深入...

    20_来聊聊redis的线程模型吧?为啥单线程还能有很高的效率?.zip

    Redis是一款高性能的键值存储系统,它以其独特的单线程模型和高效的内存管理而闻名。在深入了解Redis的线程模型之前,我们先要明白一点:虽然Redis是单线程的,但它的性能并不低,反而在很多场景下表现出非常高的...

    浅谈android线程模型

    ### 浅谈Android线程模型:深入理解与实践 #### 引言 随着智能手机的普及和技术的不断进步,Google的Android操作系统成为了移动设备领域的重要力量。Android不仅为用户提供了丰富的功能,也为开发者提供了广阔的...

    com线程模型详细解释

    Apartment 被翻译成套间或是单元,是线程模型的一个实现者,就像在操作系统课程中讲到的线程只是一个数学模型,而 Windows 的线程、进程是它(数学模型的线程、进程)的实现者。套间只是逻辑上的一个概念,实现时...

    JAVA多线程模型详解

    在多线程编程领域,Java作为一门广泛使用的编程语言,其内置的多线程模型一直是学习和应用的重点。本文将深入探讨Java多线程模型的相关知识点,包括线程与进程的区别、线程的实现原理、线程的创建方法以及线程的阻塞...

    Java同步线程模型分析与改进

    ### Java同步线程模型分析与改进 #### 一、引言 随着软件系统变得越来越复杂,多线程编程成为提高程序性能和响应性的关键手段之一。Java作为一种广泛使用的编程语言,自诞生以来就内置了对多线程的支持。然而,...

    Linux2.6内核实现的是NPTL线程模型

    ### Linux2.6内核中的NPTL线程模型 #### 概述 在Linux操作系统的发展历程中,线程管理模型经历了多次改进与优化。从早期版本到2.6版本,Linux内核针对多线程应用程序的支持有了显著的增强。其中最显著的变化之一...

    Vert.x线程模型揭秘

    ### Vert.x线程模型揭秘 #### 一、Vert.x简介 Vert.x是一个强大的JVM框架,主要用于构建反应式(Reactive)应用。该框架能够帮助开发者实现异步、可伸缩且高性能的Web应用(当然不仅仅限于Web应用)。与Node.js...

    多线程模型PPT课件.pptx

    【多线程模型】是指在一个进程中同时存在多个执行线程的编程模型,这种模型能够提高计算机系统的效率,尤其是在处理I/O密集型任务时。在操作系统中,进程是资源分配的基本单位,而线程是调度的基本单位。每个进程都...

    COM多线程模型

    COM聚合和COM跨进程模型 COM线程模型 分布式COM DCOM

    java自定义线程模型处理方法分享

    Java自定义线程模型在软件开发中扮演着重要的角色,特别是在高性能、高并发的应用场景,如游戏服务器。本文将深入探讨如何在Java中构建自定义线程模型,并分享一些实践经验。 首先,我们要明白为什么要自定义线程...

    Netty核心精讲之Reactor线程模型源码分析.mp4

    在深入探讨Netty的Reactor线程模型源码之前,我们需要先理解Reactor模式的基本概念。 Reactor模式是一种设计模式,常用于处理并发I/O事件。在多路复用I/O(如epoll、kqueue)中,Reactor模式是关键组成部分,它负责...

    02-VIP-Netty核心功能与线程模型精讲1

    总的来说,Netty 是一个强大且灵活的网络通信框架,通过其高效的线程模型、易于使用的 API 和丰富的功能,极大地降低了开发网络应用的复杂性,提高了系统的性能和可扩展性。无论是在微服务架构、游戏后端,还是...

    多线程模型

    在IT领域,多线程模型是一种常见的编程技术,特别是在处理大量并发操作时,如数据库读取。本篇文章将深入探讨“多线程读取DB数据”这一主题,旨在揭示其背后的原理、优缺点以及实现方法。 多线程是操作系统提供的一...

    180312-03 _ gRPC 线程模型分析.html

    180312-03 _ gRPC 线程模型分析.html

    VB单元线程模型实现多线程

    本项目"VB单元线程模型实现多线程"提供了一个简单的测试工程,帮助开发者了解如何在VB中创建和管理线程。 在VB中实现多线程主要依赖于`System.Threading`命名空间,这个命名空间包含了一些关键类,如`Thread`和`...

    IJK线程模型

    ijk线程模型

    基于COM STA线程模型的Delphi与Matlab混合编程研究.pdf

    基于COM STA线程模型的Delphi与Matlab混合编程研究.pdf

    COM线程模型

    COM线程模型,自己看看

Global site tag (gtag.js) - Google Analytics