学习线程(Thread)有感

近段时间在学习线程.嗨,感觉好像学另外一门语言一样...

说过这个,我想起了一个小事:以前我和一个同学去办证时,谈起了一个银行的排队系统.那时我对他说:如果两个服务员同时按了开关那将怎样?同学说:没什么呀,这种情况属于毫秒级的呀...我反驳说:就算是这样也不等于不可能吧...

呵呵,那时没学线程这个东东,所以说起来就好像在问:如果地球是圆的话,为什么住在另一边的人不会掉下去一样令人含笑!

好吧,题外话少说了.开始吧!
一.Thread与cpu.
     1.通常所以说的同时执行并不是绝对的真正的同时执行程序.我们学OS时经常看到几个程序同时执行这样的字样.其实这是一种"宏观是同时,微观是分时"的.因为一般上书上所说的Thread时针对单cpu的,所以其实是按顺序执行执行每个线程的(但可能出现调用),除非是多cpu的机器.就算是单CPU的同时也是建立在这样一个理念的:一个线程没执行完毕,另一个线程可以开始执行.所以总的来说单位时间上CPU可以执行的线程数多了(效率高了),但亦可能对于每个线程来说从开始到结束比按顺序执行该线程代表的程序会花费更长时间.但要注意的是,总的CPU使用时间将减少.<理解这一点我觉得至关重要>

    2.由于上述原因,所以线程执行时可能出现高度. Java线程调度机制是:抢占式,其中又分为A.时间片,B.独占式.

二.线程与进程.
    1.一个进程可以包括若干个线程.其中每个线程共享进程中的所有资源,包括端口,内存.线程是程序执行的基本单位,线程/进程是个动态概念.比如:行驶中的列车是进程的话,那么该火车本身就是一个(静态)程序.可以说进程包括程序和运行在程序上的数据集.一个程序在一个数据集上的一次执行叫做一个"进程".

三.线程执行时间及顺序永远不知道.

四.Thread中的若干实用方法.
     1.yield() 这个方法就是让线程将停执行(究竟时间多长及是否真的被采纳不能承诺).它就让出CPU一段时间让其它线程执行.
    2.sleep() 这个方法有几种用法,可以含参数.它作用与yield()相似,也是将线程挂线.但这个可以被interrupt()唤醒,所以使用它要将其置于声明抛出InterruptedException中.
    3.wait()--notify()/notifyAll().其它这是一个同步机制.采用了握手方式,即只有双方同意才可以执行.如果先调用wait(),然后调用获得该对象锁的线程的notify()/notifyAll()就会唤醒正在等待该对象锁的线程.但要切记:被唤醒的线程将首先进入就绪状态(Runnable)
   4.join() 假如在线程A中,调用了B.join() 这个就是让调用线程B强行插入到线程A执行前列.使得线程B.执行总是先于线程A.(除非有切换状态).当B执行完毕,才会恢复A的执行.
   5.synchronized 块/方法.其实临界区和方法相当.只是它想把它作为一个普通程序块是就使用临界区方式,否则就是使用方法.但执行它们之前总是要获得对象的锁.否则有Thread not owner异常.

五.举例说明.
    1.notify()/notifyAll()

import java.util.ArrayList;
import java.util.Timer;
import java.util.TimerTask;

class Template extends Thread {
 static ArrayList list = new ArrayList();
 private static int base=0;
 private int numb=base++;
 private Notify nty;
 public Template() {
  super("template");
  start();
 }
 public void run() {
  synchronized(list) {
   try
   {
    list.wait();
   }
   catch (InterruptedException e)
   {
    e.printStackTrace();
   }
   System.out.println("it's "+numb);
  }
 }
};

class Notify extends Thread {
 private ArrayList list;
 public Notify(ArrayList list) {
  super("Notify");
  this.list=list;
  start();
 }
 public void run () {
  synchronized(list) {
   list.notifyAll();//notify() 
  }
 };
}

