转自:http://www.lifevv.com/java/doc/20071025183748112.html?page=1
范例解说Java里的线程概念与线程同步技术
media="all" href="/tf-resource/css/outputText/tf-outputText.css" type="text/css" rel="stylesheet" />本文以通俗易懂的范例入手,由浅入深的全面介绍了Java里的线程概念与线程同步技术。 |
<script type="text/javascript">
</script>
线程 是一段完成某个特定功能的代码,程序中的执行线程。Java 虚拟机允许应用程序并发地运行多个执行线程。 每个线程都有一个优先级,高优先级线程的执行优先于低优先级线程。 进程不同的是,由同名类生成的多个线程共享相同的内存空间和系统资源。 线程与进程的区别: 一个线程是一个程序内部的顺序控制流。 1. 进程:每个进程都有独立的代码和数据空间(进程上下文) ,进程切换的开销大。线程:同一类线程共享代码和数据空间,每个线程有独立的运行栈和程序计数器(PC),线程切换的开销小。 2. 一个进程中可以包含多个线程。 本文将介绍以下线程方面的知识: 1,线程的创建 2,线程的状态 3,线程同步 4,线程组 理解线程的最有效的方法是通过实例来理解。下面我们将通过 售货员售书 为例,由浅入深地介绍线程的创建,通信,锁机制等概念。 售货员售书 我们假设一下售货员售书的操作流程: 1,我们假设有20本书,交给2个售货员去卖。 2,售货员可以卖掉任何一本尚未卖出去的书。换句话说,同一本书若被其中一位售出去了,则不能被另外一位再售出了。 清单1:
Book.java
public
class Book {
private String name; private boolean sold = false; public Book(String name) { this.name = name; } public String getName() { return name; } public void setName(String name) { this.name = name; } public boolean isSold() { return sold; } public void setSold( boolean sold) { this.sold = sold; } }
SellBookRunnable.java
import java.util.List;
public class SellBookRunnable implements Runnable { private String saleMan; private List<Book> bookList; public SellBookRunnable(String saleMan, List<Book> bookList) { this.saleMan = saleMan; this.bookList = bookList; } public void run() { for ( int i = 0; i < bookList.size(); i++) { Book book = bookList.get(i); sellBook(book); } } /** * 售货员卖书。我们这样描述售货员的卖书过程。 * * @param book Book */ private void sellBook(Book book) { //从开始售书-到售书完成,使用synchronized (book)保证book不被其他售货员售出 synchronized (book) { if (book.isSold()) { return; } else { try { //为了让各线程有执行机会,设置平均售书时间为0.5秒 Thread.sleep(500); } catch (Exception e) { } //设置已售标志 book.setSold( true); //打印该书已售信息 System.out.println(" [" + saleMan + " ]" + book.getName() + " sold out:" + book.isSold() + " . by " + Thread.currentThread().getName()); } } } }
SellBookThread.java
import java.util.List;
public class SellBookThread extends Thread { private String saleMan; private List<Book> bookList; public SellBookThread(String saleMan, List<Book> bookList) { this.saleMan = saleMan; this.bookList = bookList; } public void run() { for ( int i = 0; i < bookList.size(); i++) { Book book = bookList.get(i); sellBook(book); } } /** * 售货员卖书。我们这样描述售货员的卖书过程。 * * @param book Book */ private void sellBook(Book book) { //从开始售书-到售书完成,使用synchronized (book)保证book不被其他售货员售出 synchronized (book) { if (book.isSold()) { return; } else { try { //为了让各线程有执行机会,设置平均售书时间为0.5秒 Thread.sleep(500); } catch (Exception e) { } //设置已售标志 book.setSold( true); //打印该书已售信息 System.out.println(" [" + saleMan + " ]" + book.getName() + " sold out:" + book.isSold() + " . by " + Thread.currentThread().getName()); } } } }
CallSellBook.java
import java.util.ArrayList;
import java.util.List; //该类调用SellBookXxx类 public class CallSellBook { /** * 用线程模拟这个售书的过程 */ public static void main(String[] args) { //方法1: callSellBookThread(); //or //方法2: //callSellBookRunnable(); } //调用SellBookRunnable(Runnable接口实现类)模拟售书过程 public static void callSellBookThread() { List <Book>bookList = getBookListForSale(); //将预售书籍清单交给售货员SaleMan1 Thread t1 = new SellBookThread(" SaleMan1", bookList); //将预售书籍清单交给售货员SaleMan2 Thread t2 = new SellBookThread(" SaleMan2", bookList); //售货员SaleMan1开始售书 t1.start(); //售货员SaleMan2开始售书 t2.start(); } //调用SellBookRunnable(Runnable接口实现类)模拟售书过程 public static void callSellBookRunnable() { List <Book>bookList = getBookListForSale(); //将预售书籍清单交给售货员SaleMan1 Thread t1 = new Thread( new SellBookRunnable(" SaleMan1", bookList)); //将预售书籍清单交给售货员SaleMan2 Thread t2 = new Thread( new SellBookRunnable(" SaleMan2", bookList)); //售货员SaleMan1开始售书 t1.start(); //售货员SaleMan2开始售书 t2.start(); } //准备预售书籍 public static List<Book> getBookListForSale() { List <Book>bookList = new ArrayList(); for ( int i = 0; i < 20; i++) { Book book = new Book(" Book" + i); bookList.add(book); } return bookList; } } 执行CallSellBook
[SaleMan1]Book0 sold out:true. by Thread-0
[SaleMan2]Book1 sold out:true. by Thread-1 [SaleMan2]Book2 sold out:true. by Thread-1 [SaleMan2]Book3 sold out:true. by Thread-1 [SaleMan2]Book4 sold out:true. by Thread-1 [SaleMan2]Book5 sold out:true. by Thread-1 [SaleMan1]Book6 sold out:true. by Thread-0 [SaleMan1]Book7 sold out:true. by Thread-0 [SaleMan1]Book8 sold out:true. by Thread-0 [SaleMan1]Book9 sold out:true. by Thread-0 [SaleMan1]Book10 sold out:true. by Thread-0 [SaleMan1]Book11 sold out:true. by Thread-0 [SaleMan2]Book12 sold out:true. by Thread-1 [SaleMan2]Book13 sold out:true. by Thread-1 [SaleMan2]Book14 sold out:true. by Thread-1 [SaleMan2]Book15 sold out:true. by Thread-1 [SaleMan2]Book16 sold out:true. by Thread-1 [SaleMan2]Book17 sold out:true. by Thread-1 [SaleMan1]Book18 sold out:true. by Thread-0 [SaleMan1]Book19 sold out:true. by Thread-0 |
范例解说Java里的线程概念与线程同步技术
media="all" href="/tf-resource/css/outputText/tf-outputText.css" type="text/css" rel="stylesheet" />本文以通俗易懂的范例入手,由浅入深的全面介绍了Java里的线程概念与线程同步技术。 |
<script type="text/javascript">
</script>
线程的创建
创建新执行线程有两种方法。
方法一种方法是将类声明为 Thread 的子类。该子类应重写 Thread 类的 run 方法。事实上类Thread本身也实现了接口Runnable,所以我们可以同过继承Thread类实现线程体。 参考: SellBookThread.java 与 CallSellBook.java 另一种方法是声明实现 Runnable 接口的类。该类然后实现 run 方法。 参考: SellBookRunnable.java 与 CallSellBook.java
线程的状态
线程有四种状态:创建状态(New),可运行状态(Runnable),阻塞状态(Blocked),死亡状态(Dead)。
创建状态(New): 当执行完 Thread t1 = new SellBookThread("SaleMan1", bookList); 语句之后,则t1处于创建状态(New)。此时t1并未真正运行。 可运行状态(Runnable): 当Thread t1被创建,并执行完 t1.start(); 语句之后,t1就处于可运行状态(Runnable)。此时,系统为线程t1分配其所需的系统资源。并对t1加以调用(或者根据任务调度情况准备调用)。 阻塞状态(Blocked): 由于以下原因: 1) 调用了sleep()方法; 2) 调用了suspend()方法(该方法已不推荐使用); 3) 为等待条件锁,调用wait()方法等; 4) 输入输出,或消息发生阻塞; 使得线程处于阻塞状态(Blocked)。处于该状态的线程即使处理器空闲,也不会得到执行。 死亡状态(Dead): 死亡状态(Dead)可以为自然死亡(线程运行完毕),或者调用了stop()方法(该方法已不推荐使用)。
线程的优先级:
可以通过Thread类的
void setPriority(int newPriority) 方法为线程设置优先级。但是不能保证高优先级的线程就会被先运行。
线程组:
可以通过
ThreadGroup group = new ThreadGroup(groupName); Thread t1 = new Thread(ThreadGroup g, Runnable r1); Thread t1 = new Thread(ThreadGroup g, Runnable r2); 等方法把多个线程加到一个线程组里去,这样可以通过ThreadGroup对这些线程进行某些统一操作, 例如:group.interrupt();中断该组所有线程。
线程unchecked异常处理器:
可以通过:
public void static Thread.setDefaultUncaughtExceptionHandler(UncaughtExceptionHandler) 方法为所有线程指定一个unchecked异常处理器,该处理器必须实现UncaughtExceptionHandler接口。
线程同步:
线程同步指多个线程同时访问某资源时,采用一系列的机制以保证同时最多只能一个线程访问该资源。
线程同步是多线程中必须考虑和解决的问题,因为很可能发生多个线程同时访问(主要是写操作)同一资源,如果不进行线程同步,很可能会引起数据混乱,造成线程死锁等问题。 使用synchronized同步线程。 在J2SE5.0之前,只能使用synchronized来同步线程。可以使用synchronized来同步代码块或者方法。 同步代码块例: synchronized(欲同步的对象obj) {需要同步的代码块}可以同步代码块。 参考: SellBookThread.java
private
void sellBook(Book book) {
该例synchronized (book) 表示若多个线程同时访问时,只让其中一个线程最先取得book对象,其它线程则阻塞直到代码块执行完毕book对象被释放后,其它线程才能取得该book对象继续执行。
synchronized (book) { ... } } 很多情况下,可以使用synchronized (this){...}来同步代码块。但需要注意的是,使用this作为同步对象的话,如果同一个类中存在多个synchronized (this){...}代码块,其中任何一个synchronized(this)代码块处于被执行状态,则其它线程对其他synchronized(this)代码块的访问也会受到阻塞。 同步方法例:
synchronized
private
void sellBook(Book book) {
这种方法其实相当于
... }
private
void sellBook(Book book) {
由于默认采用this作为同步对象,所以当一个类中有多个synchronized方法时,同样会存在以上问题:即如果有一个线程访问其中某个synchronized方法时,直到该方法执行完毕,其它线程对其它synchronized方法的访问也将受到阻塞。
synchronized( this) { ... } } 有关synchronized详细说明我们将在其它文章中加以说明。 使用java.util.concurrent.locks.ReentrantLock和java.util.concurrent.locks.ReentrantReadWriteLock类同步线程。 J2SE5.0加入了ReentrantLock和ReentrantReadWriteLock可以对线程进行同步,这里举一个最简单的例子对其加以说明:
class X {
private final ReentrantLock lock = new ReentrantLock(); // ... public void m() { lock.lock(); // block until condition holds try { // ... method body } finally { lock.unlock() } } }
其它J2SE5.0新导入的有关线程的相关接口/类:
java.util.concurrent.Future
Future接口可以保持/取得异步执行的结果值 java.util.concurrent.Callable 类似于Runnable接口。但Runnable不能返回值,也不能抛出checked异常 java.util.concurrent.ExecutorService 该接口继承了Executor接口。可以通过submit方法把Runnable,Callable对象转换为Future 形式。 java.util.concurrent.FutureTask 该类实现了Runnable和Future接口。提供异步执行的取消以及异步执行结果的取得等功能。 java.util.concurrent.Executor 执行指定的Runnable对象 java.util.concurrent.Executors 工具类。提供静态方法可以创建Executor,ExecutorService,Callable等对象。可以通过newCachedThreadPool()等方法简单创建线程池。 |