多线程(二)

来源:互联网 发布:天谕女角色捏脸数据 编辑:程序博客网 时间:2024/05/20 11:27

JDK5—Lock

  虽然我们可以理解同步代码块和同步方法的锁对象问题,但是我们并没有直接看到在哪里加上了锁,在哪里释放了锁,  为了更清晰的表达如何加锁和释放锁,JDK5以后提供了一个新的锁对象Lock。  Lock:        void lock(): 获取锁。        void unlock():释放锁。    ReentrantLock是Lock的实现类。
package cn.itcast_01;import java.util.concurrent.locks.Lock;import java.util.concurrent.locks.ReentrantLock;public class SellTicket implements Runnable {    // 定义票    private int tickets = 100;    // 定义锁对象    private Lock lock = new ReentrantLock();    @Override    public void run() {        while (true) {            try {                // 加锁                lock.lock();                if (tickets > 0) {                    try {                        Thread.sleep(100);                    } catch (InterruptedException e) {                        e.printStackTrace();                    }                    System.out.println(Thread.currentThread().getName()                            + "正在出售第" + (tickets--) + "张票");                }            } finally {                // 释放锁                lock.unlock();            }        }    }}
package cn.itcast_01;public class SellTicketDemo {    public static void main(String[] args) {        // 创建资源对象        SellTicket st = new SellTicket();        // 创建三个窗口        Thread t1 = new Thread(st, "窗口1");        Thread t2 = new Thread(st, "窗口2");        Thread t3 = new Thread(st, "窗口3");        // 启动线程        t1.start();        t2.start();        t3.start();    }}

死锁问题

  同步的弊端:        A:效率低        B:容易产生死锁  死锁:        两个或两个以上的线程在争夺资源的过程中,发生的一种相互等待的现象。  举例:        中国人,美国人吃饭案例。        正常情况:            中国人:筷子两支            美国人:刀和叉        现在:            中国人:筷子1支,刀一把            美国人:筷子1支,叉一把
package cn.itcast_02;public class MyLock {    // 创建两把锁对象    public static final Object objA = new Object();    public static final Object objB = new Object();}
package cn.itcast_02;public class DieLock extends Thread {    private 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 cn.itcast_02;public class DieLockDemo {    public static void main(String[] args) {        DieLock dl1 = new DieLock(true);        DieLock dl2 = new DieLock(false);        dl1.start();        dl2.start();    }}

线程通信

  分析:        资源类:Student         设置学生数据:SetThread(生产者)        获取学生数据:GetThread(消费者)        测试类:StudentDemo  问题1:按照思路写代码,发现数据每次都是:null---0      原因:我们在每个线程中都创建了新的资源,而我们要求的时候设置和获取线程的资源应该是同一个      如何实现呢?            在外界把这个数据创建出来,通过构造方法传递给其他的类。  问题2:为了数据的效果好一些,我加入了循环和判断,给出不同的值,这个时候产生了新的问题        A:同一个数据出现多次        B:姓名和年龄不匹配      原因:            A:同一个数据出现多次                CPU的一点点时间片的执行权,就足够你执行很多次。            B:姓名和年龄不匹配                线程运行的随机性  线程安全问题:        A:是否是多线程环境      是        B:是否有共享数据       是        C:是否有多条语句操作共享数据 是  解决方案:        加锁。        注意:            A:不同种类的线程都要加锁。            B:不同种类的线程加的锁必须是同一把。
package cn.itcast_04;public class Student {    String name;    int age;}
package cn.itcast_04;public class SetThread implements Runnable {    private Student s;    private int x = 0;    public SetThread(Student s) {        this.s = s;    }    @Override    public void run() {        while (true) {            synchronized (s) {                if (x % 2 == 0) {                    s.name = "林青霞";//刚走到这里,就被别人抢到了执行权                    s.age = 27;                } else {                    s.name = "刘意"; //刚走到这里,就被别人抢到了执行权                    s.age = 30;                }                x++;            }        }    }}
package cn.itcast_04;public class GetThread implements Runnable {    private Student s;    public GetThread(Student s) {        this.s = s;    }    @Override    public void run() {        while (true) {            synchronized (s) {                System.out.println(s.name + "---" + s.age);            }        }    }}
package cn.itcast_04;public class StudentDemo {    public static void main(String[] args) {        //创建资源        Student s = new Student();        //设置和获取的类        SetThread st = new SetThread(s);        GetThread gt = new GetThread(s);        //线程类        Thread t1 = new Thread(st);        Thread t2 = new Thread(gt);        //启动线程        t1.start();        t2.start();    }}

这里写图片描述

等待唤醒机制

  问题3:虽然数据安全了,但是呢,一次一大片不好看,我就想依次的一次一个输出。  如何实现呢?        通过Java提供的等待唤醒机制解决。  等待唤醒:        Object类中提供了三个方法:            wait():等待            notify():唤醒单个线程            notifyAll():唤醒所有线程        为什么这些方法不定义在Thread类中呢?            这些方法的调用必须通过锁对象调用,而我们刚才使用的锁对象是任意锁对象。            所以,这些方法必须定义在Object类中。
package cn.itcast_05;public class Student {    String name;    int age;    boolean flag; // 默认情况是没有数据,如果是true,说明有数据}
package cn.itcast_05;public class SetThread implements Runnable {    private Student s;    private int x = 0;    public SetThread(Student s) {        this.s = s;    }    @Override    public void run() {        while (true) {            synchronized (s) {                //判断有没有                if(s.flag){                    try {                        s.wait(); //t1等着,释放锁                    } catch (InterruptedException e) {                        e.printStackTrace();                    }                }                if (x % 2 == 0) {                    s.name = "林青霞";                    s.age = 27;                } else {                    s.name = "刘意";                    s.age = 30;                }                x++; //x=1                //修改标记                s.flag = true;                //唤醒线程                s.notify(); //唤醒t2,唤醒并不表示你立马可以执行,必须还得抢CPU的执行权。            }            //t1有,或者t2有        }    }}
package cn.itcast_05;public class GetThread implements Runnable {    private Student s;    public GetThread(Student s) {        this.s = s;    }    @Override    public void run() {        while (true) {            synchronized (s) {                if(!s.flag){                    try {                        s.wait(); //t2就等待了。立即释放锁。将来醒过来的时候,是从这里醒过来的时候                    } catch (InterruptedException e) {                        e.printStackTrace();                    }                }                System.out.println(s.name + "---" + s.age);                //林青霞---27                //刘意---30                //修改标记                s.flag = false;                //唤醒线程                s.notify(); //唤醒t1            }        }    }}
package cn.itcast_05;public class StudentDemo {    public static void main(String[] args) {        //创建资源        Student s = new Student();        //设置和获取的类        SetThread st = new SetThread(s);        GetThread gt = new GetThread(s);        //线程类        Thread t1 = new Thread(st);        Thread t2 = new Thread(gt);        //启动线程        t1.start();        t2.start();    }}

线程状态转换图

线程状态转换图

线程组

  线程类里面的方法:public final ThreadGroup getThreadGroup()  线程组: 把多个线程组合到一起。          它可以对一批线程进行分类管理,Java允许程序直接对线程组进行控制。    ThreadGroup(String name)    Thread(ThreadGroup group, Runnable target, String name)    public final String getName()
package cn.itcast_06;public class MyRunnable implements Runnable {    @Override    public void run() {        for (int x = 0; x < 100; x++) {            System.out.println(Thread.currentThread().getName() + ":" + x);        }    }}
package cn.itcast_06;public class ThreadGroupDemo {    public static void main(String[] args) {        // method1();        // 我们如何修改线程所在的组呢?        // 创建一个线程组        // 创建其他线程的时候,把其他线程的组指定为我们自己新建线程组        method2();        // t1.start();        // t2.start();    }    private static void method2() {        // ThreadGroup(String name)        ThreadGroup tg = new ThreadGroup("这是一个新的组");        MyRunnable my = new MyRunnable();        // Thread(ThreadGroup group, Runnable target, String name)        Thread t1 = new Thread(tg, my, "林青霞");        Thread t2 = new Thread(tg, my, "刘意");        System.out.println(t1.getThreadGroup().getName());        System.out.println(t2.getThreadGroup().getName());        //通过组名称设置后台线程,表示该组的线程都是后台线程        tg.setDaemon(true);    }    private static void method1() {        MyRunnable my = new MyRunnable();        Thread t1 = new Thread(my, "林青霞");        Thread t2 = new Thread(my, "刘意");        // 我不知道他们属于那个线程组,我想知道,怎么办        // 线程类里面的方法:public final ThreadGroup getThreadGroup()        ThreadGroup tg1 = t1.getThreadGroup();        ThreadGroup tg2 = t2.getThreadGroup();        // 线程组里面的方法:public final String getName()        String name1 = tg1.getName();        String name2 = tg2.getName();        System.out.println(name1);        System.out.println(name2);        // 通过结果我们知道了:线程默认情况下属于main线程组        // 通过下面的测试,你应该能够看到,默任情况下,所有的线程都属于同一个组        System.out.println(Thread.currentThread().getThreadGroup().getName());    }}

最终版代码

package cn.itcast_07;public class Student {    private String name;    private int age;    private boolean flag; // 默认情况是没有数据,如果是true,说明有数据    public synchronized void set(String name, int age) {        // 如果有数据,就等待        if (this.flag) {            try {                this.wait();            } catch (InterruptedException e) {                e.printStackTrace();            }        }        // 设置数据        this.name = name;        this.age = age;        // 修改标记        this.flag = true;        this.notify();    }    public synchronized void get() {        // 如果没有数据,就等待        if (!this.flag) {            try {                this.wait();            } catch (InterruptedException e) {                e.printStackTrace();            }        }        // 获取数据        System.out.println(this.name + "---" + this.age);        // 修改标记        this.flag = false;        this.notify();    }}
package cn.itcast_07;public class SetThread implements Runnable {    private Student s;    private int x = 0;    public SetThread(Student s) {        this.s = s;    }    @Override    public void run() {        while (true) {            if (x % 2 == 0) {                s.set("林青霞", 27);            } else {                s.set("刘意", 30);            }            x++;        }    }}
package cn.itcast_07;public class GetThread implements Runnable {    private Student s;    public GetThread(Student s) {        this.s = s;    }    @Override    public void run() {        while (true) {            s.get();        }    }}
public class StudentDemo {    public static void main(String[] args) {        //创建资源        Student s = new Student();        //设置和获取的类        SetThread st = new SetThread(s);        GetThread gt = new GetThread(s);        //线程类        Thread t1 = new Thread(st);        Thread t2 = new Thread(gt);        //启动线程        t1.start();        t2.start();    }}
0 0
原创粉丝点击