public class Test {
 public static void main(String[] args) {
  for (int i=0;i<5 ;i++ ) {
   Template temp=new Template();
  }
  Notify ntf=new Notify(Template.list);
  new Timer(true).schedule(new TimerTask() {
   public void run() {
    System.out.println("the end of this program.");
    System.exit(0);
   }
  },5000);
 }
};
    1.假如在一个生产者,一个消费者中,生产不能溢出消费者的缓冲区.即生产者的速度不能大于消费者的速度(否则可能覆盖数据),而消费者的速度也不能大于生产者的速度.否则就可能重复数据.代码如下:

import java.util.LinkedList;//实现了Queue.(j2sdk 1.5)
import java.util.Random;
import java.util.Timer;
import java.util.TimerTask;
//利用队列来实现.
class ResourceQueue {
 protected LinkedList list=new LinkedList();
 private boolean flag=true;
 public synchronized boolean put(Integer obj) {//由于这个put()与get()置于synchronized块中,所以可以去掉synchronized.
  while  (list.size()==2) {//assume the buffer'size is 2 so as to complete the full list checking easilyer.
   try
   {
    System.out.println(Thread.currentThread().getName()+": the list is full,waiting...");
    wait();
   }
   catch (InterruptedException e)
   {
    e.printStackTrace();
   }//while():防止其它线程由于:1.调用了noitfyAll();2.首先执行了put()操作.  使得当线程总以为可以执行!!相当用了while(conditionsIsNotMet) wait();
  }
         //*************
  list.offer(obj);//可以不顾get()而连续放元素.
  System.out.println(Thread.currentThread().getName()+" has put a object: "+obj);
  if(list.size()==1)////0-->>1****
       notify();
  return true;
 }

 public synchronized Object get() {
        Object obj;
  while(list.size()==0) {
  try
  {
   System.out.println(Thread.currentThread().getName()+": the list is empty,waiting...");
   wait();
  }
  catch (InterruptedException e)
  {
   e.printStackTrace();
  }
  }//return get();//轮询是否有元素可取。也就是递归,作用同put()中的一样.这里当put()放了若干元素时,可以连续执行而不用等待.
        //**************
  obj=list.poll();
  if (list.size()==1)/// 2-->>1***
   notify();
  System.out.println(Thread.currentThread().getName()+" has get the object: "+obj);
  return obj;
 }
};

class Producer extends Thread {
 private ResourceQueue res;
 private int i;
 private Random rand =new Random();
 private Integer rst;
 public Producer(ResourceQueue res) {
  super("Producer");
  this.res=res;
  start();
 }
 public void run() {
  while (true) {
   i=rand.nextInt(1000);
   rst=new Integer(i);
   synchronized(res) {
    res.put(rst);
   }
   try//put into above block and try ?del ?
    {
     sleep(i);
    }
   catch (InterruptedException e)
    {
     e.printStackTrace();
    }
  }
 }
};

class Consumer extends Thread {
 private ResourceQueue res;
 public Consumer(ResourceQueue res) {
  super("Consumer");
  this.res=res;
  start();
 }
 public void run () {
  while (true) {
   synchronized(res) {
    res.get();
   }
  }
 }
};

public class PCMain {
 public static void main(String[] args ) {
  final ResourceQueue rq=new ResourceQueue();
  Producer p=new Producer(rq);
  Consumer cus=new Consumer(rq);
  new Timer(true).schedule(new TimerTask() {//让程序只执行五秒
   public void run() {
    System.out.println("the remained number of the list: "+rq.list.size());//统计剩余元素.
    System.out.println("the end of this program");
    System.exit(0);
   }
  },5000);
 }
};
    2.假如在一个餐馆中:有若干个厨师,若干个服务员,若干个顾客(在一个时间段内).首先顾客点菜,服务员将订单送给厨师.等厨师做好菜肴后就将相应的订单交还相应的服务员,服务员则交还相应的顾客.代码如下:

//1.
import java.io.*;
import java.util.*;

public class Demo {
 static List menuList = new ArrayList(); //菜单列表(假设只有一个菜单簿)private
 private static List cookList = new ArrayList(); //厨师列表
 private static List waiterList = new ArrayList(); //服务员列表
 private static List customerList = new ArrayList();//消费者列表
 private static int menuID = 0; //菜单编号
 
