JAVA线程(升级)

同步方法

package com.thread.ticket3;

/**
 * 同步代码块中的对象针对多个线程必须是同一个,
 * 其实这个对象被称为同步锁对象
 * 
 * 同步代码块的锁对象是谁呢?
 * 任意对象
 * 
 * 如果一个方法,一进去我们就看到代码被同步了,所以,我就想,这个同步能不能加在方法上面呢?
 *  能:这个东西被称为同步方法:就是把同步关键字加在同步方法上。
 *  
 * 现在我们在这样的一个代码中
 *  if(){
 *      同步代码块
 *  }else{
 *      同步方法
 *      private synchronized void show()
 *  }
 *  
 *  又会出现负票,为什么会有问题呢?
 *      就是同步的代码,必须保证锁对象一致。
 *  同步方法的锁对象是谁呢?
 *      this对象
 *  静态同步方法的锁对象是谁呢?
 *      Ticket.class 字节码文件对象
 *     当前类的字节码文件对象
 */
public class TicketDemo {
    public static void main(String[] args) {
        Ticket t1 = new Ticket();
        Thread t = new Thread(t1, "窗口1");
        Thread t4 = new Thread(t1, "窗口2");
        Thread t2 = new Thread(t1, "窗口3");
        Thread t3 = new Thread(t1, "窗口4");
        t.start();
        t4.start();
        t2.start();
        t3.start();

    }
}

package com.thread.ticket3;

public class Ticket implements Runnable {
    private static int tickets = 100;
    //private final Object b       = new Object();
    private int        x       = 0;

    @Override
    public void run() {
        while (true) {
            if (x % 2 == 0) {
                synchronized (this) {
                    if (tickets > 0) {
                        try {
                            Thread.sleep(100);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                        System.out.println(Thread.currentThread().getName() + "正在出售第" + (tickets--) + "张票");

                    }
                }
            } else {
                show();
            }
            x++;

        }
    }

    //同步方法
    private static synchronized void show() {
        if (tickets > 0) {
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName() + "正在出售第" + (tickets--) + "张票");

        }
    }
}

上面在同步方法上加了 static 又导致了线程安全问题 如下图:
这里写图片描述

那么静态同步方法的对象又是什么呢??

