Chinaunix首页 | 论坛 | 博客
  • 博客访问: 29144
  • 博文数量: 19
  • 博客积分: 410
  • 博客等级: 下士
  • 技术积分: 132
  • 用 户 组: 普通用户
  • 注册时间: 2012-03-30 13:33
文章分类
文章存档

2012年(19)

我的朋友

分类: Delphi

2012-03-30 14:36:50

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

  理解线程概念是基础

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

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

  掌握线程同步是关键

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

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

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

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

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

#div_code img{border:0px;}
public synchronized void accessVal(int newVal);

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

#div_code img{border:0px;}
synchronized(syncObject) {

  
//允许访问控制的代码

  }

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

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

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

  Synchronized实战:生产者与消费者

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

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

#div_code img{border:0px;}
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方法。

  生产者线程类如下:

#div_code img{border:0px;}
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
++);

  }

  }

  }

  消费者线程类如下:

#div_code img{border:0px;}
 class Consumer implements Runnable {

  Buffer q;

  Consumer(Buffer q) {

  this.q
= q;

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

  }

  publicvoid run() {

  
while(true) {

  q.get();

  }

  }

  }

  Main函数:

#div_code img{border:0px;}
publicclass Sample {

  publicstaticvoid main(
String args[]) {

  Buffer q
= new Buffer();

  
new Consumer(q);

  
new Producer(q);

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

  }

  }
阅读(1180) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~