 public static void main(String[] args) throws Exception {
  createCooks(3);
  createWaiters(3);
  createCustomers(3);
  startService();
  //getMenu();
 }

 //创建消费者列表
 private static void createCustomers(int num) {
  for (int i=1;i<=num ;i++ ) {
   customerList.add(new Customer("消费者-"+i));
  }
 }
 
 //创建指定个数的厨师
 private static void createCooks(int num) {
  for(int i = 1; i <= num; i++) {
   cookList.add(new Cook("厨师-" + i));////////
  }
 }
 
 //创建指定个数的服务员
 private static void createWaiters(int num) {
  for(int i = 1; i <= num; i++) {
   Waiter waiter = new Waiter("服务员-" + i);
   waiter.addServiceMenus(menuList);
   waiter.addServiceCook(cookList);
   waiterList.add(waiter);
  }
 }
 
 //开始服务
 private static void startService() {
  for(int i = 0; i < waiterList.size(); i++) {
   new Thread((Waiter)waiterList.get(i)).start();
  }
  new Thread(new MenuListener()).start();
 }
 
 //菜单列表观察线程
 private static class MenuListener implements Runnable {
  
  public void run() {
   while(true) {
    if(menuList.size() != 0) {//由于起初没元素,所以先由getMenu()先执行键盘输入.
     //如果有新菜单,就交给一个空闲的服务员...
     synchronized(menuList) {
      menuList.notify();
     }
    }
   }
  }
 }
 
 //从顾客那获得菜单
 /*private static void getMenu() throws Exception {
  System.out.println("请输入菜肴名称,中间用','号分隔,回车表示结束!");
  BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
  String line = null;
  Menu menu = null;
  StringTokenizer stk = null;
  while(true) {
   line = in.readLine();
   menu = new Menu(++menuID);
   stk = new StringTokenizer(line,",");
   while(stk.hasMoreTokens()) {
    menu.add(stk.nextToken());
   }
   menuList.add(menu);
   System.out.println("顾客填加了一份新菜单,菜单编号-" + menu.getID());
  }
 }*/
}

//2.
class Customer extends Thread {
 private String name;
 private Menu menu;
 private static int menuID=0;
 public Customer(String name) {
  this.name=name;
 }
 public void run() {
  System.out.println("请输入菜肴名称,中间用','号分隔,回车表示结束!");
  while (true) {
   synchronized(Demo.menuList) {
    menu=new Menu(++menuID);
    Demo.menuList.add(menu);
       System.out.println("顾客填加了一份新菜单,菜单编号-" + menu.getID());
   }
  }
 }
};

//3.
import java.util.*;

//菜单类
public class Menu {
 private int id; //菜单编号
 private List foodList = new ArrayList(); //表示一份菜单内的所有菜肴
 private int foodSum = 0; //菜肴总数
 
 public Menu(int id) {
  this.id = id;
 }
 
 public Menu(int id, List foodList) {
  this.id = id;
  this.foodList = foodList;
  updateFoodSum();
 }
 
 public int getID() {
  return id;
 }
 
 //为菜单填加一份菜肴
 public void add(String food) {
  foodList.add(food);
  updateFoodSum();
 }
 
 public String get(int index) {
  return (String)foodList.get(index);
 }
 
 public List getAll() {
  return foodList;
 }
 
 //获得菜肴总数
 public int getSum() {
  return foodSum;
 }
 
 //更新菜肴总数
 private void updateFoodSum() {
  foodSum = foodList.size();
 }
}

//4.

//菜肴类
public class Food {
 private String name;
 
 //创建菜肴时需指定名称
 public Food(String name) {
  this.name = name;
 }
 
 //获得菜肴名称
 public String getName() {
  return name;
 }
}

//5.
import java.util.*;

//厨师类
public class Cook {
 private String name; //厨师名称
 
 public Cook(String name) {
  this.name = name;
 }
 
 public String getName() {
  return name;
 }
 
