多线程
1.线程的基本概念
* 线程是进程中可独立执行的子任务,一个进程可以有一个或多个线程,每个线程都有一个唯一的标识符
* 进程和线程的区别:进程空间大体分为 数据区,代码区,栈区,堆区,多个进程的内部数据和状态都是完全独立的,
而线程共享进程的数据区,代码区,堆区,只有栈区是独立的,所以线程切换比进程切换的代价小
2.创建线程的方式
有两种:
a. 继承 Thread 类
例子:
public class MyThread extends Thread{
public void run(){
System.out.println("I am "+getName());//输出线程名字
}
public static void main(String args[]){
System.out.println(
"---------main start--------");
Thread t=new MyThread();
t.start();
System.out.println(
"---------main end---------");
}
}


b. 实现 Runnable 接口
如果类继承了别的类,就不能继承 Thread 了,就要实现 Runnable 接口了
例子:
public class MyThread implements Runnable{
public void run(){
System.out.println(
"I am "+Thread.currentThread().getName()); // Thread.currentThread() 返回当前线程的引用
}
public static void main(String args[]){
System.out.println(
"---------main start--------");
MyThread mt=new MyThread();
// mt 为目标线程,但不是线程,没有 sleep,run 等方法
Thread t=new Thread(mt);
t.start();
System.out.println(
"---------main end---------");
}
}


run()是 Thread 最重要的方法,想让线程做事,必须重写 run(), 线程的启动并不是调用 run(),而是调用 Thread 的 start(),
start() 将线程转入可运行状态,等待JVM安排,所以运行中的线程根据优先级保存在一个池中

3.常用方法
a. sleep 暂停线程的执行,让其他线程得到机会,sleep要抛出异常,必须抓住
try{ sleep(5*1000); }catch(InterruptedException e){}// sleep的单位是毫秒,5*1000 表示 5 秒,这是为了可读性
例子:
class MyThread extends Thread{

public void run(){
System.out.println(
"--------- my thread Begin sleep ---------");
try{
Thread.sleep( 20 * 1000);
}catch( Exception e ){
System.out.println(
"catch exception........" );
}
System.out.println(
"------ return from run ------" );
}
}

public class SleepTest{
public static void main( String[] args ){
MyThread mt = new MyThread();
mt.start();
try{
Thread.sleep( 3 * 1000 );
}catch( Exception e ){}
System.out.println(
"[main] interrupt my thread" );
mt.interrupt();
//非正常醒,会抛异常
System.out.println(
"-- return from main ---" );
}
}

b. isAlive 判断线程目前是否在执行状态中
c. suspend 暂停线程
d. resume 要求被暂停的线程继续执行
e. join 等待线程结束,被等待的线程不结束,目前线程就不执行
例子:
class MyThread extends Thread{
public void run(){
for(int i=0;i<5;i++){
try{
sleep(1*1000);
}catch(Exception e){
e.printStackTrace();
}
System.out.println(
"my thread is running...");
}
System.out.println(
"my thread stop");
}
}
public class Join{
public static void main(String args[]){
System.out.println(
"-----------main begin---------");
Thread mt=new MyThread();
mt.start();
try{
mt.join();
}catch(Exception e){
e.printStackTrace();
}
System.out.println(
"-----------return from main------");
}
}

f. yield 将执行的权利交给其他线程,自己到队列的最后等待

4.线程结束方法
用标志变量指示线程的结束,stop() 已经过期了,因为它太粗鲁,会破坏重要操作
如果用标志变量 while(flag){...} 可以保证循环体内的代码完整执行
例子:
class MyThread extends Thread{
private boolean isStop=false;
public void exit(){
isStop=true;
}
public void run(){
while(!isStop){
System.out.println(
"my thread is running...");
}
System.out.println(
"my thread stop");
}
}
public class StopTest{
public static void main(String args[]){
System.out.println(
"-----------main begin---------");
MyThread t=new MyThread();
t.start();
try{
Thread.sleep(100);
}catch(Exception e){
e.printStackTrace();
}
t.exit();
}
}


5.多线程问题--资决方案,这就是 synchronized
* 每一个对象都有一把锁,是对象生来就有的,当调用 synchronized 方法时,这个对象就被锁住,方法执行结束后自动解锁
* 在解锁之前其他线程不可以调用被锁对象的任何 synchronized 方法,因为所有 synchronized 方法都共享一个锁,这些线程会到该对象的锁池中等待
例子:
import java.util.*;
public class BankAccount{
private int[] account;
//构造10个帐户
public BankAccount(int balance){
account=new int[10];
for(int i=0;i<account.length;i++){
account<i>=balance;
}
}
//转帐过程是一个原子操作
public synchronized void transfer(int from,int to){
account[from]-=10;
account[to]+=10;
}
//核对所有帐户的金额总和
public void check(){
int sum=0;
for(int i=0;i<account.length;i++)
sum+=account<i>;
System.out.println(
"the sum of account is "+sum);
}
}

