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);
        }
  • 相关阅读:
    HearthBuddy投降插件2019-11-01的使用
    正则表达式在线分析 regex online analyzer
    Tips to write better Conditionals in JavaScript
    The fileSyncDll.ps1 is not digitally signed. You cannot run this script on the current system.
    Cannot capture jmeter traffic in fiddler
    JMETER + POST + anti-forgery token
    input type color
    HearthBuddy修改系统时间
    What are all the possible values for HTTP “Content-Type” header?
    UDK性能优化
  • 原文地址:https://www.cnblogs.com/javatalk/p/9924531.html
Copyright © 2011-2022 走看看