范例解说Java里的线程概念与线程同步技术

线程 是一段完成某个特定功能的代码,程序中的执行线程。Java 虚拟机允许应用程序并发地运行多个执行线程。
每个线程都有一个优先级,高优先级线程的执行优先于低优先级线程。
进程不同的是,由同名类生成的多个线程共享相同的内存空间和系统资源。

线程与进程的区别:
一个线程是一个程序内部的顺序控制流。
1. 进程:每个进程都有独立的代码和数据空间(进程上下文) ,进程切换的开销大。线程:同一类线程共享代码和数据空间,每个线程有独立的运行栈和程序计数器(PC),线程切换的开销小。
2. 一个进程中可以包含多个线程。

本文将介绍以下线程方面的知识:
1,线程的创建
2,线程的状态
3,线程同步
4,线程组

理解线程的最有效的方法是通过实例来理解。下面我们将通过 售货员售书 为例,由浅入深地介绍线程的创建,通信,锁机制等概念。

售货员售书
我们假设一下售货员售书的操作流程:
1,我们假设有20本书,交给2个售货员去卖。
2,售货员可以卖掉任何一本尚未卖出去的书。换句话说,同一本书若被其中一位售出去了,则不能被另外一位再售出了。


清单1:

文件名说明
Book.java 书籍类
SellBookRunnable.java 售书类,线程的创建方法之一,该类实现了Runnable 接口,并实现了 run 方法。
SellBookThread.java 售书类,线程的创建方法之一,该类声明为 Thread 的子类,并重写 Thread 类的 run 方法。
CallSellBook.java 调用类。该类分别介绍了2种不同线程创建的调用方法。


Book.java

<script src="/images/code/js/shCore.js" type="text/javascript"></script> <script></script> <script src="/images/code/js/shBrushJava.js" type="text/javascript"></script>
  1. public class Book {   
  2.     private String name;   
  3.     private boolean sold = false;   
  4.   
  5.     public Book(String name) {   
  6.         this.name = name;   
  7.     }   
  8.     public String getName() {   
  9.         return name;   
  10.     }   
  11.   
  12.     public void setName(String name) {   
  13.         this.name = name;   
  14.     }   
  15.   
  16.     public boolean isSold() {   
  17.         return sold;   
  18.     }   
  19.   
  20.     public void setSold(boolean sold) {   
  21.         this.sold = sold;   
  22.     }   
  23. }  
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

 

  1. import java.util.List;   
  2.   
  3. public class SellBookRunnable implements Runnable {   
  4.     private String saleMan;   
  5.     private List<Book> bookList;   
  6.   
  7.     public SellBookRunnable(String saleMan, List<Book> bookList) {   
  8.         this.saleMan = saleMan;   
  9.         this.bookList = bookList;   
  10.     }   
  11.   
  12.     public void run() {   
  13.         for (int i = 0; i < bookList.size(); i++) {   
  14.             Book book = bookList.get(i);   
  15.             sellBook(book);   
  16.         }   
  17.     }   
  18.   
  19.     /**  
  20.      * 售货员卖书。我们这样描述售货员的卖书过程。  
  21.      *   
  22.      * @param book Book  
  23.      */  
  24.     private void sellBook(Book book) {   
  25.         //从开始售书-到售书完成,使用synchronized (book)保证book不被其他售货员售出   
  26.         synchronized (book) {   
  27.             if (book.isSold()) {   
  28.                 return;   
  29.             } else {   
  30.                 try {   
  31.                     //为了让各线程有执行机会,设置平均售书时间为0.5秒   
  32.                     Thread.sleep(500);   
  33.                 } catch (Exception e) {   
  34.                 }   
  35.                    
  36.                 //设置已售标志   
  37.                 book.setSold(true);   
  38.                 //打印该书已售信息   
  39.                 System.out.println("[" + saleMan + "]" + book.getName() + " sold out:"  
  40.                         + book.isSold() + ". by "  
  41.                         + Thread.currentThread().getName());   
  42.   
  43.             }   
  44.         }   
  45.     }   
  46. }  
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

 

  1. import java.util.List;   
  2.   
  3. public class SellBookThread extends Thread {   
  4.     private String saleMan;   
  5.     private List<Book> bookList;   
  6.   
  7.     public SellBookThread(String saleMan, List<Book> bookList) {   
  8.         this.saleMan = saleMan;   
  9.         this.bookList = bookList;   
  10.     }   
  11.   
  12.     public void run() {   
  13.         for (int i = 0; i < bookList.size(); i++) {   
  14.             Book book = bookList.get(i);   
  15.             sellBook(book);   
  16.         }   
  17.     }   
  18.   
  19.     /**  
  20.      * 售货员卖书。我们这样描述售货员的卖书过程。  
  21.      *   
  22.      * @param book Book  
  23.      */  
  24.     private void sellBook(Book book) {   
  25.         //从开始售书-到售书完成,使用synchronized (book)保证book不被其他售货员售出   
  26.         synchronized (book) {   
  27.             if (book.isSold()) {   
  28.                 return;   
  29.             } else {   
  30.                 try {   
  31.                     //为了让各线程有执行机会,设置平均售书时间为0.5秒   
  32.                     Thread.sleep(500);   
  33.                 } catch (Exception e) {   
  34.                 }   
  35.                    
  36.                 //设置已售标志   
  37.                 book.setSold(true);   
  38.                 //打印该书已售信息   
  39.                 System.out.println("[" + saleMan + "]" + book.getName() + " sold out:"  
  40.                         + book.isSold() + ". by "  
  41.                         + Thread.currentThread().getName());   
  42.   
  43.             }   
  44.         }   
  45.     }   
  46. }  
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

 

  1. import java.util.ArrayList;   
  2. import java.util.List;   
  3.   
  4. //该类调用SellBookXxx类   
  5. public class CallSellBook {   
  6.   
  7.     /**  
  8.      * 用线程模拟这个售书的过程  
  9.      */  
  10.     public static void main(String[] args) {   
  11.         //方法1:   
  12.         callSellBookThread();   
  13.   
  14.         //or   
  15.            
  16.         //方法2:   
  17.         //callSellBookRunnable();   
  18.     }   
  19.        
  20.     //调用SellBookRunnable(Runnable接口实现类)模拟售书过程   
  21.     public static void callSellBookThread() {   
  22.         List <Book>bookList = getBookListForSale();   
  23.            
  24.         //将预售书籍清单交给售货员SaleMan1   
  25.         Thread t1 = new SellBookThread("SaleMan1", bookList);   
  26.         //将预售书籍清单交给售货员SaleMan2   
  27.         Thread t2 = new SellBookThread("SaleMan2", bookList);   
  28.   
  29.         //售货员SaleMan1开始售书   
  30.         t1.start();   
  31.         //售货员SaleMan2开始售书   
  32.         t2.start();   
  33.     }   
  34.        
  35.        
  36.     //调用SellBookRunnable(Runnable接口实现类)模拟售书过程   
  37.     public static void callSellBookRunnable() {   
  38.         List <Book>bookList = getBookListForSale();   
  39.            
  40.         //将预售书籍清单交给售货员SaleMan1   
  41.         Thread t1 = new Thread(new SellBookRunnable("SaleMan1", bookList));   
  42.         //将预售书籍清单交给售货员SaleMan2   
  43.         Thread t2 = new Thread(new SellBookRunnable("SaleMan2", bookList));   
  44.   
  45.         //售货员SaleMan1开始售书   
  46.         t1.start();   
  47.         //售货员SaleMan2开始售书   
  48.         t2.start();   
  49.     }   
  50.   
  51.     //准备预售书籍   
  52.     public static List<Book> getBookListForSale() {   
  53.         List <Book>bookList = new ArrayList();   
  54.         for (int i = 0; i < 20; i++) {   
  55.             Book book = new Book("Book" + i);   
  56.             bookList.add(book);   
  57.         }   
  58.            
  59.         return bookList;   
  60.     }   
  61. }  
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

 