class Teller extends Thread{
BankAccount ba;
boolean flag=true;
public Teller(BankAccount ba){
this.ba=ba;
}
public void run(){
int from=(int)(Math.random()*10);
int to=(int)(Math.random()*10);

ba.transfer(from,to);
System.out.println(
"I am "+getName()+", I transfered from ["+from+"] to ["+to+"]");
}

}

class BankAccountTest{
public static void main(String args[]){
System.out.println(
"--------main begin---------");

BankAccount ba=new BankAccount(10000);
Teller teller;
//核对初始金额
ba.check();
//创建10个工作人员线程
for(int i=0;i<10;i++){
teller=new Teller(ba);
try{
teller.join();
}catch(Exception e){}
teller.start();
}
//核对转帐后金额
ba.check();
System.out.println(
"---------return from main-------");
}
}


6.线程间的协作
* 要让线程协同工作,关键是让线程能相互协商,这个任务是 Object 的 wait() 和 notify() 完成的
* wait()的作用是让线程等待
* notify()的作用是唤醒等待同一个监视器的线程
* wait() 和 sleep() 都是等待,他们有什么区别呢?
a.线程sleep()的时候并不释放对象的锁,而wait()的时候释放对象的锁,也就是线程wait()期间别的线程可以调用对象的synchronized方法,
当线程调用了某个对象的wait()后他就终止运行并释放那个对象的锁
b.sleep()属于Thread,而wait()和notify()属于Object
* 我们只能在 synchronized 段中调用 wait() 和 notify(),如果你不这么做也是可以编译的,但运行是会抛出异常,
因为调用这两个方法是必须先拥有这个对象的锁
* 线程协作的典型例子:生产者和消费者
class Holder{
private int sharedInt;
//缓冲区中的信息
private boolean writable=true;
//生产者的控制信号

public synchronized void setSharedInt(int val){
//如果不能生产就等待
while(!writable){
try{
wait();
}catch(Exception e){
e.printStackTrace();
}
}
sharedInt=val;
//生产后通知消费者
writable=false;
notify();
}
public synchronized int getSharedInt(){

while(writable){
try{
wait();
}catch(Exception e){
e.printStackTrace();
}
}
writable=true;
notify();
return sharedInt;
}
}
class Producer extends Thread{
private Holder pholder;
public Producer(Holder h){
pholder=h;
}
public void run(){
//生产10个
for(int i=0;i<10;i++){
pholder.setSharedInt(i);
System.out.println(
"Producer "+getName()+" set sharedInt to "+i);

try{
sleep(1000);
}catch(Exception e){
e.printStackTrace();
}
}
}
}
class Consumer extends Thread{
private Holder cholder;
public Consumer(Holder h){
cholder=h;
}
public void run(){
int val;
val=cholder.getSharedInt();
System.out.println(
"Consumer "+getName()+" retrieved "+val);
//消费10个后停止
while(val!=9){
try{
sleep(1000);
}catch(Exception e){
e.printStackTrace();
}
val=cholder.getSharedInt();
System.out.println(
"Consumer "+getName()+" retrieved "+val);
}
}
}
public class SharedCell{
public static void main(String args[]){
Holder holder=new Holder();
Producer p=new Producer(holder);
Consumer c=new Consumer(holder);
p.start();
c.start();
}
}


7.守护(daemon)线程
* 在客户/服务器模式下,服务器的作用是等待用户发来请求,并按请求完成客户的工作
* 守护线程是为其他线程提供服务的线程
* 守护线程一般应是一个独立线程,他的 run() 是一个无限循环
* 守护线程与其他线程的区别是 如果守护线程是唯一运行着的线程程序会自动退出

8. 小结
a. 线程的概念和与进程的区别
b. 创建线程的两种方式:继承 Thread 和 实现 Runnable 接口
c. 常用方法: sleep alive join suspend resume yield
d. 终止线程的常用方法:设置标志变量
e. 资源协调 synchronized()
f. 线程间的协作 wait() notify()/notifyAll()
e. daemon线程

线程的好处是他是“轻型”的,合理应用会提高系统的利用率
难点是多线程会共享同一资源,就要确保不会有两个或两个以上的线程同时访问那项资源,需要合理的使用 synchronized
irini   2005-12-11 23:17:38 评论:0   阅读:369   引用:0

发表评论>>

署名发表(评论可管理,不必输入下面的姓名)

姓名:

主题:

内容: 最少15个,最长1000个字符

验证码: (如不清楚,请刷新)

Copyright@2008 powered by YuLog