转自:http://www.iteye.com/magazines/131-Java-Concurrency
计算机的使用者一直以为他们的计算机可以同时做很多事情。他们认为当其他的应用程序在下载文件,管理打印队列或者缓冲音频的时候他们可以继续在文字处理程序上工作。甚至对于单个应用程序,他们任然期待它能在在同一时间做很多事情。举个例子,一个流媒体播放程序必须能同时完成以下工作:从网络上读取数字音频,解压缩数字音频,管理播放和更新程序显示。甚至文字处理器也应该能在忙于重新格式化文本和刷新显示的情况下同时响应键盘和鼠标事件。这样的软件就被称为并发软件。
通过Java语言和Java类库对于基础并发的支持,Java平台具有完全(from the ground up)支持并发编程的能力。从JDK5.0起,Java平台还引入了高级并发APIs。这个课程不仅涵盖了Java平台基础并发内容,还对高级并发APIs有一定的阐述。
通过Java语言和Java类库对于基础并发的支持,Java平台具有完全(from the ground up)支持并发编程的能力。从JDK5.0起,Java平台还引入了高级并发APIs。这个课程不仅涵盖了Java平台基础并发内容,还对高级并发APIs有一定的阐述。
进程和线程![Java并发教程(Oracle官方资料) Top](https://i-blog.csdnimg.cn/blog_migrate/a4c26d1e5885305701be709a3d33442f.gif)
(本部分 原文链接, 译文链接,译者:bjsuo,校对:郑旭东)
在并发编程中,有两个基本的执行单元:进程和线程。在java语言中,并发编程最关心的是线程,然而,进程也是非常重要的。
即使在只有单一的执行核心的计算机系统中,也有许多活动的进程和线程。因此,在任何给定的时刻,只有一个线程在实际执行。处理器的处理时间是通过操作系统的时间片在进程和线程中共享的。
现在具有多处理器或有多个执行内核的多处理器的计算机系统越来越普遍,这大大增强了系统并发执行的进程和线程的吞吐量–但在不没有多个处理器或执行内核的简单的系统中,并发任然是可能的。
进程
进程具有一个独立的执行环境。通常情况下,进程拥有一个完整的、私有的基本运行资源集合。特别地,每个进程都有自己的内存空间。
进程往往被看作是程序或应用的代名词,然而,用户看到的一个单独的应用程序实际上可能是一组相互协作的进程集合。为了便于进程之间的通信,大多数操作系统都支持进程间通信(IPC),如pipes和sockets。IPC不仅支持同一系统上的通信,也支持不同的系统。
Java虚拟机的大多数实现是单进程的。Java应用可以使用的ProcessBuilder对象创建额外的进程,多进程应用超出了本课的范围。
线程
线程有时也被称为轻量级的进程。进程和线程都提供了一个执行环境,但创建一个新的线程比创建一个新的进程需要的资源要少。
线程是在进程中存在的 —每个进程最少有一个线程。线程共享进程的资源,包括内存和打开的文件。这样提高了效率,但潜在的问题就是线程间的通信。
多线程的执行是Java平台的一个基本特征。每个应用都至少有一个线程 –或几个,如果算上“系统”线程的话,比如内存管理和信号处理等。但是从程序员的角度来看,启动的只有一个线程,叫主线程。这个线程有能力创建额外的线程,我们将在下一节演示。
线程对象![Java并发教程(Oracle官方资料) Top](https://i-blog.csdnimg.cn/blog_migrate/a4c26d1e5885305701be709a3d33442f.gif)
(本部分 原文链接, 译文链接,译者:郑旭东)
在Java中,每个线程都是 Thread类的实例。并发应用中一般有两种不同的线程创建策略。
- 直接控制线程的创建和管理,每当应用程序需要执行一个异步任务的时候就为其创建一个线程
- 将线程的管理从应用程序中抽象出来作为执行器,应用程序将任务传递给执行器,有执行器负责执行。
定义并启动一个线程
应用程序在创建一个线程实例时,必须提供需要在线程中运行的代码。有两种方式去做到这一点:
- 提供一个Runnable对象。Runnable对象仅包含一个run()方法,在这个方法中定义的代码将在会线程中执行。将Runnable对象传递给Thread类的构造函数即可,如下面这个HelloRunnable的例子:
Java代码
- public
class HelloRunnable implements Runnable { -
-
public void run() { -
System.out.println("Hello from a thread!"); -
} -
-
public static void main(String args[]) { -
(new Thread(new HelloRunnable())).start(); -
} -
- }
- 继承Thread类。Thread类自身已实现了Runnable接口,但它的run()方法中并没有定义任何代码。应用程序可以继承与Thread类,并复写run()方法。如例子HelloThread
Java代码
- public
class HelloThread extends Thread { -
-
public void run() { -
System.out.println("Hello from a thread!"); -
} -
-
public static void main(String args[]) { -
(new HelloThread()).start(); -
} -
- }
需要注意的是,上述两个例子都需要调用Thread.start()方法来启动一个新的线程。哪一种方式是我们应该使用的?相对来说,第一种更加通用,因为Runnable对象可以继承于其他类(Java只支持单继承,当一个类继承与Thread类后,就无法继承与其他类)。第二种方法更易于在简单的应用程序中使用,但它的局限就是:你的任务类必须是Thread的子类。这个课程更加聚焦于第一种将Runnable任务和Thread类分离的方式。不仅仅是因为这种方式更加灵活,更因为它更适合后面将要介绍的高级线程管理API。Thread类定义了一些对线程管理十分有用的的方法。在这些方法中,有一些静态方法可以给当前线程调用,它们可以提供一些有关线程的信息,或者影响线程的状态。而其他一些方法可以由其他线程进行调用,用于管理线程和Thread对象。我们将在下面的章节中,深入探讨这些内容。
使用Sleep方法暂停一个线程
使用Thread.sleep()方法可以暂停当前线程一段时间。这是一种使处理器时间可以被其他线程或者运用程序使用的有效方式。sleep()方法还可以用于调整线程执行节奏(见下面的例子)和等待其他有执行时间需求的线程(这个例子将在下一节演示)。
在Thread中有两个不同的sleep()方法,一个使用毫秒表示休眠的时间,而另一个是用纳秒。由于操作系统的限制休眠时间并不能保证十分精确。休眠周期可以被interrups所终止,我们将在后面看到这样的例子。不管在任何情况下,我们都不应该假定调用了sleep()方法就可以将一个线程暂停一个十分精确的时间周期。
SleepMessages程序为我们展示了使用sleep()方法每四秒打印一个信息的例子
Java代码