再学—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-- + " 张");
            }
        }
    }
   }
}

执行效果图.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();

    }

}

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

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

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 215,539评论 6 497
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,911评论 3 391
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 161,337评论 0 351
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,723评论 1 290
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,795评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,762评论 1 294
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,742评论 3 416
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,508评论 0 271
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,954评论 1 308
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,247评论 2 331
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,404评论 1 345
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,104评论 5 340
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,736评论 3 324
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,352评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,557评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,371评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,292评论 2 352