默认当前类的字节码文件对象,所以要将同步代码块中的对象改为Ticket.class  同步方法和同步代码块中的对象一致。
 synchronized (Ticket.class) {
                    if (tickets > 0) {
                        try {
                            Thread.sleep(100);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                        System.out.println(Thread.currentThread().getName() + "正在出售第" + (tickets--) + "张票");

                    }
                }

JDK1.5 的新特性

package com.thread.ticket4;

/**
 * jdk1.5的新特性
 * 以前同步关键字的方式,可以实现同步,并且是我们比较常见的方式,
 * 但是我们老是需要去考虑它使用的哪个锁对象,比较麻烦
 * 所以,在JDK5以后,提供了一个新的方式: 
 *  Lock锁对象
 * 这种方式在以前的代码中比较少见,但是以后开发中可以这样中
 * vector:线程安全,加了synchronizd
 * ArrayList:线程不安全 
 * hashTable:线程安全
 * hashMap :线程不安全
 * 
 * @author yuliyang
 * @version $Id: TicketDemo.java, v 0.1 2016年12月4日 下午7:28:39 yuliyang Exp $
 */
public class TicketDemo {
    public static void main(String[] args) {
        Ticket t = new Ticket();
        Thread t1 = new Thread(t);
        Thread t2 = new Thread(t);
        Thread t3 = new Thread(t);
        Thread t4 = new Thread(t);
        t1.start();
        t2.start();
        t3.start();
        t4.start();
    }
}

package com.thread.ticket4;

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class Ticket implements Runnable {
    private int        ticket = 100;

    //private final ReentrantLock lock = new ReentrantLock();
    //定义一个锁对象
    private final Lock lock   = new ReentrantLock();

    @Override
    public void run() {

        while (true) {
            lock.lock();
            try {
                Thread.sleep(100);
                if (ticket > 0) {
                    System.out.println(Thread.currentThread().getName() + "---正在出售:" + (ticket--) + "张票");
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                //释放锁
                lock.unlock();
            }
        }
    }

}

生产者与消费者问题

package com.thread.ticket5;

/**
 * 线程间的通信问题:
 *      不同种类的线程对同一资源的操作问题。
 * 
 * 需求:我有一个学生,我可以对其属性赋值,我也可以获取其属性值,请用线程间的通信的案例来体现
 * 
 * 资源:学生student
 * 获取线程:GetThread
 * 设置线程:SetThread
 * 测试类:StudentDemo
 * 
 * 问题一:每个线程操作的是自己的那一个学生对象,
 *  怎么解决:让多个线程操作同一个学生对象
 * 问题二:因为线程的随机性,一次赋值和获取值得操作,数据出现了问题
 *  怎么解决:
 * 问题三:线程操作的东西一般都是比较复杂的操作
 * 所以我们加入了循环操作。产生了更大的问题。
 * 数据产生了混乱
 *  A:同一数据出现多次:
 *      CPU的一点点时间就足够一个线程执行很多次。
 *  B:数据出现了问题:
 *      由于CPU每一次的执行点应该是一次原子性的操作。
 *  解决线程安全问题:
 *      分析:真的是线程安全问题吗?
 *          1:是多线程环境吗?
 *              是
 *          2:有共享数据吗?
 *              有:student
 *          3:对共享数据有多条语句操作吗?    
 *              有:s.name s.age
 *  
 *  如何解决?
 *      把出现问题的代码用同步代码块包起来。
 *  问题4:加入同步方法块之后还是出现了问题。为什么?
 *      A:多种线程进行操作的时候,要保证同步,必须是每一种类型的线程都必须加同步
 *      B:即使是给多种类型的线程加锁,也必须是同一把锁
 *  到此解决了线程安全问题。
 *  
 * @author yuliyang
 * @version $Id: TicketDemo.java, v 0.1 2016年12月4日 下午7:28:39 yuliyang Exp $
 */
public class StudentDemo {
    public static void main(String[] args) {
        Student student = new Student();
        SetThread st = new SetThread(student);
        GetThread gt = new GetThread(student);
        Thread g = new Thread(gt);
        Thread s = new Thread(st);
        s.start();
        g.start();
    }
}


package com.thread.ticket5;

public class Student {
    private String name;
    private String age;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getAge() {
        return age;
    }

    public void setAge(String age) {
        this.age = age;
    }

}
package com.thread.ticket5;

public class SetThread implements Runnable {
    private final Student s;
    private int           x = 0;

    public SetThread(Student s) {
        this.s = s;
    }

    @Override
    public void run() {
        while (true) {
            //加同步,加同一把锁(s)
            synchronized (s) {
                if (x % 2 == 0) {
                    s.setName("aaa");
                    s.setAge("20");
                } else {
                    s.setName("vvv");
                    s.setAge("10");
                }
            }
            x++;
        }
    }
}

package com.thread.ticket5;

public class GetThread implements Runnable {
    private final Student student;

    public GetThread(Student student) {
        this.student = student;
    }

    @Override
    public void run() {
        while (true) {
            //加同步,加同一把锁
            synchronized (student) {
                System.out.println(student.getName() + ":" + student.getAge());
            }
        }
    }
}

等待唤醒机制

package com.thread.ticket6;

/**
 * 线程间的通信问题:
 *      不同种类的线程对同一资源的操作问题。
 * 
 * 需求:我有一个学生,我可以对其属性赋值,我也可以获取其属性值,请用线程间的通信的案例来体现
 * 
 * 资源:学生student
 * 获取线程:GetThread
 * 设置线程:SetThread
 * 测试类:StudentDemo
 * 
 * 问题一:店铺生产了包子,客人才可以买,卖完了,店铺再生产
 *  刚才的数据是大片大片的数据,此时需要改为:只有设置了学生的属性,才能获
 *  取到学生的属性值,依次的出现,怎么办呢?
 * 原理:
 *      如果我的学生属性没有值,应该先赋值,然后才可以使用,
 *      如果我的学生属性有值,应该使用后再赋值。
 * 在Student中加入了一个flag标记,false表示没有值
 * 
 * 为了配合这种操作,java提供了一种机制:等待唤醒机制。
 *  notify(); //该方法在Object中
 *  wait();  //该方法在Object中
 *  这两个方法的调用,必须是锁对象(对象监视器)调用。
 *      为什么这两个方法没有定义到Thread类里面呢,
 *      因为如果放到了Thread类中,就得使用Thread对象去调用这两个方法,
 *      但是,同步代码块的锁对象是任意的。那么只能用Object。
 * 怎么用?
 *  看代码例子。
 *  
 */
public class StudentDemo {
    public static void main(String[] args) {
        Student student = new Student();
        SetThread st = new SetThread(student);
        GetThread gt = new GetThread(student);
        Thread g = new Thread(gt);
        Thread s = new Thread(st);
        s.start();
        g.start();
    }
}


package com.thread.ticket6;

public class SetThread implements Runnable {
    private final Student student;
    private int           x = 0;

    public SetThread(Student s) {
        this.student = s;
    }

    @Override
    public void run() {
        while (true) {
            //加同步,加同一把锁(s)
            synchronized (student) {
                //如果有值:等待,(店家如果有包子,就等它卖完)
                if (student.getFlag()) {
                    try {
                        student.wait();
                        /**
                         * s线程就等待在这里了。
                         */
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }

                //设置值(如果没有包子,就设置值)
                if (x % 2 == 0) {
                    student.setName("余丽阳");
                    student.setAge("20");
                } else {
                    student.setName("简小慧");
                    student.setAge("10");
                }
                x++;

                //有值后,修改标记为true(告诉顾客有包子了)
                student.setFlag(true);
                //唤醒等待程序
                student.notify();
                /**
                 * 唤醒等待的线程,唤醒其他的线程,不代表其他的线程就能够立即执行
                 */
            }
            //可能g线程,可能s线程
        }
    }
}


package com.thread.ticket6;

public class GetThread implements Runnable {
    private final Student student;

    public GetThread(Student student) {
        this.student = student;
    }

    @Override
    public void run() {
        while (true) {
            //加同步,加同一把锁
            synchronized (student) {
                //如果没有值,就等待。(顾客发现如果没有包子,就等着)
                if (!student.getFlag()) {
                    try {
                        student.wait();
                        /**
                         * g线程在这里等待了,那么它就会释放锁资源。
                         * 将来,当它再次获取锁对象的时候,
                         * 是从哪里等待,哪里醒来
                         */
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                //如果有值,则输出(如果有包子,就买了吃)
                System.out.println(student.getName() + ":" + student.getAge());
                //告诉店家没有包子了
                student.setFlag(false);
                //唤醒:(叫店家做包子)
                student.notify();
            }
            //可能g线程,可能s线程
        }
    }
}


package com.thread.ticket6;

public class Student {
    private String  name;
    private String  age;
    private boolean flag = false; //默认表示没有值

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getAge() {
        return age;
    }

    public void setAge(String age) {
        this.age = age;
    }

    public boolean getFlag() {
        return flag;
    }

    public void setFlag(boolean f) {
        this.flag = f;
    }
}

死锁

package com.thread.DieLock;

/**
 * 举例:
 *  五个哲学家的故事
 *  
 * 面试题:
 *  死锁例子:
 *      有两把锁 A,B 一个线程先使用了A锁,然后使用B锁
 *                一个线程先使用了B锁,然后使用了A锁。
 * 
 * @author yuliyang
 * @version $Id: DieLockDemo.java, v 0.1 2016年12月4日 下午9:59:10 yuliyang Exp $
 */
public class DieLockDemo {
    public static void main(String[] args) {
        DieLock d1 = new DieLock(true);
        DieLock d2 = new DieLock(false);
        d2.start();
        d1.start();
    }

}


package com.thread.DieLock;

public class DieLock extends Thread {
    private final boolean flag;

    public DieLock(boolean flag) {
        this.flag = flag;
    }

    @Override
    public void run() {
        if (flag) {
            synchronized (MyLock.objA) {
                System.out.println("if objA");
                synchronized (MyLock.objB) {
                    System.out.println("if objB");
                }

            }
        } else {
            synchronized (MyLock.objB) {
                System.out.println("else objB");
                synchronized (MyLock.objA) {
                    System.out.println("else objA");
                }
            }
        }
    }
}


package com.thread.DieLock;

public class MyLock {
    public static final Object objA = new Object();
    public static final Object objB = new Object();
}

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值