A股上市公司传智教育(股票代码 003032)旗下技术交流社区北京昌平校区

 找回密码
 加入黑马

QQ登录

只需一步,快速开始

© 兰海忠 黑马帝   /  2011-8-1 23:04  /  2490 人查看  /  5 人回复  /   0 人收藏 转载请遵从CC协议 禁止商业使用本文

关于线程我学会的都是一些鸡毛蒜皮的内容——并发把所有的线程有start了就完了,可是线程的同步问题我是不太懂啊?什么生产--消费者问题我一块书里面的代码我就晕晕的都不知道说什么谁能更简介的说一下这个是什么实现的?

5 个回复

倒序浏览
黑马网友  发表于 2011-8-2 00:10:17
沙发
线程的同步是为了防止多个线程访问同一个数据时,对数据造成破坏,如果他们有共同的要操作的数据,则每个线程必须一个一个的执行这段操作公共数据的代码,前一个线程在执行完这段代码之前,不允许别的线程执行这段代码,否则就有可能出错,这是因为:CPU会分时的去执行每个线程,假如我们启动了2个线程,如果线程A正在执行操作数据的这段代码,CPU切换到了线程B,线程B执行后,CPU又切换到线程A断点的地方,如果这时执行的条件已不满足,线程A却不会再判断条件是否满足而是从断点处继续执行,结果就会出错。

生产者--消费者是线程通信的问题,对于消费者:相当于生产者将生产好的产品放到箱子里,在生产者将产品放到箱子里之前,不允许消费者去取,放入之后才通知消费者去取,对于生产者:只有消费者将箱子里的产品取出后,才通知生产者继续生产,否则不允许它再生产产品

评分

参与人数 1技术分 +2 收起 理由
admin + 2 有道理!

查看全部评分

回复 使用道具 举报
:) 楼上解释的很正确,也就是这么一个道理,比如我们平常生活中的一个赏识、比如你在银行有个账户,当然你的账户里面有钱你才能取出来钱,假如你的帐号有500块钱,有两条线程同时取钱。每条线程取500,那么假如其中一条线程在判断完账户余额是否大于取钱数时候暂停,切换到另外一条线程,那么这就有可能导致账户余额为负,这就是银行不希望看到的结果,那么我们就应该为这个取钱的操作进行同步,也就是说,不管任何时候只有一条线程进入这个取钱的操作代码快中

:) 比如有这么一个需求,加入现在系统中有两条线程,这两条线程分别代表存款者和取款者---现在假设系统有一种特殊的要求,系统要求存款者和取钱者不断的重复取钱、存钱操作、并且要求每当存款者将钱存入账户之后、取钱着就立即取出这笔钱,不允许存款者连续两次取钱,也不允许取钱者两次取钱,

这个时候我们就应该为这两个取钱和存钱的方法进行同步。比如下面程序我们实现这个功能:
1、Account账户类---提供了取钱和存钱的两个方法,还要提供一个flag旗标来表示账户是不是已经有存款了[code=java]package cn.itcast.heima
public class Account
{
        //用来标识账户号
        private String accountNo;
        //用来表示账户余额
        private double balance;
        //标识账户中是否已有存款的旗标
        private boolean flag = false;

    //无参和有参构造器
        public Account(){}

        public Account(String accountNo , double balance)
        {
                this.accountNo = accountNo;
                this.balance = balance;
        }


        public void setAccountNo(String accountNo)
        {
                this.accountNo = accountNo;
        }
        public String getAccountNo()
        {
                 return this.accountNo;
        }

    //不提供balance属性的setter方法,防止程序直接修改这个属性
        public double getBalance()
        {
                 return this.balance;
        }

         //因为有有两个方法可能同时需要并发修改Account类的balance属性,所以我们用synchronized 修饰这两个方法
        public synchronized void draw(double drawAmount)
        {
                try
                {
                        //如果flag为假,表明账户中还没有人存钱进去,则取钱方法阻塞
                        if (!flag)
                        {
                                wait();
                        }
                        else
                        {
                                //执行取钱
                                System.out.println(Thread.currentThread().getName() +
                                        " 取钱:" +  drawAmount);
                                balance -= drawAmount;
                                System.out.println("账户余额为:" + balance);
                                //将标识账户是否已有存款的旗标设为false。
                                flag = false;
                                //唤醒其他线程
                                notifyAll();
                        }
                }
                catch (InterruptedException ex)
                {
                        ex.printStackTrace();
                }
        }
        public synchronized void deposit(double depositAmount)
        {
                try
                {
                        //如果flag为真,表明账户中已有人存钱进去,则存钱方法阻塞
                        if (flag)
                        {
                                wait();
                        }
                        else
                        {
                                //执行存款
                                System.out.println(Thread.currentThread().getName() +
                                        " 存款:" +  depositAmount);
                                balance += depositAmount;
                                System.out.println("账户余额为:" + balance);
                                //将表示账户是否已有存款的旗标设为true
                                flag = true;
                                //唤醒其他线程
                                notifyAll();
                        }
                }
                catch (InterruptedException ex)
                {
                        ex.printStackTrace();
                }
        }

