黑马程序员技术交流社区

标题: 线程安全问题 [打印本页]

作者: 冯鹏飞    时间: 2011-7-22 21:39
标题: 线程安全问题
为什么会有线程安全机制呢,看视频时不明白为什么要添加syncronized方法,希望能举例说明在什么情况下需要使用syncronized ?
作者: 匿名    时间: 2011-7-22 21:48
syncronized 就是一个监听器
由于cpu是时间片机制的。就是这个线程运行一会,跑到另一个线程上运行一会。
如果在第一个线程上刚操作了一个参数,又跑到另一个线程上再操作这个参数,再回到第一个线程上,那个现在的参数就不是原来的参数了吧
syncronized 就是起到一个,在第一个线程操作的时候,第二个线程不能操作这个线程的数据。从而保证了线程的同步。
java1.5之后又有了一个叫lock的接口。他的子类起到的作用和syncronized 差不多。通过它的luck和unlock方法可以比syncronized 更灵活。所以更推荐使用lock接口的子类
作者: 匿名    时间: 2011-7-24 09:38
:) 很简单的一个日常生活中的列子,一个账户给两个人用,每人都去取出这个账户的所有余额,如果一个线程取钱的操作刚好在账户余额减少之前就暂停,另外一个线程进入这个取钱的方法进行取钱,那么也就使这个账户的余额为负了
看如下的例子[code=java]public class Account
{
        //封装账户编号、账户余额两个属性
        private String accountNo;
        private double balance;
        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;
        }

        public void setBalance(double balance)
        {
                 this.balance = balance;
        }
        public double getBalance()
        {
                 return this.balance;
        }


        //下面两个方法根据accountNo来计算Account的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][code=java]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;
        }

        //当多条线程修改同一个共享数据时,将涉及到数据安全问题。
        public void run()
        {
                //账户余额大于取钱数目
                if (account.getBalance() >= drawAmount)
                {
                        //吐出钞票
                        System.out.println(getName() +
                                "取钱成功!吐出钞票:" + drawAmount);
                        /*
                        try
                        {
                                Thread.sleep(1);                       
                        }
                        catch (InterruptedException ex)
                        {
                                ex.printStackTrace();
                        }
                        */
                        //修改余额
                        account.setBalance(account.getBalance() - drawAmount);
                        System.out.println("\t余额为: " + account.getBalance());
                }
                else
                {
                        System.out.println(getName() + "取钱失败!余额不足!");
                }
        }
}[/code][code=java]public class TestDraw
{
    public static void main(String[] args)
    {
                //创建一个账户
                Account acct = new Account("1234567" , 1000);
                //模拟两个线程对同一个账户取钱
                new DrawThread("甲" , acct , 800).start();
                new DrawThread("乙" , acct , 800).start();
    }
}[/code]这个例子运行结果之后就得出账户余额最后为负值,
作者: 匿名    时间: 2011-7-24 14:01
synchronized 非静态方法只是保护当前实例
synchronized 静态方法才是包括类所有共享的数据
1.synchronized 方法:通过在方法声明中加入 synchronized关键字来声明 synchronized 方法。如:
public synchronized void accessVal(int newVal);
 synchronized 方法控制对类成员变量的访问:每个类实例对应一把锁,每个 synchronized 方法都必须获得调用该方法的类实例的锁方能执行,否则所属线程阻塞,方法一旦执行,就独占该锁,直到从该方法返回时才将锁释放,此后被阻塞的线程方能获得该锁,重新进入可执行状态。这种机制确保了同一时刻对于每一个类实例,其所有声明为 synchronized 的成员函数中至多只有一个处于可执行状态(因为至多只有一个能够获得该类实例对应的锁),从而有效避免了类成员变量的访问冲突(只要所有可能访问类成员变量的方法均被声明为 synchronized)。
 在 Java 中,不光是类实例,每一个类也对应一把锁,这样我们也可将类的静态成员函数声明为 synchronized ,以控制其对类的静态成员变量的访问。
 synchronized 方法的缺陷:若将一个大的方法声明为synchronized 将会大大影响效率,典型地,若将线程类的方法 run() 声明为 synchronized ,由于在线程的整个生命期内它一直在运行,因此将导致它对本类任何 synchronized 方法的调用都永远不会成功。当然我们可以通过将访问类成员变量的代码放到专门的方法中,将其声明为 synchronized ,并在主方法中调用来解决这一问题,但是 Java 为我们提供了更好的解决办法,那就是 synchronized 块。
2. synchronized 块:通过 synchronized关键字来声明synchronized 块。语法如下:
synchronized(syncObject)
{
//允许访问控制的代码
}
    synchronized 块是这样一个代码块,其中的代码必须获得对象 syncObject (如前所述,可以是类实例或类)的锁方能执行,具体机制同前所述。由于可以针对任意代码块,且可任意指定上锁的对象,故灵活性较高。




欢迎光临 黑马程序员技术交流社区 (http://bbs.itheima.com/) 黑马程序员IT技术论坛 X3.2