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

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

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

  • 为什么会出现重复的数据和负数的数据的情况?
    因为多线程操作的是共享数据,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-- + " 张");
            }
        }
    }
   }
}

再学—Java基础:多线程(2)_第1张图片
执行效果图.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. 同步的弊端
    当线程相当多时,因为每个线程都会去判断同步上的锁,这是很耗费资源的,无形中会降低程序的运行效率,但是为了数据的安全也值了,鱼和熊掌不能兼得。
  • 两个线程实现交替打印(等待与唤醒机制)
/**
 * 两个线程 实现交替打印 一
 * 

* 输入线程 输出 一 *

* 输出线程 输出 二 */ 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))