多线程和多路复用外文翻译资料

 2022-08-22 10:48:37

3 Multithreading and Multiplexing

Learning Objectives

After reading this chapter, you should:

  • understand what is meant by a thread (in a programming context);
  • appreciate the need for multithreaded programming;
  • be aware of typical circumstances under which multithreading might be appropriate;
  • know how to implement threads in Java;
  • know how to implement variable locking in Java;
  • be aware of the danger posed by deadlock;
  • know what Java methods to use in order to improve thread efficiency and reduce the likelihood of deadlock;
  • know how to implement a multithreaded server;
  • know how to implement a non-blocking server via multiplexing.

It is often the case nowadays that programs need to carry out more than one significant task at the same time (i.e., lsquo;concurrentlyrsquo;). For example, a GUI-driven program may be displaying a background animation while processing the userrsquo;s foreground interactions with the interface, or a Web browser may need to download and display the contents of a graphics file while rendering the rest of the associated Web page. The popularity of client/server applications over the past decade has exacerbated this demand enormously, with server programs sometimes having to process the needs of several hundreds of clients at the same time.

Not many years ago, each client that connected to a server would have caused a new process to be spawned on the server. The problem with this approach is that a fresh block of memory is set aside for each such process. While the number of clients connecting to the server remained reasonably low, this presented no difficulties. However, as the use of the Internet mushroomed, servers that created a new process for each client would grind to a halt as hundreds, possibly thousands, of clients attempted to access their services simultaneously. A way of significantly alleviating this problem is to use what are called threads, instead of processes. Though the use of threads cannot guarantee that a server will not crash, it greatly reduces the likelihood of it happening by significantly increasing the number of client programs that can be handled concurrently.

Thread Basics

A thread is a flow of control through a program. Unlike a process, a thread does not have a separate allocation of memory, but shares memory with other threads created

52 An Introduction to Network Programming with Java

by the same application. This means that servers using threads do not exhaust their supply of available memory and collapse under the weight of excessive demand from clients, as they were prone to do when creating many separate processes. In addition, the threads created by an application can share global variables, which is often highly desirable. This does not prevent each thread from having its own local variables, of course, since it will still have its own stack for such variables.

Though it has been entirely transparent to us and we have had to make no explicit programming allowance for it, we have already been making use of threads in our Java programming. In fact, we cannot avoid using threads in Java, since each program will have at least one thread that is launched automatically by our machinersquo;s JVM when that program is executed. Such a thread is created when main is started and lsquo;killedrsquo; when main terminates. If we wish to make use of further threads, in order to lsquo;offloadrsquo; processing tasks onto them, then we have to program such threads explicitly. Using more than one thread in this way is called multithreading.

Of course, unless we have a multiprocessor system, it is not possible to have more than one task being executed simultaneously. The operating system, then, must have some strategy for determining which thread is to be given use of the processor at any given time. On PCs, threads with the same priority are each given an equal time- slice or time quantum for execution on the processor. When the quantum expires, the first thread is suspended and the next thread in the queue is given the processor, and so on. If some threads require more urgent attention than others, then they may be assigned higher priorities (allowing pre-emption to occur). Under the Solaris operating system, a thread runs either to completion or until another higher-priority thread becomes ready. If the latter occurs first, then the second thread pre-empts the first and is given control of the processor. For threads with the same priority, time- slicing is used, so that a thread does not have to wait for another thread with the same priority to end.

Using Threads in Java

Java is unique amongst popular programming languages in making multithreading directly accessible to the programmer, without him/her having to go through an operating system API. Unfortunately, writing multithreaded programs can be rather tricky and there are certain pitfalls that need to be avoided. These pitfalls are caused principally by the need to coordinate the activities of the various threads, as will be seen in Section 3.4.

In Java, an object can be run as a thread if it implements the inbuilt interface Runnable, which has just one method: run. Thus, in order to implement the interface, we simply have to provide a definition for method run. Since the inbuilt class Thread implements this interface, there are two fundamental methods for creating a thread class:

      • create a class that extends Thread;
      • create a class that does not extend Thread and specify explicitly that it implements Runnable.

Multithreading and Multiplexing 53

Of

剩余内容已隐藏,支付完成后下载完整资料


3 多线程和多路复用

学习目标

读完这一章后,你应该:

  • 理解线程的含义(在编程上下文中);
  • 理解多线程编程的必要性;
  • 意识到多线程可能适用的典型情况;
  • 知道如何在Java中实现线程;
  • 知道如何在Java中实现变量锁定;
  • 意识到死锁带来的危险;
  • 知道使用什么Java方法来提高线程效率和减少死锁的可能性;
  • 知道如何实现多线程服务器;
  • 知道如何通过多路复用实现非阻塞服务器。

