您的位置:首页 > 编程语言 > Java开发

Java多线程同步调度技术:实战经典案例

2012-04-23 16:30 309 查看
http://tech.it168.com/a2012/0131/1305/000001305256.shtml



【IT168 技术】作为一名Java开发人员,不管作为面试官,还是被面试的对象,甚至是两者兼有。Java线程技术的考察,势必成为整个面试过程的重点之一。分析一下原因,不难发现,实际工作当中,涉及到的Java应用几乎全是多线程,单线程Java应用微乎其微。如何管理好多线程的调度,比如线程的安全问题,是Java应用实现高效并行运行的关键点之一,也是摆在大多数Java初学者的难题。本文的目的是以Java线程核心之一的同步Synchronized机制入手,原理与实例并重,以便读者能够较好的理解并熟练应用Java多线程技术。
  理解线程概念是基础

  简单的说,相对于操作系统而言,运行的程序我们称之为进程。而进程又可细分为多个线程,从这个角度来说,线程是操作系统能够调度的最小单位。服务于同一进程的不同线程,按照角色的不同,执行不同的任务,我们称为多线程。多线程由于共享内存资源而获得较高的运行效率,对于应用程序,尤其是企业级应用,性能指标尤为重要。

  无间道II的一句经典台词“出来混,迟早要还的”被人津津乐道。多线程带来性能提高的同时,也引入复杂的问题。其中之一就是多线程的同步问题。试想一下,当多个线程同时读写同一份文件,一个线程正在写,还未写完,另一个线程就开始读,这样就引起了所谓的线程冲突。

  掌握线程同步是关键

  面对上述线程冲突的问题,一个习惯性的想法是,同一时间,要么读,要么写。当写线程(我们姑且命名为A)在执行时,读线程(我们命名为B)挂起,反之亦然。换句话说,就是把并行的两个操作,通过引入某种控制,变换成串行操作。我们把多线程通过特定的东西(如互斥量)来控制线程之间的执行顺序(同步)这种操作称之为线程同步。

  在Java编程模型中,我们称所谓的互斥量为线程锁,它对应一个Java实例对象或者类对象。而同步这一过程使用关键字Synchronized表示。

  千里之行始于足下:Synchronized语法详解

  俗话说,工欲善其事必先利其器,在给出Java一些具体的编程实例前,我们先来看看Synchronized关键字的具体用法。

  从使用方式上,Synchronized包含两种用法:Synchronized 方法和 Synchronized 块。前者在方法声明前加synchronized关键字,如:

public synchronized void accessVal(int newVal);

  用来控制对共享资源的访问。同一时间,仅允许一个线程操作该方法,其它线程排队。后者在代码块前加synchronized关键字,如:

synchronized(syncObject) {

  //允许访问控制的代码

  }

  和前者一样,同一时间,仅允许一个线程进入该代码块。两者的唯一区别是,Synchronized 方法对应的锁对象是this,而Synchronized 块对应的锁对象可以自由指定。

  从作用域的角度,Synchronized也分为两种:实例对象和类对象。synchronized aMethod(){}是一个实例方法,这就意味着,对于同一个对象,同一时间,仅有可能被一个线程调用,其它线程排队。而对于与不同的对象,其它线程仍然能访问该方法。所不同的是,对于类对象而言,情况则完全不同。synchronized static aStaicMethod(){}是一个类方法,这就意味着,同一时间,该方法只能被一个线程调用,其它的线程没有任何机会。除非当前线程执行完操作或终止,释放线程锁。

  此外,虽然继承是面向对象的Java语言特点之一,但是Synchronized关键字不具备继承性。也就是说,基类的方法synchronized aMethod(){} 在继承类中并不自动是synchronized aMethod(){},而是变成了aMethod(){}。继承类需要显式指定synchronized关键字。

  Synchronized实战:生产者与消费者

  线程的同步最经典的案例莫过于生产者与消费者问题。我们的例子将围绕它展开。生产者与消费者指的是两个线程共享一个公共的固定大小的缓冲区。其中一个是生产者线程,用于将“产品”放入缓冲区;另一个是消费者线程,用于从缓冲区取出“产品”。问题出现在缓冲区已满,生产者还想添加“产品”,其解决办法是让生产者此时休眠,待消费者从缓冲区取走一个或者多个“产品”再唤醒。同样地,当缓冲区已空,消费者还想取出“产品”,此时也可以让消费者休眠,待生产者放入一个或者多个数据时再唤醒它。

  为简单起见,这里我们假设缓冲区的长度为1,这里的“产品”对应一个整数。该缓冲区提供读、写操作。对应的Buffer类如下:

class Buffer {

  intn;

  booleanhasValue =
false;

  synchronizedint get() {

  while(!hasValue)

  try {

  wait();

  } catch(InterruptedException e) {

  e.printStackTrace();

  }

  System.out.println("Get:
" + n);

  hasValue =
false;

  notify();

  returnn;

  }

  synchronizedvoid put(int n) {

  while(hasValue)

  try {

  wait();

  } catch(InterruptedException e) {

  e.printStackTrace();

  }

  this.n = n;

  hasValue =
true;

  System.out.println("Put:
" + n);

  notify();

  }

  }

  

  wait()与notify()是Java线程同步机制最重要的两个方法。wait方法可以使在当前线程的对象等待,直到别的线程调用此对象的notify或notifyAll方法。

  生产者线程类如下:

class Producer implements Runnable {

  Buffer q;

  Producer(Buffer q) {

  this.q = q;

  new Thread(this,
"Producer").start();

  }

  publicvoid run() {

  int i
= 0;

  while(true) {

  q.put(i++);

  }

  }

  }

  消费者线程类如下:

 class Consumer implements Runnable {

  Buffer q;

  Consumer(Buffer q) {

  this.q = q;

  new Thread(this,
"Consumer").start();

  }

  publicvoid run() {

  while(true) {

  q.get();

  }

  }

  }

  Main函数:

publicclass Sample {

  publicstaticvoid main(String args[]) {

  Buffer q =
new Buffer();

  new Consumer(q);

  new Producer(q);

  System.out.println("Press Ctrl-C to stop.");

  }

  }
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: