class TicketsSystem_deadLock
{
public static void main(String[] args)
{
//main线程开始跑,发送start消息让持有obj锁的Thread0线程就绪,
//由于中途让main线程休息了200毫秒,所以Thread0线程开始跑,
//main线程休息完200毫秒,开始让Thread1线程就绪,而后开跑,
//由于Thread0线程未跑结束,并没有释放obj锁,所以导致Thread1
//试图使用obj锁失败,从而导致同步死锁.
//简单来说就是T0,T1共用1把锁,T0还没有使用完这把锁,T1想抢锁,2个人
//就开始不跑别的事,开始互相抢锁.也就是2个人同步的时间片出现交集.
//可让main线程休息足够长时间,让T0跑完,再让T1跑.
SellThread6 st = new SellThread6();
new Thread(st).start();
try
{
Thread.sleep(200);
}
catch(Exception e)
{
e.printStackTrace();
}
st.b = true;
new Thread(st).start();
}
}class SellThread6 implements Runnable
{
boolean b = false;
int tickets=100;
Object obj=new Object();
public void run()
{
if(b==false)
{
while(true)
{
sellTicket();
}
}
else
{
while(true)
{
synchronized(obj)
{
try
{
Thread.sleep(100);
}
catch(Exception e)
{
e.printStackTrace();
}
synchronized(this)
{
if(tickets>0)
{

System.out.println("obj:"+Thread.currentThread().getName()+" sell tickets:"+tickets);
tickets--;
}
}
}
}
}
}
public synchronized void sellTicket()
{
synchronized(obj)
{
if(tickets>0)
{
try
{
Thread.sleep(10);
}
catch(Exception e)
{
e.printStackTrace();
}
System.out.println("sell():"+Thread.currentThread().getName()+" sell tickets:"+tickets);
tickets--;
}
}
}
}上面注释是我对这个同步代码的理解...求教高手看我的理解有什么问题..

解决方案 »

  1.   

    比如我们一起外出就餐,每个人都是一个线程,餐桌上的食物则是共享资源,当我看到红烧鸡腿上桌后立即拿起筷子直奔目标,眼看着就得手的时候,突然~~~鸡腿消失了,一个距离盘子更近的线程正在得意地啃着。为了避免上述问题的发生,Java为我们提供了“synchronized(同步化)修饰符”来避免资源冲突,你可以将资源类中某个函数或变量声明为synchronized(同步化),每个继承自Object的类都含有一个机锁(Lock),它是余生俱来的,不需要编写任何代码来启用它。当我们调用任何synchronized(同步化)函数时,该对象将被锁定,对象中所有synchronized(同步化)函数便无法被调用,直到第一个函数执行完毕并解除机锁。
    synchronized(同步化)修饰符的使用方式:
    1、两个方法在data上是同步的。
    Object data = new Object();
    public void method1(){
        synchronized(data){
            //...
        }
    }
    public void method2(){
        synchronized(data){
            //...
        }

    2、两个方法是同步的,method1不能同时有两个使用者,method1和method2之间也不能同时被调用。
    public synchronized void method1(){
        //...
    }
    public synchronized void method2(){
        //...

    3、使用this关键字,效果同上。
    public void method1(){
        synchronized (this){
            //...
        }
    }
    public void method2(){
        synchronized (this){
            //...
        }

    学会了使用方法,下面就来实际应用一下,我们将要模拟一个银行的存储过程,用线程来模拟银行的各个交易大厅,几乎同时派出6个对同一帐号进行的存取业务,假设每笔业务需要1秒钟来处理,我们来测试一下没有进行同步化处理和进行过同步化的处理的过程和结果。
    import java.awt.BorderLayout;
    import java.awt.event.ActionEvent;
    import java.awt.event.ActionListener;
    import javax.swing.JButton;
    import javax.swing.JFrame;
    import javax.swing.JPanel;
    import javax.swing.JScrollPane;
    import javax.swing.JTextArea;
    import javax.swing.JTextField;
     
    /**
     * 线程同步
     * 我们模拟一个银行存储过程来证明线程同步的必要性以及在Java中进行线程同步的方法
     * 重点:synchronized 修饰符
     * @author 五斗米
     * @blog http://blog.csdn.net/mq612
     */
    public class TestMain5 extends JFrame {
        private MyAccounts myAccounts = null; // 我的帐号
        private JTextField text = null; // 银行存款数额显示
        private JTextArea textArea = null; // 交易过程显示
        private JButton button = null; // 开始模拟交易的按钮
       /**
         * 构造一个银行存取款界面
         */
        public TestMain5(){
            super("线程同步测试");
            myAccounts = new MyAccounts();
            text = new JTextField(Integer.toString(myAccounts.inquire()), 10); // 我们在银行中的初始存款为100
            textArea = new JTextArea();
            textArea.setText("交易日志:");
            JScrollPane sp = new JScrollPane(textArea);
            button = new JButton("开始交易");
            button.addActionListener(new ActionListener(){
                public void actionPerformed(ActionEvent e) {
                    new Bank("钟楼支行", myAccounts, Bank.DEAL_SAVING, 800);
                    new Bank("高新支行", myAccounts, Bank.DEAL_SAVING, 1300);
                    new Bank("小寨支行", myAccounts, Bank.DEAL_FETCH, 200);
                    new Bank("雁塔支行", myAccounts, Bank.DEAL_FETCH, 400);
                    new Bank("兴庆支行", myAccounts, Bank.DEAL_SAVING, 100);
                    new Bank("土门支行", myAccounts, Bank.DEAL_FETCH, 700);
                }
            });
            JPanel pane = new JPanel();
            pane.add(text);
            pane.add(button);
            this.getContentPane().add(pane, BorderLayout.NORTH);
            this.getContentPane().add(sp);
            this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
            this.setSize(300, 200);
            this.setLocationRelativeTo(null);
            this.setVisible(true);
       }
        
        /**
         * 银行交易大厅类
         * 一般银行都会有N个交易大厅,这些大厅可以同时处理多笔业务,这正好符合多线程的特点
         */
        class Bank extends Thread{
            /**
             * 静态字段:用于表示储存
             */
            public static final int DEAL_SAVING = 0;
            /**
             * 静态字段:用于表示提取
             */
            public static final int DEAL_FETCH = 1;
            private int buy = Bank.DEAL_FETCH; // 默认使取款
            private int count = 0;
            private MyAccounts myAccounts = null; // 我的帐号
            /**
             * 构造这个银行交易大厅
             * @param name 这个交易大厅的名称
             * @param myAccounts 我的银行帐号
             * @param buy 行为,参考字段:DEAL_SAVING或DEAL_FETCH
             * @param count 钱的数量
             */
            public Bank(String name, MyAccounts myAccounts, int buy, int count){
                super(name);
                this.myAccounts = myAccounts;
                this.buy = buy;
                this.count = count;
                this.start();
            }
            public void run(){
                int $count = 0;
                if(buy == Bank.DEAL_SAVING){ // 如果是存款业务
                    $count = myAccounts.saving(count);
                }else if(buy == Bank.DEAL_FETCH){ // 如果是取款业务
                    $count = myAccounts.fetch(count);
                }
                text.setText(Integer.toString($count));
                textArea.append("\n" + this.getName() + "   " + (buy == Bank.DEAL_SAVING ? "存款": "取款") + "   金额:" + count + "    结余:" + $count);
            }
        }
        /**
         * 我的帐号
         * 进行同步测试
         */
        class MyAccounts{
            private Integer count = 1100;
            public MyAccounts(){
            }
            /**
             * 查询我的帐号
             */
            public int inquire(){
                synchronized (count){
                    return count;
                }
            }
            /**
             * 存款业务
             * @param c 存款的数量
             * @return 业务办理完成后的数量
             */
            public int saving(int c){
                synchronized (count){
                    //return count += c; // 为了能更好的观察,我们将这个简洁的语句注释掉
                    int $count = inquire(); // 先查询帐户中的存款
                    $count += c;
                    try {
                        Thread.sleep(1000); // 为了更好的观察,使业务在此停顿1秒钟
                    } catch (InterruptedException ex) {
                        ex.printStackTrace();
                    }
                    count = $count; // 最后将总数储存起来
                    return inquire(); // 返回最新的存款数
                }
            }
            /**
             * 取款业务
             * @param c 取款的数量
             * @return 业务办理完成后的数量
             */
            public int fetch(int c){
                synchronized (count){
                    //return count -= c; // 为了能更好的观察,我们将这个简洁的语句注释掉
                    int $count = inquire(); // 先查询帐户中的存款
                    $count -= c;
                    try {
                        Thread.sleep(1000); // 为了更好的观察,使业务在此停顿1秒钟
                    } catch (InterruptedException ex) {
                        ex.printStackTrace();
                    }
                    count = $count; // 最后将总数储存起来
                    return inquire(); // 返回最新的存款数
                }
            }
        }
        
        public static void main(String [] args){
            new TestMain5();
        }
        

    所以 你的理解差不多是对的