美文网首页Java基础程序员
再学—Java基础:多线程(2)

再学—Java基础:多线程(2)

作者: 0ur | 来源:发表于2019-01-25 00:10 被阅读6次

    上一篇获得了三个喜欢和一个关注,给我了继续写下去的动力,谢谢,谢谢!!!
    以前没写过,写的时候发现真的很有挑战,自己也从中收获很多,希望简友们觉得如果觉得有一篇文章真的给自己一点感动,一点帮助等,请给作者一个赞,可能真的会产生蝴蝶效应,再次谢谢你们看我的文章。

    以上都是废话,哈哈。接着上篇,继续讨论多线程的数据安全问题

    • 为什么会出现重复的数据和负数的数据的情况?
      因为多线程操作的是共享数据,mTicket,是三个线程的共享数据。如果每个线程都有自己单独的数据,是没有问题,因为那是单线程了。

    重复的数据:System.out.println(Thread.currentThread().getName() + " 出售车票= " + mTicket-- + " 张");这行代码翻译成计算机可以执行的指令,当0号线执行sop(),输出7,但是刚要执行--操作的时候。1号线线程抢到了CPU的执行权,这时候0号线程没有执行 -- 操作,所以mTicket 还是7,所以输出7,出现重复的数据。

    负数的数据:当mTicket =1时,0号线程做判断 > 1,进入sleep(10),放弃执行资格和执行权,1号线程判断 >0,进去进入sleep(10),这时候0号线程醒来抢到执行资格,输入0,1号线程醒来,抢到执行资格,输入-1。

    • 怎么解决多线程操作共享数据的问题呢?
    1. 同步代码块 格式 : synchronized(对象){ 需要同步的代码}

    把多个语句操作共享数据的代码给锁起来,让任意时刻只能有一个线程执行。

    public class RunnableDemo1 implements Runnable {
        //定义一百张车票
        private int mTicket = 100;
        private Object obj = new Object();
    
        @Override
        public void run() {
          while(true){
            synchronized (obj) {
                if(mTicket > 0) {
                    // 模拟卡机  让线程休眠10毫秒
                    try {
                        Thread.sleep(10);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    } 
    
                    System.out.println(Thread.currentThread().getName() + " 出售车票= " + mTicket-- + " 张");
                }
            }
        }
       }
    }
    
    
    执行效果图.png

    用同步代码块就可以解决,这里主要是那个obj,多线程同步的时候,必须是同一把锁。要不然还会出现共享数据的问题。

    1. 同步方法:就是把关键字synchronized 加在方法上
     public class RunnableDemo implements Runnable {
        //定义一百张车票
        private int mTicket = 100;
    
        @Override
        public void run() {
            while (true) {
                    sale();
            }
        }
    
        private synchronized void sale() {
            if (mTicket > 0) {
                // 模拟卡机  让线程休眠10毫秒
    //            try {
    //                Thread.sleep(10);
    //            } catch (InterruptedException e) {
    //                e.printStackTrace();
    //            }
                System.out.println(Thread.currentThread().getName() + " 出售车票= " + mTicket-- + " 张");
            }
        }
    }
    

    非静态方法的锁是本类对象的引用 this。

    public class RunnableDemo1 implements Runnable {
        //定义一百张车票
        private static int mTicket = 100;
    
        @Override
        public void run() {
            while (true) {
                    sale();
            }
        }
    
        private synchronized static void sale() {
            if (mTicket > 0) {
                // 模拟卡机  让线程休眠10毫秒
    //            try {
    //                Thread.sleep(10);
    //            } catch (InterruptedException e) {
    //                e.printStackTrace();
    //            }
                System.out.println(Thread.currentThread().getName() + " 出售车票= " + mTicket-- + " 张");
            }
        }
    }
    

    静态方法的锁是本类对象的class 字节码文件。

    • 总结下:
    1. 同步的前提
      多个线程使用的是同一个锁对象
      多个线程操作共享资源
    2. 同步的好处
      同步的出现解决了多线程的安全问题。
    3. 同步的弊端
      当线程相当多时,因为每个线程都会去判断同步上的锁,这是很耗费资源的,无形中会降低程序的运行效率,但是为了数据的安全也值了,鱼和熊掌不能兼得。
    • 两个线程实现交替打印(等待与唤醒机制)
    /**
     * 两个线程 实现交替打印 一
     * <p>
     * 输入线程   输出 一
     * <p>
     * 输出线程   输出 二
     */
    public class AlternatePrintThread {
    
        public static boolean mFlag = true;
    
        public static void main(String[] args) {
    
    
            new Thread(new InputThread()).start();
    
            new Thread(new OutputThread()).start();
        }
    
    
    }
    
    
    class InputThread implements Runnable {
    
        @Override
        public void run() {
            while (true) {
                synchronized (AlternatePrintThread.class) {
    
                    if (AlternatePrintThread.mFlag) {
                        try {
                            AlternatePrintThread.class.wait();
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                    AlternatePrintThread.mFlag = true;
                    System.out.println("我是一 ");
                    AlternatePrintThread.class.notify();
                }
            }
        }
    }
    
    class OutputThread implements Runnable {
     
    
        @Override
        public void run() {
            while (true) {
                synchronized (AlternatePrintThread.class) {
                    if (!AlternatePrintThread.mFlag) {
                        try {
                            AlternatePrintThread.class.wait();
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                    AlternatePrintThread.mFlag = false;
                    System.out.println(".......我是二 ");
                    AlternatePrintThread.class.notify();
                }
            }
        }
    }
    

    实现思路:
    交替打印肯定是两个线程 定义两个线程类
    不管你用的什么对象作为锁,连个线程一定是同一把锁

    • 多生产与多消费
    public class ThreadDemo {
    
        public static void main(String[] args) {
            Product p = new Product();
            new Thread(new Produce(p)).start();
            new Thread(new Produce(p)).start();
            new Thread(new Produce(p)).start();
            new Thread(new Produce(p)).start();
            new Thread(new Consumer(p)).start();
            new Thread(new Consumer(p)).start();
            new Thread(new Consumer(p)).start();
            new Thread(new Consumer(p)).start();
        }
    }
    
    
    class Product {
        //名字
        private String name;
        //计数器
        private int count;
        //标记
        private boolean flag = true;
        //生产方法,是让生产线程调用
        public synchronized void set(String name) {
            while (!flag)
                try {
                    this.wait();
                } catch (Exception e) {
                }
            this.name = name + count++;
            System.out.println(Thread.currentThread().getName() + "生产第  " + this.name);
            flag = false;
            this.notify();
        }
    
        //消费方法,是让消费线程调用
        public synchronized void get() {
            while (flag)
                try {
                    this.wait();
                } catch (Exception e) {
                }
            System.out.println(Thread.currentThread().getName() + "消费第... " + this.name);
            flag = true;
            this.notify();
        }
    }
    
    //生产者线程
    class Produce implements Runnable {
        private Product p;
    
        Produce(Product p) {
            this.p = p;
        }
    
        public void run() {
            while (true)
                p.set("Iphone");
        }
    }
    
    //消费者线程
    class Consumer implements Runnable {
        private Product p;
    
        Consumer(Product p) {
            this.p = p;
        }
    
        public void run() {
            while (true)
                p.get();
        }
    }
    
    

    notify():唤醒的是等待顺序唤醒(这是的 多生产与多消费不能用这个)
    notifyAll():唤醒的是等待的全部线程 (太浪费资源,如果我想唤醒 A线程的按等待顺序的线程的一个 就好了 ,所以Lock接口来了)
    线程还有一个特点就是 从那倒下去的 从那起来继续执行(要想让一段代码流转起来,那就是死循环,满足条件才让出去,否则一直转圈)

    • jdk since 1.5出现行的管理线程的类
    /**
     * since JDK1.5   Lock ,Condition 生产者 和 消费者
     */
    public class LockDemo {
        public static void main(String[] args) {
            Resource resource = new Resource();
            new Thread(new ConsumeThread(resource)).start();
            new Thread(new ConsumeThread(resource)).start();
            new Thread(new ConsumeThread(resource)).start();
            new Thread(new ConsumeThread(resource)).start();
    
            new Thread(new ProductThread(resource)).start();
            new Thread(new ProductThread(resource)).start();
            new Thread(new ProductThread(resource)).start();
            new Thread(new ProductThread(resource)).start();
        }
    }
    
    /**
     * 资源类
     */
    class Resource {
        private String productName;
        private int count = 0;
        private boolean flag = true;
    
    
        private Lock lock = new ReentrantLock();
        // 用锁创建了 两个线程的管理器   用自己的管理器去让自己的管理的线程等待  用对方的管理器去唤醒对方等待的线程
        private Condition pro = lock.newCondition();
        private Condition con = lock.newCondition();
    
        public void setProductName(String productName) {
            lock.lock();
            while (!this.flag) {
                try {
                    pro.await();//自己的线程管理器
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            this.productName = productName + "   " + count++;
            System.out.println("生产=" + this.productName);
            this.flag = false;//    和  while (this.flag) 方法必须向反 要不 这个线程会无限等待
            con.signal();//对方的线程管理器
            lock.unlock();
        }
    
        public void getProductName() {
    
            lock.lock();
            while (this.flag) {
                try {
                    con.await();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            System.out.println("消费=......" + this.productName);
            this.flag = true;
            pro.signal();
            lock.unlock();
    
        }
    }
    
    
    /**
     * 生产线程
     */
    class ProductThread implements Runnable {
        Resource mResource;
    
        public ProductThread(Resource resource) {
            this.mResource = resource;
        }
    
        @Override
        public void run() {
            while (true) {
                mResource.setProductName("Iphone100");
            }
        }
    }
    
    /**
     * 消费线程
     */
    class ConsumeThread implements Runnable {
        Resource mResource;
    
        public ConsumeThread(Resource resource) {
            this.mResource = resource;
        }
    
        @Override
        public void run() {
            while (true) {
                mResource.getProductName();
            }
        }
    }
    

    JDK1.5中,java.util.concurrent.locks包
    提供线程的新的管理方式
    接口Lock,替代了原有的synchronized的使用,使用灵活广泛
    接口中的方法 获取锁lock() 释放锁 unlock()
    接口实现类[ReentrantLock]
    接口Condition 替代原有监视器方法 wait notify notifyAll
    新旧方法的对比
    接口中Condition Object类
    await() wait()
    signal() notify()
    signalAll() notifyAll()
    获取接口的实现类对象,用Lock接口方法newCondition实现
    分解成截然不同对象(线程管理对象)

    • 死锁
    /**
     * 多线程的死锁
     */
    public class DeadLockDemo {
    
    
        public static class DeadRunnable implements Runnable {
            private boolean mFlag;
    
            public DeadRunnable(boolean flag) {
                this.mFlag = flag;
            }
    
            @Override
            public void run() {
                while (true) {
                    if (mFlag) {
                        //A锁
                        synchronized (ALock.mALock) {
                            System.out.println("A..............锁");
                            synchronized (BLock.mBLock) {
                                System.out.println("B..............锁");
                            }
                        }
                    } else {
                        //B锁
                        synchronized (BLock.mBLock) {
                            System.out.println("B..............锁");
                            synchronized (ALock.mALock) {
                                System.out.println("A..............锁");
    
                            }
                        }
                    }
                }
            }
        }
    
    
        /**
         * A锁
         */
        public static class ALock {
            public static final ALock mALock = new ALock();
        }
    
        /**
         * B锁
         */
        public static class BLock {
            public static final BLock mBLock = new BLock();
        }
    
    
        public static void main(String[] args) {
            new Thread(new DeadRunnable(true)).start();
            new Thread(new DeadRunnable(false)).start();
    
        }
    
    }
    
    

    多线程的一种程序的假死状态,同步的嵌套,停了,但是没退出,出现在多线程争抢同一个同步锁的时候,才会出现

    如有出入,望大佬扶正,谢谢!!!!

    相关文章

      网友评论

        本文标题:再学—Java基础:多线程(2)

        本文链接:https://www.haomeiwen.com/subject/chtdjqtx.html