 //提供菜单,并开始产生菜肴,然后返回所完成的菜肴
 public List cooking(Menu menu) {
  //为了测试用,让厨师烹饪时等待10秒
  try {
   System.out.println(getName() + "开始烹饪菜单(编号-" + menu.getID() + ")中的菜肴,需要等待10秒...");
   Thread.sleep(10000);
  }catch(Exception ex) {
   ex.printStackTrace();
  }
  List foodList = new ArrayList();
  for(int i = 0; i < menu.getSum(); i++) {///////
   String foodName = menu.get(i);
   //根据指定的菜肴名称,进行烹饪动作...
   //烹饪完成后,创建此菜肴...
   foodList.add(new Food(foodName));
  }
  return foodList;//作为一个订单(菜单)
 }
}

//6.
import java.util.*;

//服务员类
public class Waiter implements Runnable {
 private String name; //服务员名称
 private Menu menu;   //每一份菜单.
 private List foodList;//菜肴列表(相当一份菜单)
 private List menuList; //服务员所服务的菜单列表
 private List cookList; //服务员所服务的厨师列表
 
 public Waiter(String name) {
  this.name = name;
 }
 
 public String getName() {
  return name;
 }
 
 //顾客给服务员一张菜单
 public void addMenu(Menu menu) {
  this.menu = menu;
 }
 
 //服务员把菜单给厨师
 public Menu getMenu() {
  return menu;
 }
 
 //厨师给服务员完成的菜肴
 public void addFoods(List foodList) {
  this.foodList = foodList;
 }
 
 //服务员把菜肴给顾客
 public List getFoods() {
  return foodList;
 }
 
 //指定服务员所要服务的菜单对象列表
 public void addServiceMenus(List menuList) {
  this.menuList = menuList;
 }
 
 //指定服务员所要服务的厨师对象列表
 public void addServiceCook(List cookList) {
  this.cookList = cookList;
 }
 
 public void run() {
  while(true) {
   try {
    synchronized(menuList) {//就算menuList无元素,也可以获锁.当然不能是null(这个初始时已经指向了一个ArrayList对象,所以不为null)
     menuList.wait();
    Menu menu = (Menu)menuList.remove(0);//相当队列
    addMenu(menu);
    System.out.println(getName() + "获得了一份新菜单,菜单编号-" + menu.getID());
    }
   }catch(Exception ex) {
    ex.printStackTrace();
   }
   //synchronized(menuList) {//将两个同步作为一个?****ok
   //}
   Cook cook = null;
   synchronized(cookList) {
    cook = (Cook)cookList.remove(0);//相当队列
   }
   System.out.println(getName() + "把菜单(编号-" + getMenu().getID() + ")交给了" + cook.getName());
   foodList = cook.cooking(menu);////
   System.out.println(cook.getName() + "烹饪完成了菜单(编号-" + getMenu().getID() + ")中所有的菜肴");
   addFoods(foodList);
   System.out.println(getName() + "得到了" + cook.getName() + "烹饪完成的所有菜肴");
   getFoods(); //假设这里把菜肴交给了顾客,顾客调用了此服务员的此方法...
   System.out.println(getName() + "把所有菜肴交给了顾客");
   //当把菜肴交给了顾客以后,清空服务员所持有的该菜单和菜肴...
   menu = null;
   foodList = null;
   cookList.add(cook); //厨师继续回到等待队列中去...(循环添加)
  }
 }
}

总结:学习线程要有个概念.就是说不同的SDK或不同的版本其执行结果可能不一样.就算是同一机器同一SDK在不同的时刻运行也可能是这样.其实这是学习中的最大迷惑人之处.因为它可能看过来是正确的,所以我们可能看到时执行几次后就确定结果是那样,但可能再试我几次后就会大乎出人意料.

TIJ已经学习一段时间了,现在打算准备攻J2ME,不学J2EE的原因是A.本人对网络不是很熟,起码说连一个局域网都没接过.内部网也是.B.J2EE学的东西现在来说多得十个手指头都数不完.学起来时间长.唉,算了,就学J2ME吧,将可能的话再转J2EE咯.呵呵!!

阅读更多

没有更多推荐了,返回首页