线程的创建

创建新执行线程有两种方法。
方法一种方法是将类声明为 Thread 的子类。该子类应重写 Thread 类的 run 方法。事实上类Thread本身也实现了接口Runnable,所以我们可以同过继承Thread类实现线程体。
参考:SellBookThread.javaCallSellBook.java
另一种方法是声明实现 Runnable 接口的类。该类然后实现 run 方法。
参考:SellBookRunnable.javaCallSellBook.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

<script src="/images/code/js/shCore.js" type="text/javascript"></script> <script></script> <script src="/images/code/js/shBrushJava.js" type="text/javascript"></script>
  1.     private void sellBook(Book book) {   
  2.         synchronized (book) {   
  3.             ...   
  4.         }   
  5.     }  
    private void sellBook(Book book) {
        synchronized (book) {
            ...
        }
    }


该例synchronized (book) 表示若多个线程同时访问时,只让其中一个线程最先取得book对象,其它线程则阻塞直到代码块执行完毕book对象被释放后,其它线程才能取得该book对象继续执行。
很多情况下,可以使用synchronized (this){...}来同步代码块。但需要注意的是,使用this作为同步对象的话,如果同一个类中存在多个synchronized (this){...}代码块,其中任何一个synchronized(this)代码块处于被执行状态,则其它线程对其他synchronized(this)代码块的访问也会受到阻塞。

同步方法例:

  1. synchronized private void sellBook(Book book) {   
  2. ...   
  3. }  
synchronized private void sellBook(Book book) {
...
}


这种方法其实相当于

  1. private void sellBook(Book book) {   
  2.     synchronized(this) {   
  3.         ...   
  4.     }   
  5. }  
private void sellBook(Book book) {
    synchronized(this) {
        ...
    }
}


由于默认采用this作为同步对象,所以当一个类中有多个synchronized方法时,同样会存在以上问题:即如果有一个线程访问其中某个synchronized方法时,直到该方法执行完毕,其它线程对其它synchronized方法的访问也将受到阻塞。
有关synchronized详细说明我们将在其它文章中加以说明。


使用java.util.concurrent.locks.ReentrantLock和java.util.concurrent.locks.ReentrantReadWriteLock类同步线程。
J2SE5.0加入了ReentrantLock和ReentrantReadWriteLock可以对线程进行同步,这里举一个最简单的例子对其加以说明:

  1.  class X {   
  2.    private final ReentrantLock lock = new ReentrantLock();   
  3.    // ...   
  4.   
  5.    public void m() {    
  6.      lock.lock();  // block until condition holds   
  7.      try {   
  8.        // ... method body   
  9.      } finally {   
  10.        lock.unlock()   
  11.      }   
  12.    }   
  13.  }   
  14.    
 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()等方法简单创建线程池。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值