现在的情况往往是,程序需要同时执行多个重要任务(即“并发性”)。 例如,GUI驱动的程序可能在处理用户与界面的前台交互时显示背景动画,或者Web浏览器可能需要下载和显示图形文件的内容,同时呈现相关网页的其余部分。 客户/服务器应用程序在过去十年中的普及极大地加剧了这种需求,服务器程序有时不得不同时处理数百个客户的需求。

几年前,每个连接到服务器的客户端都会在服务器上生成一个新的进程。 这种方法的问题是为每个这样的进程预留了一个新的内存块。 由于连接到服务器的客户端数量仍然相当低,所以这并没有带来任何困难。 然而,随着互联网使用的迅速增长,为每个客户端创建一个新进程的服务器将逐渐瘫痪,因为数百个甚至数千个客户端试图同时访问他们的服务。 一种大大缓解这一问题的方法是使用所谓的线程,而不是进程。 虽然线程的使用不能保证服务器不会崩溃,但它通过显著增加可以同时处理的客户端程序的数量,大大的降低了发生这种情况的可能性。

线程基础

线程是通过程序进行控制的流程.. 与进程不同,线程没有单独的内存分配,而是通过同样的应用程序与创建的其他线程共享内存。

这意味着使用线程的服务器不会耗尽其可用内存的供应,也不会像创建多个独立进程时那样,在客户机的过度需求的重压下崩溃。 此外,应用程序创建的线程通常是非常理想的可以共享全局变量的。 当然,这并不能阻止每个线程都有自己的局部变量,因为它仍然有大量自己的变量。

尽管它对我们来说是完全透明的,但是我们没有为它提供明确的编程允许,不过我们已经在Java编程中使用了线程。 事实上,我们不能避免在Java中使用线程,因为每个程序都至少有一个线程,当该程序执行时,由我们机器的JVM自动启动。 这样的线程是在Main启动时创建的,而在Main终止时“杀死”。 如果我们希望使用进一步的线程,以便将处理任务“转移”到它们上,那么我们必须明确的对这样的线程进行编程。 以这种方式使用多个线程称为多线程..

当然,除非我们有一个多处理器系统,否则不可能同时执行多个任务。 因此,操作系统必须有一些策略来确定在任何给定时间使用处理器的线程。在PC机上,具有相同优先级的线程都有相同的时间片或时间量在处理器上执行。 当量子期满时,第一个线程被挂起,队列中的下一个线程被赋予处理器,以此类推。 如果某些线程比其他线程需要更紧急的处理,那么它们可能会被分配到更高的优先级(允许预先抢占)。 在Solaris操作系统下,一个线程要么运行到完成,要么直到另一个更高优先级的线程准备就绪。如果后者先发生,则第二个线程先占第一个线程,并获得对处理器的控制权。 对于具有相同优先级的线程,使用时间切片,这样线程就不必等待具有相同优先级的另一个线程结束。

使用Java中的线程

在流行的编程语言中,Java是独一无二的,它使程序员可以直接访问多线程,而不需要通过操作系统API。 不幸的是,编写多线程程序可能相当棘手,并且有些陷阱需要避免。 如第3.4节所述,造成这些缺陷的主要原因是需要协调各种线程的活动。

在Java中,如果一个对象实现了内置的接口Runnable(只有一个方法:Run),它就可以作为线程运行。 因此,为了实现这个接口,我们只需为方法Run提供一个定义。 由于内置类Thread实现了此接口,因此创建线程类有两种基本方法:

      • create a class that extends Thread;
      • create a class that does not extend Thread and specify explicitly that it implements Runnable.

当然,如果应用程序类已经有一个超类(Object除外),那么扩展Thread将不是一个选择,因为Java不支持多重继承.. 以下两个小节将依次考虑上述每一种方法..

      1. 扩展线程类

Run方法指定线程要执行的操作,并为在线程上运行的进程提供像Main对完整应用程序所做的那样的相同的作用,。 和main一样,run可能不能直接调用.. 包含程序调用start方法(从类Thread继承),然后自动调用run..

类Thread有七个构造函数,其中最常见的两个是:

        • Thread()
        • Thread(Stringlt;namegt;)

其中的第二个通过它的参数为线程提供了一个名称。 如果使用第一个,系统将生成表单Thread-n的名称,其中n是一个整数,从零开始,并为之后的线程增加值。 因此,如果通过第一个构造函数创建了三个线程,那么它们将分别命名为Thread-0、Thread-1和Thread-2。 无论使用哪个构造函数,都可以使用方法getName来检索名称。

Example