    //重写这个类的hashCode和equals方法
        //一般都要重写这两个方法,论坛中有帖子已经说明为什么了,
        public int hashCode()
        {
                return accountNo.hashCode();
        }
        public boolean equals(Object obj)
        {
                if (obj != null && obj.getClass() == Account.class)
                {
                        Account target = (Account)obj;
                        return target.getAccountNo().equals(accountNo);
                }
                return false;
        }
}[/code]关于equals方法和hashCode方法可以参考这个帖子http://bbs.itheima.com/thread-696-1-1.html
以后我们还会学到同步锁其实功能都差不多一样,但是同步锁功能比这个更强大,
2、下面定义两条线程,其中的线程执行体分别执行上面Account中的取钱和存钱方法:
DrawThread ---模拟取钱的操作[code=java]package cn.itcast.heima

public class DrawThread extends Thread
{
        //模拟用户账户
        private Account account;
        //当前取钱线程所希望取的钱数
        private double drawAmount;

        public DrawThread(String name , Account account ,
                double drawAmount)
        {
                super(name);
                this.account = account;
                this.drawAmount = drawAmount;
        }

        //重复50次执行取钱操作
        public void run()
        {
                for (int i = 0 ; i < 50 ; i++ )
                {
                        account.draw(drawAmount);
                }
        }
}[/code]DepositThread ---模拟存钱的操作[code=java]package cn.itcast.heima

public class DepositThread extends Thread
{
        //模拟用户账户
        private Account account;
        //当前取钱线程所希望存款的钱数
        private double depositAmount;

        public DepositThread(String name , Account account ,
                double depositAmount)
        {
                super(name);
                this.account = account;
                this.depositAmount = depositAmount;
        }

        //重复50次执行存款操作
        public void run()
        {
                for (int i = 0 ; i < 50 ; i++ )
                {
                        account.deposit(depositAmount);
                }               
        }
}[/code]3、主程序可以启动多条取钱线程和存钱线程---[code=java]package cn.itcast.heima
public class TestDraw
{
        public static void main(String[] args)
        {
                //创建一个账户
                Account acct = new Account("530335264" , 0);
                new DrawThread("取钱者" , acct , 800).start();
                new DepositThread("存款者甲" , acct , 812).start();
                new DepositThread("存款者乙" , acct , 812).start();
        }
}[/code]运行结果我们可以看到:[code=java]存款者甲 存钱:812.0
账户余额:812.0
取钱者 取钱:812.0
账户余额:0.0
...............
取钱者 取钱:812.0
账户余额:0.0
存款者丙 存钱:812.0
账户余额:812.0
取钱者 取钱:812.0
账户余额:0.0
.........
存款者乙 存钱:812.0
账户余额:812.0  ---------------程序阻塞[/code]最后程序阻塞:因为有两个线程存钱,循环100次  ,一个取钱线程操作50次,所以最后取钱线程运行结束,程序阻塞--但不是死锁
[ 本帖最后由 詹季春 于 2011-08-02  01:41 编辑 ]

评分

参与人数 1技术分 +2 收起 理由
admin + 2

查看全部评分

回复 使用道具 举报
黑马网友  发表于 2011-8-2 12:48:14
板凳
楼上的同学:lol ,请问这时候有必要覆盖equals和hashCode吗,你能解释一下嘛,不太理解,谢谢!
回复 使用道具 举报
:) 习惯最重要、上面只是实现了这个类自有的“逻辑相等的标准而已”,是往后考虑,
[ 本帖最后由 詹季春 于 2011-08-02  14:09 编辑 ]
回复 使用道具 举报
黑马网友  发表于 2011-8-2 15:52:18
地板
谢谢各位的热心答复,虽然还是有点乱,但很有收获 谢谢啦!
回复 使用道具 举报
您需要登录后才可以回帖 登录 | 加入黑马