`
风在你的眼里
  • 浏览: 7098 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

线程 进程

    博客分类:
  • java
 
阅读更多

一、进程与线程

进程(process)是一个可并发执行的具有独立功能的程序(program)关于某个数据集合的一次执行过程,也是操作系统进行资源分配和保护的基本单位。

线程(thread)是操作系统进程中能够独立执行的实体(控制流),是处理器调度和分派的基本单位。

比如word进程中有拼写检查、语法检查很多线程。

二、Java的线程实现

        在Java中如果要想进行多线程代码的实现有两种方式:

                  ·继承Thread类

                  ·实现Runnable接口

1.1、继承Thread类

Thread类声明如下

  1. public class Thread extends Object implements Runnable  
  2. {  
  3.     public Thread()                             //构造方法  
  4.     public Thread(String name)                  //name指定线程名  
  5.     public Thread(Runnable target)         //target指定线程的目标对象  
  6.     public Thread(Runnable target, String name)  
  7.     public void run()                           //描述线程操作的线程体  
  8.     public final String getName()               //返回线程名  
  9.     public final void setName(String name)     //设置线程名  
  10.     public static int activeCount()              //返回当前活动线程个数  
  11.     public static Thread currentThread()   //返回当前执行线程对象  
  12.     public Sting toString()             //返回线程的字符串信息,包括名字、优先级和线程组  
  13.     public synchronized void start()           //启动已创建的线程对象  
  14. }  

当一个类需要按照多线程的方式处理时,可以让这个类直接继承自Thread类即可,而且继承的时候要覆写好Thread类中提供的run()方法:

  1. //声明自定义线程类继承Thread类实现奇数/偶数序列线程并发执行。  
  2.   
  3. public class NumberThread extends Thread {  
  4.     private int k; // 序列初始值  
  5.   
  6.     public NumberThread(String name, int k) {  
  7.         super(name);  
  8.         this.k = k;  
  9.     }  
  10.   
  11.     public NumberThread(String name) {  
  12.         this(name, 0);  
  13.     }  
  14.   
  15.     public void run() // 覆盖run方法的线程体  
  16.     {  
  17.         int i = k;  
  18.         System.out.print("\n" + this.getName() + ":  ");  
  19.         while (i < 50) {  
  20.             System.out.print(i + "  ");  
  21.             i += 2;  
  22.         }  
  23.         System.out.println(this.getName() + "结束!");  
  24.     }  
  25.   
  26.     public static void main(String args[]) {  
  27.         NumberThread thread_odd = new NumberThread("奇数线程"1); // 创建线程对象  
  28.         NumberThread thread_even = new NumberThread("偶数线程"2);  
  29.         // thread_odd.setPriority(10); //设置优先级为最高  
  30.   
  31.         thread_odd.start(); // 启动线程对象  
  32.         thread_even.start();  
  33.        // 获得当前线程对象名  
  34.         System.out.println("currentThread=" + Thread.currentThread().getName()); 
  35.        
  36.         System.out.println("activeCount=" + thread_even.activeCount());  
  37.         System.out.println("main Priority="  
  38.                 + Thread.currentThread().getPriority()); // 获得当前线程对象的优先级  
  39.     }  
  40. }  

启动一个线程并不是依靠run()方法而是start()方法。此时通过start()方法执行线程的操作,操作中可以发现,每一个线程间都属于交替的运行状态,即:所有的线程都是交替运行的,且:那个线程抢到了CPU资源,那个线程就执行。

1.2实现Runnable接口

Runnable接口定义如下:

  1. public interfaceRunnable{  
  2.   
  3.          public void run() ;  
  4.   
  5. }  

线程实现的第二种手段,实现Runnable接口来实现线程的操作类,   

  1. //声明自定义线程类实现Runnable接口实现奇数/偶数序列线程并发执行。  
  2.   
  3. public class NumberRunnable implements Runnable {  
  4.     private int k;  
  5.   
  6.     public NumberRunnable(int k) {  
  7.         this.k = k;  
  8.     }  
  9.   
  10.     public NumberRunnable() {  
  11.         this(0);  
  12.     }  
  13.   
  14.     public void run() {  
  15.         int i = k;  
  16.         System.out.println();  
  17.         while (i < 50) {  
  18.             System.out.print(i + "  ");  
  19.             i += 2;  
  20.         }  
  21.         System.out.println("结束!");  
  22.     }  
  23.   
  24.     public static void main(String args[]) {  
  25.         NumberRunnable odd = new NumberRunnable(1); // 创建具有线程体的目标对象  
  26.         Thread thread_odd = new Thread(odd, "奇数线程"); // 以目标对象创建线程对象  
  27.         thread_odd.start();  
  28.   
  29.         new Thread(new NumberRunnable(2), "偶数线程").start();  
  30.          
  31.        // 获得当前线程对象名  
  32.         System.out.println("currentThread=" + Thread.currentThread().getName());
  33.         System.out.println("activeCount=" + Thread.activeCount());  
  34.     }  
  35. }  

      线程确实已经实现了,但是需要注意的是,如果要想启动一个线程肯定是Thread类中的start()方法完成,观察Thread类中提供的构造方法:public Thread(Runnable target)通过构造发现,Thread类可以接收Runnable子类的对象,所以一切的线程都可以通过Thread类进行启动。此时,通过Thread类进行了线程的启动。

1.3、两种实现方式的区别

        对于Thread类和Runnable接口本身都是可以进行多线程的实现,那么两者到底该使用谁更好呢?

              1  继承局限:使用Runnable接口可以避免单继承的局限,而Thread类则有此局限;

              2  资源共享:使用Runnable接口实现多线程,可以实现资源(对象属性)的共享,而Thread类却无法实现。

                            |-此点只是相对而言,因为两者的此种区别是有其应用范围的。

范例:观察资源共享

  1. class MyThreadextends Thread {  
  2.   
  3.          private int count = 5 ;  
  4.   
  5.          public void run(){  
  6.   
  7.                    for(int x=0;x<50;x++){  
  8.   
  9.                             if(this.count>0){  
  10.   
  11.                                      System.out.println("count= " + this.count--) ;  
  12.   
  13.                             }  
  14.   
  15.                    }  
  16.   
  17.          }  
  18.   
  19. };  
  20.   
  21. public classThreadDemo03 {  
  22.   
  23.          public static void main(String args[]){  
  24.   
  25.                    new MyThread().start() ;  
  26.   
  27.                    new MyThread().start() ;  
  28.   
  29.                    new MyThread().start() ;  
  30.   
  31.          }  
  32.   
  33. };  

现在的程序中每一个线程都各自占有各自的count属性,所以并没有达到资源共享的目的,如果换成Runnable呢?

  1. class MyThread implements Runnable {  
  2.   
  3.          private int count = 5 ;  
  4.   
  5.          public void run(){  
  6.   
  7.                    for(int x=0;x<50;x++){  
  8.   
  9.                             if(this.count>0){  
  10.   
  11.                                      System.out.println("count= " + this.count--) ;  
  12.   
  13.                             }  
  14.   
  15.                    }  
  16.   
  17.          }  
  18.   
  19. };  
  20.   
  21. public classThreadDemo04 {  
  22.   
  23.          public static void main(String args[]){  
  24.   
  25.                    MyThreadmt = new MyThread() ;  
  26.   
  27.                    new Thread(mt).start() ;  
  28.   
  29.                    new Thread(mt).start() ;  
  30.   
  31.                    new Thread(mt).start() ;  
  32.   
  33.          }  
  34.   
  35. };  

现在的代码中可以发现,count属性已经被所有的线程对象所共同拥有了。

  1. class MyThread implements Runnable {  
  2.   
  3.          private int count = 5 ;  
  4.   
  5.          public void run(){  
  6.   
  7.                    for(int x=0;x<50;x++){  
  8.   
  9.                             if(this.count>0){  
  10.   
  11.                                      System.out.println("count= " + this.count--) ;  
  12.   
  13.                             }  
  14.   
  15.                    }  
  16.   
  17.          }  
  18.   
  19. };  
  20.   
  21. public classThreadDemo04 {  
  22.   
  23.          public static void main(String args[]){  
  24.   
  25.                    MyThreadmt1= new MyThread() ;  
  26.   
  27.              MyThread mt2 = new MyThread() ;  
  28.   
  29.              MyThread mt3 = new MyThread() ;  
  30.   
  31.                    new Thread(mt1).start() ;  
  32.   
  33.                    new Thread(mt2).start() ;  
  34.   
  35.                    new Thread(mt3).start() ;  
  36.   
  37.          }  
  38.   
  39. };  

现在的程序中每一个线程都各自占有各自的count属性,

1.4、两种实现方式的联系

 Thread类和Runnable接口中都可以发现,都必须同时覆写run()方法,两者的关系如何呢?观察Thread类的定义:

public class Thread extends Object implements Runnable

其中有个Runable类型的变量

  1. /* What will be run. */  
  2.     private Runnable target;  

其中的run方法定义如下:

  1.  public void run() // 描述线程操作的线程体  
  2.  {  
  3.   if (target != null)  
  4.    target.run(); // 执行目标对象的run()方法}  
  5.  }  

发现Thread类实际上是Runnable的子类。而且Thread类也要去接收Runnable其他子类的对象,而且所有的线程中,通过Runnable接口实现的线程类里面都是编写的具体功能,而并没有所谓的CPU调度,而真正意义上的CPU调度由操作系统完成(通过Thread类的start()方法调用的)

Thread类要去协调操作系统,并且最终还要执行具体的线程主体的方法,而线程的主体呢,现在只专著于具体的功能实现,至于如何调度根本不管。Thread代理自定义的线程类的对象,如图所示:

从图的关系上可以清楚的发现,现在在线程中应用的设计思路就是代理设计模式。

三、线程的状态

 

1、当执行new Thread(Runnable r)后,新创建出来的线程处于new状态,这种线程不可能执行

2、当执行thread.start()后,线程处于runnable状态,这种情况下只要得到CPU,就可以开始执行了。runnable状态的线程,会接受JVM的调度,进入running状态,但是具体何时会进入这个状态,是随机不可知的

3、running状态中的线程最为复杂,可能会进入runnable、waiting、timed_waiting、blocked、dead状态
如果CPU调度给了别的线程,或者执行了Thread.yield()方法,则进入runnable状态,但是也有可能立刻又进入running状态
如果执行了Thread.sleep(long),或者thread.join(long),或者在锁对象上调用object.wait(long)方法,则会进入timed_waiting状态
如果执行了thread.join(),或者在锁对象上调用了object.wait()方法,则会进入waiting状态 
如果进入了同步方法或者同步代码块,没有获取锁对象的话,则会进入blocked状态

4、处于waiting状态中的线程,如果是因为thread.join()方法进入等待的话,在目标thread执行完毕之后,会回到runnable状态;如果是因为object.wait()方法进入等待的话,在锁对象执行object.notify()或者object.notifyAll()之后会回到runnable状态

处于timed_waiting状态中的线程,和waiting状态中的差不多,只不过是设定时间到了,就会回到runnable状态

5、处于blocked状态中的线程,只有获取了锁之后,才会脱离阻塞状态

6、当线程执行完毕,或者抛出了未捕获的异常之后,会进入dead状态,该线程结束

四、线程的操作方法

4.1、命名和取得

        每一个线程实际上都可以为其设置名字,而且也可以取得每一个线程的名字:

                  ·设置线程名称:public final void setName(String name)

                  ·取得线程名称:public final String getName()

                  ·取得当前线程:public static Thread currentThread()

                    返回当前活动线程个数 public static int activeCount()

       除了以上的设置名称的方法外,在Thread类中也提供了两个构造方法:

                  ·public Thread(String name)

                  ·public Thread(Runnable target,String name)

 每次java运行的时候,实际上都会启动一个JVM的进程。那么既然是多线程的处理机制,实际上方法是在一个JVM上产生的一个线程而已

4.2、线程的休眠

所谓的休眠就是指减缓程序的运行速度,如果要休眠使用如下的方法:

          ·休眠:public static void sleep(long millis) throwsInterruptedException,指定休眠时间

比如Thread.sleep(1000),当前线程睡眠1秒。需要知道的是,1秒后,线程是回到可执行状态,并不是执行状态,什么时候执行那是由虚拟机来决定的。所以sleep(1000)并不是在睡眠1秒后立即执行。

4.3、线程的优先级

        实际上所有的线程启动之后并不是立刻运行的,都需要等待CPU进行调度,但是调度的时候本身也是存在“优先”级的,如果优先级高则有可能最先被执行。

        如果要想设置优先级可以使用:publicfinal void setPriority(int newPriority)

        这个优先级需要接收一个整型的数字,这个数字只能设置三个内容:

                  ·最高优先级:public static final int MAX_PRIORITY        1

                  ·中等优先级:public static final int NORM_PRIORITY       5

                  ·最低优先级:public static final int MIN_PRIORITY        10

问题:主方法的优先级是什么?

  1. public classMainDemo {  
  2.   
  3.          public static void main(String args[]){  
  4.   
  5.                    System.out.println(Thread.currentThread().getPriority());   //5  
  6.   
  7.                    System.out.println("MAX_PRIORITY" + Thread.MAX_PRIORITY) ;//10  
  8.   
  9.                    System.out.println("MIN_PRIORITY" + Thread.MIN_PRIORITY) ;//1  
  10.   
  11.                    System.out.println("NORM_PRIORITY" + Thread.NORM_PRIORITY) ;//5  
  12.   
  13.          }  
  14.   
  15. };  

主方法属于中等优先级。

4.4、  join方法

线程有join方法,当线程1调用线程2的join方法时,线程1必须等待线程2执行完毕,线程1才能继续往下执行。join方法主要用来将大问题分解成小问题,当小问题计算完成时,大问题才能继续往下执行,这时候我们就可以利用join方法了。下面演示了一下线程的join方法。

  1. public class TestJoin {  
  2.   
  3.   
  4.     public static void main(String[] args) {  
  5.           
  6.         for(int i =0;i<10;i++)  
  7.         {  
  8.             System.out.println(Thread.currentThread().getName()+"  "+i);  
  9.             if(i==2)  
  10.             {  
  11.                 JoinThread jt=new JoinThread();  
  12.                 jt.start();  
  13.                 //主线程调用jt的join方法,主线程必须等待jt线程执行完才能继续执行  
  14.                 try {  
  15.                     jt.join();  
  16.                 } catch (InterruptedException e) {  
  17.                     e.printStackTrace();  
  18.                 }  
  19.             }  
  20.         }  
  21.   
  22.     }  
  23.   
  24. }  
  25.   
  26. class  JoinThread extends Thread  
  27. {  
  28.   
  29.     @Override  
  30.     public void run() {  
  31.        for(int i = 0;i<10;i++)  
  32.        {  
  33.            System.out.println(this.getName()+"  "+i);  
  34.        }  
  35.     }  
  36.        
  37. }  

运行结果如下:

main  0
main  1
main  2
Thread-0  0
Thread-0  1
Thread-0  2
Thread-0  3
Thread-0  4
Thread-0  5
Thread-0  6
Thread-0  7
Thread-0  8
Thread-0  9
main  3
main  4
main  5
main  6
main  7
main  8
main  9

4.5、yield方法
  

暂停当前正在执行的线程对象。
yield()只是使当前线程重新回到可执行状态,所以执行yield()的线程有可能在进入到可执行状态后马上又被执行。yield()只能使同优先级或更高优先级的线程有执行的机会。

4.6、interrupt方法

interrupt()只是为线程设置一个中断标记,一个线程对象被设置了中断标记只有仍然可以运行,isAlive()返回true。实例方法isInterrupted()测试线程对象中的中断标记,并不清除标记。静态的interrupted()方法会测试当前执行的线程是否被中断,并且在肯定的情况下,清除当前对象中断标记,并返回true。

interrupt()不会中断一个正在运行的线程.就是指线程如果正在运行的过程中, 去调用此方法是没有任何反应的.为什么呢, 因为这个方法只是提供给被阻塞的线程, 即当线程调用了Object.wait, Thread.join, Thread.sleep三种方法之一的时候, 再调用interrupt方法, 才可以中断刚才的阻塞而继续去执行线程.

  1. class ThreadDemo134 extends Thread {  
  2.     int count = 0;  
  3.   
  4.     public void run() {  
  5.         System.out.println(getName() + " 将要运行...");  
  6.         while (!this.isInterrupted()) {  
  7.             System.out.println(getName() + " 运行中 " + count++);  
  8.             try {  
  9.                 Thread.sleep(400); // 休眠400毫秒  
  10.             } catch (InterruptedException e) { // 退出阻塞态时将捕获异常  
  11.                 System.out.println(getName() + "从阻塞态中退出...");  
  12.                 this.interrupt(); // 改变线程状态,使循环结束  
  13.             }  
  14.         }  
  15.         System.out.println(getName() + " 已经终止!");  
  16.     }  
  17.   
  18.     public static void main(String argv[]) throws InterruptedException {  
  19.         ThreadDemo134 ta = new ThreadDemo134();  
  20.         ta.setName("ThreadA");  
  21.         ta.start();  
  22.         Thread.sleep(2000);// 主线程休眠2000毫秒,等待其他线程执行  
  23.           // 2000毫秒过去后,main拿到cpu控制权。  
  24.         System.out.println(ta.getName() + " 正在被中断.....");
  25.         ta.interrupt(); // 中断线程ThreadA  
  26.     }  
  27. }  

运行结果:

ThreadA 将要运行...
ThreadA 运行中 0
ThreadA 运行中 1
ThreadA 运行中 2
ThreadA 运行中 3
ThreadA 运行中 4
ThreadA 正在被中断.....
ThreadA从阻塞态中退出...
ThreadA 已经终止!

五、线程的竞争关系与线程互斥

并发执行的交互线程间存在与时间有关的错误
  1. // 银行账户的存取款线程设计。  
  2.   
  3. public class Account // 账户类  
  4. {  
  5.     private String name; // 储户姓名  
  6.     private double balance; // 账户余额  
  7.   
  8.     public Account(String name) {  
  9.         this.name = name;  
  10.         this.balance = 0;  
  11.     }  
  12.   
  13.     public String getName() // 返回账户名  
  14.     {  
  15.         return name;  
  16.     }  
  17.   
  18.     public double balance() // 查看账户余额  
  19.     {  
  20.         return balance;  
  21.     }  
  22.   
  23.     public void put(double value) // 存款操作,参数为存入金额  
  24.     {  
  25.         if (value > 0)  
  26.             this.balance += value; // 存款操作使余额值增加  
  27.     }  
  28.   
  29.     public double get(double value) // 取款操作,参数为取款金额,返回实际取到金额  
  30.     {  
  31.         if (value > 0) {  
  32.             if (value <= this.balance)  
  33.                 this.balance -= value; // 取款操作使余额值减少  
  34.             else // 账户余额不够所取时  
  35.             {  
  36.                 value = this.balance; // 取走全部余额  
  37.                 this.balance = 0;  
  38.             }  
  39.             return value; // 返回实际取款额  
  40.         }  
  41.         return 0;  
  42.     }  
  43. }  
  44.   
  45. class Save extends Thread // 存款线程类  
  46. {  
  47.     private Account account; // 账户  
  48.     private double value; // 存款金额  
  49.   
  50.     public Save(Account a1, double value) {  
  51.         this.account = a1;  
  52.         this.value = value;  
  53.     }  
  54.   
  55.     public void run() {  
  56.         double howmatch = this.account.balance();// 查看账户余额  
  57.   
  58.         try {  
  59.             sleep(1); // 花费时间,线程执行被打断  
  60.         } catch (InterruptedException e) {  
  61.         }  
  62.   
  63.         this.account.put(this.value);  
  64.         System.out.println(this.account.getName() + "账户:现有" + howmatch + ", 存入"  
  65.                 + this.value + ", 余额" + this.account.balance());  
  66.     }  
  67. }  
  68.   
  69. class Fetch extends Thread // 取款线程类  
  70. {  
  71.     private Account account; // 账户  
  72.     private double value; // 取款金额  
  73.   
  74.     public Fetch(Account a1, double value) {  
  75.         this.account = a1;  
  76.         this.value = value;  
  77.     }  
  78.   
  79.     public void run() {  
  80.         double howmatch = this.account.balance();// 查看账户余额  
  81.   
  82.         try {  
  83.             sleep(1); // 花费时间,线程执行被打断  
  84.         } catch (InterruptedException e) {  
  85.         }  
  86.   
  87.         System.out.println(this.account.getName() + "账户:现有" + howmatch + ", 取走"  
  88.                 + this.account.get(this.value) + ", 余额"  
  89.                 + this.account.balance());  
  90.     }  
  91.   
  92.     public static void main(String args[]) {  
  93.         Account wang = new Account("Wang");  
  94.         (new Save(wang, 100)).start();  
  95.         (new Save(wang, 200)).start();  
  96.         (new Fetch(wang, 300)).start();  
  97.   
  98.         (new Save(new Account("Li"), 100)).start();  
  99.     }  
  100.   
  101. }  

错误结果:

Wang账户:现有0.0, 取走300.0, 余额0.0
Wang账户:现有0.0, 存入100.0, 余额0.0
Wang账户:现有0.0, 存入200.0, 余额0.0
Li账户:现有0.0, 存入100.0, 余额100.0

5.1、线程间的竞争关系

如果两个线程要访问同一资源,则线程间存在资源竞争关系。一个线程通过操作系统得到该资源,另一个将得不到。这时候一个线程的执行可能影响到同其竞争资源的其他线程。在极端情况下,被阻塞的线程永远得不到访问权,从而不能成功终止。资源竞争会出现两个问题:

死锁(deadlock) :一组进程获得了部分资源,还想获得其他进程占用的资源,最终所有的进程陷入死锁。

饥饿(starvation):一个进程由于其他进程优先级总是优于它而被无限期拖延。

5.2、临界区管理

每个进程中访问临界资源的那段程序称为临界区(Critical Section)(临界资源是一次仅允许一个进程使用的共享资源)。每次只准许一个进程进入临界区,进入后不允许其他进程进入。不论是硬件临界资源,还是软件临界资源,多个进程必须互斥地对它进行访问。
  多个进程中涉及到同一个临界资源的临界区称为相关临界区。 
  进程进入临界区的调度原则是: ①如果有若干进程要求进入空闲的临界区,一次仅允许一个进程进入。②任何时候,处于临界区内的进程不可多于一个。如已有进程进入自己的临界区,则其它所有试图进入临界区的进程必须等待。③进入临界区的进程要在有限时间内退出,以便其它进程能及时进入自己的临界区。④如果进程不能进入自己的临界区,则应让出CPU,避免进程出现“忙等”现象。

5.3、Java线程互斥实现

Java采用synchronized关键字声明一段程序为临界区。synchronized有两种用法:同步语句(代码块)、同步方法。

1.同步语句

synchronized(对象)

   语句

2.同步方法

synchronized 方法声明

范例:使用同步代码块完成进程互斥

  1. //互斥的存取款线程设计。  
  2.   
  3. public class SaveLock extends Thread // 带互斥锁的存款线程类  
  4. {  
  5.     private Account account; // 账户  
  6.     private double value; // 存款金额  
  7.   
  8.     public SaveLock(Account a1, double value) {  
  9.         this.account = a1;  
  10.         this.value = value;  
  11.     }  
  12.   
  13.     public void run() {  
  14.         synchronized (this.account) // 声明临界区,锁定账户对象  
  15.         {  
  16.             double howmatch = this.account.balance();  
  17.             try {  
  18.                 sleep(1); // 花费时间  
  19.             } catch (InterruptedException e) {  
  20.             }  
  21.             account.put(this.value);  
  22.             System.out.println(this.account.getName() + "账户:现有" + howmatch  
  23.                     + ", 存入" + this.value + ", 余额" + this.account.balance());  
  24.         }  
  25.     }  
  26. }  
  27.   
  28. class FetchLock extends Thread // 带互斥锁的取款线程类  
  29. {  
  30.     private Account account;  
  31.     private double value;  
  32.   
  33.     public FetchLock(Account a1, double value) {  
  34.         this.account = a1;  
  35.         this.value = value;  
  36.     }  
  37.   
  38.     public void run() {  
  39.         synchronized (this.account) // 声明临界区,锁定账户对象  
  40.         {  
  41.             double howmatch = this.account.balance();  
  42.             try {  
  43.                 sleep(1); // 花费时间  
  44.             } catch (InterruptedException e) {  
  45.             }  
  46.             System.out.println(this.account.getName() + "账户:现有" + howmatch  
  47.                     + ", 取走" + this.account.get(this.value) + ", 余额"  
  48.                     + this.account.balance());  
  49.         }  
  50.     }  
  51.   
  52.     public static void main(String args[]) {  
  53.         Account wang = new Account("Wang");  
  54.         (new SaveLock(wang, 100)).start();  
  55.         (new SaveLock(wang, 200)).start();  
  56.         (new FetchLock(wang, 300)).start();  
  57.     }  
  58.   
  59. }  
  60.   
  61. /* 
  62.  * 程序运行结果如下: Wang账户:现有0.0, 存入100.0, 余额100.0 Wang账户:现有100.0, 存入200.0, 余额300.0 
  63.  * Wang账户:现有300.0, 取走300.0, 余额0.0 
  64.  */  

除了可以使用同步代码块之外还可以使用同步方法完成以上的操作。

5.4、一个方法的完整定义格式

         [public | protected | private ][static] [final] [synchronized]

        返回值类型方法名称(参数列表) [throws异常1,异常2,…]{

                   [return返回值 ;]

}

六、线程间的协作关系与线程同步

6.1、线程间的协作关系

发送线程与接收线程通过缓冲区实现数据传递

 

 

  1. // 发送线程与接收线程。  
  2.   
  3. public class Buffer // 缓冲区  
  4. {  
  5.     private int value; // 共享变量  
  6.   
  7.     public void put(int i) {  
  8.         value = i;  
  9.     }  
  10.   
  11.     public int get() {  
  12.         return value;  
  13.     }  
  14. }  
  15.   
  16. class Sender extends Thread // 发送线程类  
  17. {  
  18.     private Buffer buffer; // 用于交换数据的共享变量  
  19.   
  20.     public Sender(Buffer buffer) // 指定缓冲区  
  21.     {  
  22.         this.buffer = buffer;  
  23.     }  
  24.   
  25.     public void run() {  
  26.         for (int i = 1; i < 6; i++) // 连续向缓冲区发送若干数据  
  27.         {  
  28.             buffer.put(i);  
  29.             System.out.println("Sender  put : " + i);  
  30.   
  31.             try {  
  32.                 sleep(1);  
  33.             } catch (InterruptedException e) {  
  34.             }  
  35.         }  
  36.     }  
  37. }  
  38.   
  39. class Receiver extends Thread // 接收线程类  
  40. {  
  41.     private Buffer buffer;  
  42.   
  43.     public Receiver(Buffer buffer) // 指定缓冲区  
  44.     {  
  45.         this.buffer = buffer;  
  46.     }  
  47.   
  48.     public void run() {  
  49.         for (int i = 1; i < 6; i++) // 连续从缓冲区接收若干数据  
  50.         {  
  51.             System.out.println("\t\t\tReceiver get : " + buffer.get());  
  52.   
  53.             try {  
  54.                 sleep(1);  
  55.             } catch (InterruptedException e) {  
  56.             }  
  57.         }  
  58.     }  
  59.   
  60.     public static void main(String args[]) {  
  61.         Buffer buffer = new Buffer();  
  62.         (new Sender(buffer)).start();  
  63.         (new Receiver(buffer)).start();  
  64.     }  
  65.   
  66. }  
  67.   
  68. /* 
  69.  * 没有sleep(1)语句时,程序运行结果如下: Sender put : 1
  70.  Sender put : 2 Sender put : 3 Sender 
  71.  * put : 4 Sender put : 5 
  72.  *  
  73.  * Receiver get : 5 Receiver get : 5 Receiver get : 5 Receiver get : 5 Receiver 
  74.  * get : 5 
  75.  *  
  76.  *  
  77.  * 有sleep(1)方法时,每次运行结果不确定,交替执行,间隔输出,一种可能的运行结果如下: 
  78. Sender put : 1 Receiver get : 1 
  79.  * Sender put : 2 Receiver get : 2 Sender put : 3 Sender put : 4 Receiver get : 
  80.  * 4 Sender put : 5 Receiver get : 5 Receiver get : 5 
  81.  */  

 6.2、线程同步

线程同步是解决协作关系的手段。线程同步是指两个以上线程基于摸个条件来协调他们的活动。一个线程的执行依赖于另一个协作线程的消息或者信号,当一个线程没有得到另一个线程的消息或者信号时,这需要等待。知道消息或者信号到达才能被唤醒。可理解线程A和B一块配合,A执行到一定程度时要依靠B的某个结果,于是停下来,示意B运行;B依言执行,再将结果给A;A再继续操作。

6.3、java 线程通信方法

Object类对线程的支持:在Object类中提供了以下的方法可以实现对线程的等待及唤醒的处理:

                ·等待:public final void wait() throws InterruptedException

                ·等待:public final void wait(long timeout) throws InterruptedException

                ·唤醒:public final void notify(),唤醒第一个等待的线程

                ·唤醒:public final void notifyAll(),唤醒全部等待的线程

6.4、采用信号量和同步方法实现线程同步

  1. //采用信号量和同步方法使发送线程与接收线程同步运行。  
  2.   
  3. public class BufferLock // 加互斥锁的缓冲区  
  4. {  
  5.     private int value; // 共享变量  
  6.     private boolean isEmpty = true// value是否为空的信号量  
  7.   
  8.     public synchronized void put(int i) // 同步方法  
  9.     {  
  10.         while (!isEmpty) // 当value不空时,等待  
  11.         {  
  12.             try {  
  13.                 this.wait(); // 使调用该方法的当前线程等待,即阻塞自己  
  14.             } catch (InterruptedException e) {  
  15.             }  
  16.         }  
  17.   
  18.         value = i; // 当value空时,value获得值  
  19.         System.out.println("Sender  put : " + i);  
  20.         isEmpty = false// 设置value为不空状态  
  21.         notify(); // 唤醒其他等待线程  
  22.     }  
  23.   
  24.     public synchronized int get() // 同步方法  
  25.     {  
  26.         while (isEmpty) // 当value空时,等待  
  27.         {  
  28.             try {  
  29.                 this.wait();  
  30.             } catch (InterruptedException e) {  
  31.             }  
  32.         }  
  33.   
  34.         isEmpty = true// 设置value为空状态,并返回值  
  35.         System.out.println("\t\t\tReceiver get : " + value);  
  36.         notify();  
  37.         return value;  
  38.     }  
  39.   
  40.     public static void main(String args[]) {  
  41.         BufferLock buffer = new BufferLock();  
  42.         (new Sender(buffer)).start();  
  43.         (new Receiver(buffer)).start();  
  44.     }  
  45.   
  46. }  
  47.   
  48. class Sender extends Thread // 发送线程类  
  49. {  
  50.     private BufferLock buffer;  
  51.   
  52.     public Sender(BufferLock buffer) {  
  53.         this.buffer = buffer;  
  54.     }  
  55.   
  56.     public void run() {  
  57.         for (int i = 1; i < 6; i++) {  
  58.             buffer.put(i);  
  59.   
  60.         }  
  61.     }  
  62. }  
  63.   
  64. class Receiver extends Thread // 接收线程类  
  65. {  
  66.     private BufferLock buffer;  
  67.   
  68.     public Receiver(BufferLock buffer) {  
  69.         this.buffer = buffer;  
  70.     }  
  71.   
  72.     public void run() {  
  73.         for (int i = 1; i < 6; i++)  
  74.             buffer.get();  
  75.     }  
  76.   
  77. }  

运行结果:

Sender  put : 1
   Receiver get : 1
Sender  put : 2
   Receiver get : 2
Sender  put : 3
   Receiver get : 3
Sender  put : 4
   Receiver get : 4
Sender  put : 5
   Receiver get : 5

 

 

分享到:
评论

相关推荐

    C#线程进程操作

    在提供的资源中,"操作系统实验之线程进程.pdf"可能包含了更深入的实验分析和理论讲解,而"ExaProcess"可能是一个演示性的程序或实验代码,读者可以通过阅读这些资料进一步理解线程和进程的操作。 总结来说,线程和...

    线程进程互斥锁

    在计算机科学中,线程和...综上所述,线程进程互斥锁是并发编程中不可或缺的一部分,它确保了在多线程或多进程环境下,对共享资源的访问是有序且安全的。理解和熟练掌握互斥锁的使用,对于编写健壮的并发程序至关重要。

    线程进程查看器.rar_进程查看

    线程进程查看器是一款实用工具,它允许用户实时监控计算机上正在执行的进程,并深入了解这些进程内部的线程活动。这个程序对于系统管理员、开发者以及任何关心计算机性能和安全性的用户来说都十分有价值。通过这样的...

    对Solaris操作系统中多线程进程体系结构的研究

    ### 对Solaris操作系统中多线程进程体系结构的研究 #### 摘要 根据教科书定义,进程被视为资源管理的最小单位,而线程则是程序执行的基本单元。随着计算机技术的发展,操作系统的设计逐渐演进,引入了线程概念以更...

    线程进程安全概述.pptx

    【线程进程安全概述】 线程和进程是操作系统中并行执行任务的基本单元,它们各自有不同的特性和安全问题。理解并掌握线程和进程的概念及其安全性对于编写高效、可靠的多线程或多进程程序至关重要。 **进程与线程的...

    EnumProc.zip_enumesystem_线程 进程

    在IT领域,操作系统管理和调度着无数的进程与线程,以保证系统的高效运行。本文将深入探讨"EnumProc.zip_enumesystem_线程 进程"这个话题,它是一个关于枚举系统进程及其线程的小型示例。通过分析这个压缩包中的...

    线程进程安全PPT学习教案.pptx

    线程进程安全是计算机科学中的核心概念,尤其是在多任务并行处理和系统级编程中至关重要。线程和进程是操作系统管理资源和执行任务的基本单元。 **进程**是程序的执行实例,是操作系统分配资源的基本单位。每个进程...

    【嵌入式软件工程师面经】Linux系统编程(线程进程).pdf

    ### 嵌入式软件工程师面经:Linux系统编程(线程进程) #### 一、线程与进程的区别 1. **定义**: - **进程**:是系统中的一个可执行程序的一次运行实例,它是系统进行资源分配和调度的基本单位。 - **线程**:...

    多线程进程间交换数据使用的共享内存

    多线程进程间交换数据使用的共享内存多线程进程间交换数据使用的共享内存多线程进程间交换数据使用的共享内存多线程进程间交换数据使用的共享内存多线程进程间交换数据使用的共享内存

    进程和线程之间的关系

    ### 进程和线程之间的关系 在计算机科学领域中,进程和线程是两个非常重要的概念,它们之间既有联系又有区别。对于初学者来说,理解进程与线程的关系至关重要,因为这有助于深入掌握操作系统的基本原理及应用。下面...

    进程与线程的生动形象理解

    ### 进程与线程的生动形象理解 #### 一、引言 进程与线程作为操作系统中的核心概念,对于理解和开发高效的软件至关重要。本文旨在通过一个生动的类比来帮助读者更好地理解进程与线程的基本概念及其差异,并进一步...

    易语言多线程监控进程源码

    "易语言源码分享站"标签提示我们,这个压缩包可能包含了易语言编写的多线程进程监控程序的源代码。通过阅读和学习这些源代码,开发者可以了解到如何在易语言中实现多线程,以及如何获取和处理系统进程数据。content....

    服务器客户端-socket(进程线程)

    服务器客户端-socket(进程线程),包括套接字,多线程,多进程,单进程,并发,互斥锁,tcp/ip,udp等

    java线程进程锁资源

    Java线程和进程是计算机程序执行的两种基本方式,它们在多任务环境下尤为关键。Java作为一门多线程编程语言,提供了丰富的工具和机制来管理线程和进程,以实现高效的并发执行。 **进程与线程的区别** 1. **进程**:...

    Linux程序设计(包含线程 进程 网路编程)

    《Linux程序设计》一书深入探讨了在Linux操作系统环境下进行程序开发的核心概念和技术,包括线程、进程、线程同步、进程通信以及网络编程。这本书是针对程序员精心编写的,旨在帮助他们掌握Linux下的高级编程技能。 ...

    Windows存储管理实习.rar_线程 模拟_线程 进程

    在Windows操作系统中,管理和操作内存是一项关键任务,特别是在多线程和多进程环境中。本实践项目专注于使用Windows API函数创建一个包含两个线程的进程,一个用于模拟内存分配,另一个用于监控内存活动。同时,通过...

    linux 线程进程经典文章

    在Linux操作系统中,线程和进程是两个基本的执行单元,它们在系统设计中起着至关重要的作用。根据定义,进程是系统管理资源的基本单位,它拥有独立的内存空间和资源,而线程则是程序执行的最小单位,共享同一进程的...

    python线程进程.doc

    【Python线程与进程】 在计算机编程中,线程和进程是并发执行任务的基本单位。在Python中,理解和掌握线程和进程的概念及其使用对于编写高效、并行的程序至关重要。 **单线程** 在早期的操作系统中,如MS-DOS,...

    C++多线程编程实战 _C++_线程进程_thread_

    P303.zip

Global site tag (gtag.js) - Google Analytics