Thread firstThread = new Thread();

Thread secondThread = new Thread('namedThread'); System.out.println(firstThread.getName()); System.out.println(secondThread.getName());

上述项目的产出如下:

Thread-0 namedThread

注意,保有线程地址的变量的名称与线程的名称不一样! 但是,我们往往不需要知道后者。

方法sleep用于制定线程暂停指定的毫秒数。 例如:

myThread.sleep(1500); //Pause for 1.5 seconds.

这将暂停当前线程的执行,并允许执行其他线程。 当休眠时间到期时,休眠线程返回到就绪状态,等待处理器。

方法中断可用于中断单个线程。 特别是,在此之前,其他线程可以使用此方法“唤醒”睡眠时间已经过期了的线程。

由于如果另一个线程调用中断方法,方法sleep将抛出一个选中的异常(InterruptedException),因此必须从捕获此异常的try块中调用它。

在下一个例子中,使用了来自核心类Math中的static方法random用于为两个程中的每一个线程生成一个随机休眠时间,这些线程只显示自己的名称十次。 如果我们在不使用随机元素的情况下运行程序,那么它只会显示交替的名称,这将非常繁琐,并且不会给出线程正在使用的指示。 方法random返回范围0-0.999...中的随机十进制值,然后乘以3000的比列因子并将其类型转换为int,生成一个0-2999范围内的最终的整数值。 这种随机化技术也被用于之后的线程示例中,同样是为了避免从给定程序产生相同的输出模式。

注意在类的开头行中使用了extends Thread。 虽然这个类已经实现了Runnable接口(因此也有方法Run的定义),但Run的默认实现什么都不做,必须由我们提供的定义重写。

Example

public class ThreadShowName extends Thread

{

public static void main (String[] args)

{

ThreadShowName thread1, thread2;

thread1 = new ThreadShowName(); thread2 = new ThreadShowName();

thread1.start(); //Will call run. thread2.start(); //Will call run.

}

public void run()

{

int pause;

for (int i=0; ilt;10; i )

{

try

{

System.out.println(

getName() ' being executed.'); pause = (int)(Math.random()*3000);

sleep(pause); //0-3 seconds.

}

catch (InterruptedException interruptEx)

{

System.out.println(interruptEx);

}

}

}

}

在上面的程序中,两个线程中的每一个都执行了完全相同的任务,这意味着它们中的每一个都可以从同一个Thread类创建,并使用完全相同的Run方法。 当然,在实践中,不同的线程通常会执行不同的任务。 如果我们希望线程执行与彼此不同的任务,那么我们必须为每个线程创建一个单独的类(每个线程都有自己的Run方法),如下一个示例所示。

在本例中,我们将再次创建两个线程,但一个线程五次显示消息“Hello”,另一个线程输出整数1-5。 对于第一个线程,我们将创建一个名为HelloThread的类;对于第二个线程,我们将创建CountThread。 注意,这次扩展类Thread的不是主应用类(这里是ThreadHelloCount),而是两个从属类Hello Thread和Count Thread。 每个类都有自己版本的Run方法。

public class ThreadHelloCount

{

public static void main(String[] args)

{

HelloThread hello = new HelloThread(); CountThread count = new CountThread(); hello.start();

count.start();

}

}

class HelloThread extends Thread

{

public void run()

{

int pause;

for (int i=0; ilt;5; i )

{

try

{

System.out.println('Hello!');

//Again, introduce an element

//of randomnesshellip;

pause = (int)(Math.random()*3000);

sleep(pause);

}

catch (InterruptedException interruptEx)

{

System.out.println(interruptEx);

}

}

}

}

class CountThread extends Thread

{

int pause;

public void run()

{

for (int i=0; ilt;5; i )

{

try

{

System.out.println(i); pause=(int)(Math.random()*3000); sleep (pause);

}

catch (InterruptedException interruptEx)

{

System.out.println(interruptEx);

}

}

}

}

明白的实现可运行接口

这与上一小节中描述的技术非常相似.. 但是,使用此方法,我们首先创建一个明确地实现Runnable接口的应用程序类。 然后,为了创建线程,我们实例化了Runnable类的一个对象,并将其“包装”在一个线程对象中。 我们通过创建一个Thread对象并将Runnable对象作为

剩余内容已隐藏,支付完成后下载完整资料


资料编号:[409486],资料为PDF文档或Word文档,PDF文档可免费转换为Word

原文和译文剩余内容已隐藏,您需要先支付 30元 才能查看原文和译文全部内容!立即支付

以上是毕业论文外文翻译,课题毕业论文、任务书、文献综述、开题报告、程序设计、图纸设计等资料可联系客服协助查找。