zoukankan      html  css  js  c++  java
  • 多线程通讯(wait、notify、Lock、ThreadLocal)

    多线程之间通讯

    什么是多线程通讯?

    就是多个线程对同一个共享资源,进行不同的操作。

    介绍两个API中的方法,这两个是Object里面的方法:

    wait();等待,线程从运行状态变为休眠状态

    notify();唤醒,线程从休眠状态变为运行状态

    现在解决一下这样一个案例:

    两个线程,面向一个仓库进行读写操作,仓库里面用一个用户类表示,里面包括姓名和性别这两个属性,A线程往里面写,然后B线程立马读出来,这样交替执行,该怎么设计?

    分析一下这个题目:仓库里面是两个属性,两个线程同时对仓库进行操作,肯定要同步,不然会出现数据混乱问题,然后考虑的是让两个线程交替执行,A线程写完后要等待B线程读出以后在继续写,这时候要用到线程之间的通讯。wait和notify的使用必须与synchronized一起使用,wait包括释放锁,并进入阻塞队列这两个语义,这两步需要指定一个监视器来完成;notify是唤醒该线程,要想唤醒,首先需要知道该对象在哪儿,需要获取该对象的锁,才能去该对象对应的等待队列去唤醒一个线程,只有已经释放该对象锁的线程,才能被唤醒然后去竞争该对象锁。

    为了更好的看出效果,我让写线程奇数和偶数是写入不同的姓名和性别,看是否打印会出现数据混乱。

    代码如下:

    class User {
        String name;
        String sex;
        boolean flag = true;
    }
    class Write extends Thread {
        User user;
        public Write(User user) {
            this.user = user;
        }
        @Override
        public void run() {
            int count = 2;
            while (true) {
                synchronized (user) {
                    if (!user.flag) {
                        try {
                            user.wait();
                        } catch (InterruptedException e) {
                            // TODO Auto-generated catch block
                            e.printStackTrace();
                        }
                    }
                    if (count % 2 == 0) {
                        user.name = "周瑜";
                        user.sex = "男";
                    } else {
                        user.name = "小乔";
                        user.sex = "女";
                    }
                    count = (count + 1) % 2;
                    user.notify();
                    user.flag = false;
                }
            }
        }
    }
    class Read extends Thread {
        User user;
        public Read(User user) {
            this.user = user;
        }
        @Override
        public void run() {
            while (true) {
                synchronized (user) {
                    if (user.flag) {
                        try {
                            user.wait();
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                    System.out.println(user.name + "," + user.sex);
                user.notify();
                user.flag = true;
                }
            }
        }
    }
    public class OutInputDemo {
        public static void main(String[] args) {
            User user = new User();
            Write write = new Write(user);
            Read read = new Read(user);
            write.start();
            read.start();
        }
    }
    View Code

    wait和sleep的区别:

    wait位于同步中,需要释放锁的资源,需要被notify唤醒。

    sleep不释放锁的资源,时间到自然醒。

     Lock锁

    jdk1.5以后,并发包中新增了Lock接口及其相应的实现类来实现锁的功能,提供了和synchronized一样的同步功能,但是也有区别。

    Lock和synchronized的区别:

    synchronized是从代码开始上锁,代码结束释放锁,完全自动化,这种锁的效率低、扩展性不高。

    Lock锁属于手动的,手动上锁,手动释放锁,灵活性高

    在Lock中,不能使用wait和notify,取而代之的为:

    Condition   它的功能类似于Object.wait()和Object.notify()的功能。

    Condition condition = lock.newCondition();
    condition.await();//相当于wait
    condition.signal();//相当于notify

    上面的案例用Lock锁修改为:

    class User2 {
        String name;
        String sex;
        boolean flag = true;
        Lock lock = new ReentrantLock();
    }
    class Write2 extends Thread {
        User2 user;
        Condition condition;
        public Write2(User2 user,Condition condition) {
            this.user = user;
            this.condition = condition;
        }
        @Override
        public void run() {
            int count = 2;
            while (true) {
                try {
                    user.lock.lock();
                    if (!user.flag) {
                        try {
                            condition.await();
                        } catch (InterruptedException e) {
                            // TODO Auto-generated catch block
                            e.printStackTrace();
                        }
                    }
                    if (count % 2 == 0) {
                        user.name = "周瑜";
                        user.sex = "男";
                    } else {
                        user.name = "小乔";
                        user.sex = "女";
                    }
                    count = (count + 1) % 2;
                    condition.signal();
                    user.flag = false;
                } catch (Exception e) {
                    e.printStackTrace();
                } finally {
                    user.lock.unlock();
                }
            }
        }
    }
    class Read2 extends Thread {
        User2 user;
        Condition condition;
        public Read2(User2 user,Condition condition) {
            this.user = user;
            this.condition = condition;
        }
        @Override
        public void run() {
            while (true) {
                try {
                    user.lock.lock();
                    if (user.flag) {
                        try {
                            condition.await();
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                    System.out.println(user.name + "," + user.sex);
                    condition.signal();
                    user.flag = true;
                } catch (Exception e) {
                    e.printStackTrace();
                } finally {
                    user.lock.unlock();
                }
            }
        }
    }
    public class OutInputDemo2 {
        public static void main(String[] args) {
            User2 user = new User2();
            Condition condition = user.lock.newCondition();
            Write2 write = new Write2(user,condition);
            Read2 read = new Read2(user,condition);
            write.start();
            read.start();
        }
    }
    View Code

    怎么来停止线程???

    stop()???

    这个方法已经被弃用,不推荐使用,太暴力,不可恢复,就会导致不安全。

    我么使用interrupt来停止线程,API中还有Thread.currentThread().isInterrupted()来进行判断是否中断了线程,案例如下:

    class StopThreadDemo2 extends Thread{
        @Override
        public synchronized void run() {
            while(!Thread.currentThread().isInterrupted()){
                for (int i = 0; i < 30; i++) {
                    System.out.println(i);
                }
            }
            System.out.println("Thread is interrupt!");
        }
    }
    public class InterruptDemo {
        public static void main(String[] args) {
            StopThreadDemo2 stopThreadDemo = new StopThreadDemo2();
            stopThreadDemo.start();
            for (int i = 0; i < 10; i++) {
                if (i == 2) {
                    stopThreadDemo.interrupt();
                }
                System.out.println("主线程"+i);
            }
        }
    }

     ThreadLocal

    本地线程,为每一个线程提供一个局部变量。

    定义的变量不会共享,是自己的本地局部变量。

    看下面这个案例:

    class Number {
        int count = 0;
        public int getNumber() {
            count = count + 1;
            return count;
        }
    }
    class ThreadLocalThread extends Thread {
        Number number;
        public ThreadLocalThread(Number number) {
            this.number = number;
        }
        @Override
        public void run() {
            for (int i = 0; i < 3; i++) {
                System.out.println(Thread.currentThread().getName() + ":" + number.getNumber());
            }
        }
    }
    public class ThreadLocalDemo {
        public static void main(String[] args) {
            Number number1 = new Number();
            Number number2 = new Number();
            Number number3 = new Number();
            ThreadLocalThread threadLocalThread1 = new ThreadLocalThread(number1);
            ThreadLocalThread threadLocalThread2 = new ThreadLocalThread(number2);
            ThreadLocalThread threadLocalThread3 = new ThreadLocalThread(number3);
            threadLocalThread1.start();
            threadLocalThread2.start();
            threadLocalThread3.start();
        }
    }

     这个案例是三个线程分别用来生成自己的数字number,我们定义了三个Number对象,如果有100个线程,是不是需要定义100个number对象,该怎么解决这个问题呢???

    class Number {
        int count;
        public static ThreadLocal<Integer> threadLocal = new ThreadLocal<Integer>(){
            protected Integer initialValue() {//初始化threadLocal.get()的值
                return 0;
            };
        };
        public int getNumber() {
            count = threadLocal.get() + 1;
            threadLocal.set(count);//更新threadLocal里面的值
            return count;
        }
    }
    class ThreadLocalThread extends Thread {
        Number number;
        public ThreadLocalThread(Number number) {
            this.number = number;
        }
        @Override
        public void run() {
            for (int i = 0; i < 3; i++) {
                System.out.println(Thread.currentThread().getName() + ":" + number.getNumber());
            }
        }
    }
    public class ThreadLocalDemo {
        public static void main(String[] args) {
            Number number = new Number();
            ThreadLocalThread threadLocalThread1 = new ThreadLocalThread(number);
            ThreadLocalThread threadLocalThread2 = new ThreadLocalThread(number);
            ThreadLocalThread threadLocalThread3 = new ThreadLocalThread(number);
            threadLocalThread1.start();
            threadLocalThread2.start();
            threadLocalThread3.start();
        }
    }

    通过get()和set()进行对本地局部变量的更新。

    原理:Map集合存储

    get()源码解析:

     public T get() {
            Thread t = Thread.currentThread();//获取当前线程
            ThreadLocalMap map = getMap(t);//获取当前线程的ThreadLocalMap集合,
            if (map != null) {//判断是否存在该线程的Map集合
                ThreadLocalMap.Entry e = map.getEntry(this);//然后就判断该集合里面是否有该对象的值,有的话,就返回存在的值,没有就返回初始值
                if (e != null) {
                    @SuppressWarnings("unchecked")
                    T result = (T)e.value;
                    return result;
                }
            }
            return setInitialValue();
        }

    set()源码:

    public void set(T value) {//获取当前线程,看是否存在ThreadLocalMap,存在就直接放里面放值,不存在就创建一个ThreadLocalMap
            Thread t = Thread.currentThread();
            ThreadLocalMap map = getMap(t);
            if (map != null)
                map.set(this, value);
            else
                createMap(t, value);
        }
  • 相关阅读:
    Fegin参数使用总结
    navicat彻底卸载
    VM虚拟机win10无法联网,DNS配置问题
    Navicat15的安装及破解
    Docker 配置国内镜像源拉取prometheus,解决prometheus拉取特别慢的问题
    python中faker(生成随机数据)
    初探移动网站的架构和设计
    利用HTML5的一个重要特性 —— DeviceOrientation来实现手机网站上的摇一摇功能
    响应式Web设计(三):响应式Web设计的方法
    响应式Web设计(四):响应式Web设计的优化
  • 原文地址:https://www.cnblogs.com/javatalk/p/9924531.html
Copyright © 2011-2022 走看看