Java线程的同步

一、线程的同步

这里写图片描述

  • 对共享对象的访问必须同步,叫做条件变量.
  • Java语言允许通过监视器(有的参考书称其为管程)使用条件变量实现线程同步.
  • 监视器阻止两个线程同时访问同一个条件变量.它的如同锁一样作用在数据上.
  • 线程1进入withdrawal方法时,获得监视器(加锁);当线程1的方法执行完毕返回时,释放监视器(开锁),线程2的withdrawal方能进入.
    这里写图片描述

二、多线程问题—资源协调

  • 用synchronized来标识的区域或方法即为监视器监视的部分。
  • 一个类或一个对象有一个监视器,如果一个程序内有两个方法使用synchronized标志,则他们在一个监视器管理之下.
    这里写图片描述

  • 般情况下,只在方法的层次上使用关键区

class Account
{ 
   static int balance=1000; 
   static int expense=0;
   public synchronized void withdrawl(int amount)
   { if (amount<=balance)
      {    balance-=amount;
            expense+=amount;}
      else 
     {  System.out.println(“bounced: “+amount);}
    }
}

三、死锁问题
这里写图片描述

  • 如果你的持有一个锁并试图获取另一个锁时,就有死锁的危险.
  • 死锁可以通过以下方法来避免
    -决定获取锁的次序
    -始终遵照这个次序
    -按照相反的次序释放锁

四、生产者和消费者问题

  • 生产者和消费者模型是典型的线程同步问题,下面我们通过这个模型来说明线程同步的处理方法
  • 使用某种资源的线程称为消费者,产生或释放这个资源的线程称为生产者。生产者生成10个整数(0~9),存储到一个共享对象中,并把它们打印出来。每生成一个数就随机休眠0~100毫秒,然后重复这个过程。一旦这10个数可以从共享对象中得到,消费者将尽可能快地消费这10个数,即把它们取出后打印出来。

    (1)生产者程序

public class Producer extends Thread {
    private Share shared;
    private int number;

    public Producer(Share s, int number) {
    shared=s;
    this.number=number;
    }
    public void run( ) {
    for (int i=0; i<10; i++) {
        shared.put(i);
        System.out.println(″生产者″+this.number+″输出的数据为:   ″+i);
        try {
            sleep((int)(Math.random( ) * 1000));
        } catch (InterruptedException e) {}
         }
    }
}
(2) 消费者程序
public class Consumer extends Thread {
    private Share shared;
    private int number;

    public Consumer(Share s, int number) {
        shared=s;
        this.number=number;
    }
    public void run( ) {
        int value = 0;
        for (int i=0; i<10; i++) {
            value=shared.get( );
            System.out.println(″消费者″+this.number+″得到的数据为 :      ″+value);
        }
    }
}
 (3) 共享资源对象
public class Share {
    private int contents;
    public int get( ){
         return contents;
    }
    public void put(int value){
         contents=value;
    } 
}
(4) 主程序
public class PCTest {
    public static void main(String[] args) {
        Share s=new Share( );
        Producer p=new Producer(s,1);
        Consumer c=new Consumer(s,1);
        p.start( );
        c.start( );
    }
}

运行结果如下图所示
这里写图片描述

  • 我们分析一下可能发生的情况:一种情况是生产者比消费者速度快,那么在消费者还没有取出上一个数据之前,生产者又存入了新数据,于是,消费者很可能会跳过上一个数据。另一种情况则相反,当消费者比生产者速度快,消费者可能两次取出同一个数据。
  • 这两种情况不是我们所希望的。我们希望生产者存入一个数,消费者取出的就是这个数。为了避免上述情况发生,就必须锁定生产者线程,当它向共享对象中存储数据时禁止消费者线程从中取出数据,反之也一样。将共享对象Share中的put和get分别定义为同步化方法就可达到这个目的。
public class Share {
    private int contents;
    private boolean available=false;

    public synchronized int get( ) {
        while (available==false) {
            try {     
                wait( );
            } catch (InterruptedException e) { }
        }
        available=false;
        notifyAll( );
        return contents;
    }
    public synchronized void put(int value) {
        while (available==true) {
            try {
                wait( );
            } catch (InterruptedException e) { }
         }
        contents=value;
        available=true;
        notifyAll( );
    }
}
  • 修改后的Share仍利用put和get方法来写入和读取数据,但增加了wait和notifyAll功能。wait使线程进入短暂休眠,收到notifyAll的通知后会马上醒来。当消费者线程调用共享对象的get方法时,如果生产者没有写入数据,available变量就会保持为假,线程进入循环并调用wait方法等待。一旦生产者写入了新数据,available的值就会改变,同时生产者还会向消费者发出通知,唤醒消费者线程退出循环。此时,消费者线程将做两个非常重要的工作,一是把available变量改为假,二是通知生产者线程。最后,返回contents,它包含最新写入的数据。
  • 当生产者线程第一次调用共享对象的put方法时,available变量为假,线程将跳过循环并将第一个数据写入contents变量,然后将available变量改为真值,调用notifyAll方法通知消费者线程可以取数据了。再次调用put方法时,如果消费者没有取走数据,available变量就会保持为真,线程将进入循环并调用wait方法等待。一旦消费者取走上一个数据,available的值就会改变,线程也会被唤醒并退出循环,继续后面的工作。采用这样的处理方式,就可以保证消费者一直等到生产者写入一个新数据后再把它取出,而生产者则一直等到消费者取走上一个数据后再写入新数据。

    五、多线程问题

    对多线程程序本身来说,它会对系统产生以下影响:
    1.线程需要占用内存。
    2.线程过多,会消耗大量CPU时间来跟踪线程。
    3.必须考虑多线程同时访问共享资源的问题,如果没有协调好,就会产生令人意想不到的问题,例如可怕的死锁和资源竞争。
    4.因为同一个任务的所有线程都共享相同的地址空间,并共享任务的全局变量,所以程序也必须考虑多线程同时访问全局变量的问题。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值