java多线程笔记3

线程间通信

线程之间不是独立的个体,他们彼此间是可以互相通信和协作的。

wait/notify机制概念

服务员等厨师做好菜(wait),厨师做好菜通知服务员(notify)

wait/notiry机制原理

持有相同的锁的线程才可以实现通信机制。并且要在同步方法或代码块中执行wait方法是Object的方法,作用是让当前执行wait方法的线程等待,在wait所在的代码行处暂停执行,并且释放锁,直到接到通知或被中断为止。在调用wait之前,线程必须获得该对象的对象级别锁,即只能在同步方法或同步代码块中调用wait方法。通过通知机制使某个线程继续执行wati方法后面的代码时,对线程的选择是按照执行wait方法的顺序确定的,并且需要重新获取锁。如果调用wait时没有持适当的锁,则抛出IllegalMonitorStateException,他是RuntimeException的子类,并不需要try语句捕捉异常。
notiry方法要在同步方法或者同步块中调用,即在调用前,线程必须获得锁,如果调用notiry时没有持有适当的锁,则会抛出IllegalMonitorStateException。该方法用来通知那些可能等待该锁的其他线程,如果有多个线程等待,则按照执行wait方法的顺序对处于wait状态的线程发出一次通知norify,并使该线程重新获取锁。需要说明的是,notify方法后,当前线程不会立即释放锁,呈wait状态的线程也不能马上获得该对象锁,要等到执行notify方法所在线程的方法或者代码块执行完,即退出synchroinzed同步区域后,当前线程才会释放锁,而呈wait状态的线程才能获得该对象锁。当第一个获得了该对象的wait线程运行完毕后,他会释放该对象锁,此时如果没有再次使用notify语句,那么其他呈wait状态的线程因为没有得到notify通知,会继续处于wait状态。
总之:wait方法使线程暂停运行,notify方法通知暂停的线程继续运行。

wait方法的使用,wait方法的作用是使当前线程暂停运行,并释放锁(如果没有锁,报RuntimEexception)

1.错误演示

public class Test {
    public static void main(String[] args) {
        try {
            String s = new String();
            s.wait();
        }catch (InterruptedException e){
            e.printStackTrace();
        }
    }
}

结果:出现异常的原因是没有对象监视器,即没有锁
在这里插入图片描述
2.正确演示

public class Test {
    public static void main(String[] args) {
        try {
            String s = new String();
            synchronized (s){
                System.out.println("wait before");
                s.wait();
                System.out.println("wait end");
            }
        }catch (InterruptedException e){
            e.printStackTrace();
        }
    }
}

结果:
在这里插入图片描述

wait/notify方法组合使用

public class MyThread1 extends Thread{
    private Object lock;
    public MyThread1(Object lock) {
        this.lock = lock;
    }
    @Override
    public void run() {
        try {
            synchronized (lock){
                System.out.println("开始等待时间:"+System.currentTimeMillis());
                lock.wait();
                System.out.println("结束等待时间:"+System.currentTimeMillis());
            }
        }catch (InterruptedException e){
            e.printStackTrace();
        }
    }
}

public class MyThread2 extends Thread {
    private Object lock;
    public MyThread2(Object lock) {
        this.lock = lock;
    }
    @Override
    public void run() {
        synchronized (lock) {
            System.out.println("开始通知时间:" + System.currentTimeMillis());
            lock.notify();
            System.out.println("结束通知时间:" + System.currentTimeMillis());
        }
    }
}

public class Test {
    public static void main(String[] args) {
        try {
            Object lock = new Object();
            MyThread1 myThread1 = new MyThread1(lock);
            myThread1.start();
            Thread.sleep(3000);
            MyThread2 myThread2 = new MyThread2(lock);
            myThread2.start();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

结果:
线程myThread1获取锁lock,执行同步代码块,接着执行wait方法,线程暂停并释放lock锁,回到main线程,main线程接着睡眠3秒,3秒过后,由于myThread1是wait状态,所以myThread2执行代码,获取锁lock,执行同步代码块,执行notiry方法,由于synchronized是同步的,并不会立即释放lock锁,有wait方法的线程也不会立即得到lock锁,所以notify之后的代码要执行完,notify所在的线程才会释放锁,有wait方法的线程才会获取锁,然后执行wait方法之后的代码,myThread1执行wait之后的方法。
在这里插入图片描述

使用wait/notify机制实现list.size=5时,线程销毁

public class MyList {
    private static List list = new ArrayList<>();
    public static void add(){
        list.add("111");
    }
    public static int size(){
        return list.size();
    }
}

public class ThreadA extends Thread{
    private Object lock;
    public ThreadA(Object lock) {
        this.lock = lock;
    }
    @Override
    public void run() {
        try {
            synchronized (lock){
                if(MyList.size()!=5){
                    System.out.println("开始wait时间:"+System.currentTimeMillis());
                    lock.wait();
                    System.out.println("结束wait时间:"+System.currentTimeMillis());
                }
            }
        }catch (InterruptedException e){
            e.printStackTrace();
        }
    }
}

public class ThreadB extends Thread{
    private Object lock;
    public ThreadB(Object lock) {
        this.lock = lock;
    }
    @Override
    public void run() {
        synchronized (lock){
            for (int i = 0; i < 10; i++) {
                MyList.add();
                if(MyList.size()==5){
                    lock.notify();
                    System.out.println("notify");
                }
                System.out.println("添加了"+(i+1)+"个元素");
            }
        }
    }
}

public class Test {
    public static void main(String[] args) {
        try {
            Object lock = new Object();
            ThreadA threadA = new ThreadA(lock);
            threadA.start();
            Thread.sleep(1000);
            ThreadB threadB = new ThreadB(lock);
            threadB.start();
        }catch (InterruptedException e){
            e.printStackTrace();
        }
    }
}

结果:
1.notify方法执行有不立即释放锁,而是继续执行后面的代码。java中Object都实现类wait和notify方法。
2.wait方法可以使调用该方法的线程释放锁,然后从运行状态转成wait状态,等待被唤醒,
3.notify通知暂停的线程继续运行,但是notify方法执行后并不能立即释放锁,只通知一个线程,唤醒的顺序和执行wait方法的顺序一致。
4.notifyAll:按照执行wait方法的倒序依次对其他线程进行唤醒。
5.wait(long),等待一段时间,超过这个时间自动唤醒,但是不是绝对的时间,要持有锁之后才可以。
在这里插入图片描述

线程状态的切换(待研究)

创建:new
运行:start、notify
暂停:wait、sleep
销毁:stop

Object处理线程的方法

1.wait

立即释放锁

2.sleep

不释放锁

3.notify

不立即释放锁,只通知一个线程

public class MyService {
    private Object lock = new Object();
    public void waitMethod(){
        try {
            synchronized (lock){
                System.out.println(Thread.currentThread().getName()+"开始wait:"+System.currentTimeMillis());
                lock.wait();
                System.out.println(Thread.currentThread().getName()+"结束wait:"+System.currentTimeMillis());
            }
        }catch (InterruptedException e){
            e.printStackTrace();
        }
    }
    public void notifyMethod(){
        synchronized (lock){
            System.out.println(Thread.currentThread().getName()+"开始notify:"+System.currentTimeMillis());
            lock.notify();
            System.out.println(Thread.currentThread().getName()+"结束notify:"+System.currentTimeMillis());
        }
    }
}

public class MyThreadWait extends Thread{
    private MyService myService;
    public MyThreadWait(MyService myService) {
        this.myService = myService;
    }
    @Override
    public void run() {
        myService.waitMethod();
    }
}

public class MyThreadNotify extends Thread{
    private MyService myService;
    public MyThreadNotify(MyService myService) {
        this.myService = myService;
    }
    @Override
    public void run() {
        myService.notifyMethod();
//        myService.notifyAllMethod();
    }
}

public class Test {
    public static void main(String[] args) throws InterruptedException {
        MyService myService = new MyService();
        for (int i = 0; i < 10; i++) {
            MyThreadWait myThreadWait = new MyThreadWait(myService);
            myThreadWait.start();
        }
        Thread.sleep(1000);
        MyThreadNotify myThreadNotify1 = new MyThreadNotify(myService);
        myThreadNotify1.start();
        Thread.sleep(500);
        MyThreadNotify myThreadNotify2 = new MyThreadNotify(myService);
        myThreadNotify2.start();
        Thread.sleep(500);
        MyThreadNotify myThreadNotify3 = new MyThreadNotify(myService);
        myThreadNotify3.start();
        Thread.sleep(500);
        MyThreadNotify myThreadNotify4 = new MyThreadNotify(myService);
        myThreadNotify4.start();
        Thread.sleep(500);
        MyThreadNotify myThreadNotify5 = new MyThreadNotify(myService);
        myThreadNotify5.start();
        Thread.sleep(500);
//        MyThreadNotify myThreadNotify = new MyThreadNotify(myService);
//        myThreadNotify.start();
    }
}

结果:
wait的顺序0、9、8、7、4,notify后wait唤醒的顺序还是0、9、8、7、4
在这里插入图片描述

4.notifyAll

通知所有线程唤醒,上述3中,要唤醒5个线程,需要写5个唤醒线程,当不知道系统有多少线程要唤醒的时候,就用notifyAll
在这里插入图片描述

5.wait(long)

等待一段时间被唤醒,如果超出这个时间,自动唤醒,自动唤醒的前提是要重新获取锁lock,如果超出这个时间,lock依旧没有获取,则等待。

6.如果先执行了notify后执行wait,则wait不会被唤醒。因此wait方法不需要被执行,代码里面应有相应的处理逻辑来判断是否是notify先执行。如果notify先执行,则wait方法不需要执行。

生产者与消费者(待研究)

1.一生产与一消费:操作值
public class ValueObj {
    public static String value = "";
}

public class Produce {
    private String lock;

    public Produce(String lock) {
        this.lock = lock;
    }
    public void setValue(){
        try {
            synchronized (lock){
                if(!ValueObj.value.equals("")){
                    lock.wait();
                }
                String value = System.currentTimeMillis()+"";
                System.out.println("set:"+value);
                ValueObj.value = value;
                lock.notify();
            }
        }catch (InterruptedException e){
            e.printStackTrace();
        }
    }
}

public class Consume {
    private String lock;

    public Consume(String lock) {
        this.lock = lock;
    }
    public void getValue(){
        try {
            synchronized (lock){
                if(ValueObj.value.equals("")){
                    lock.wait();
                }
                System.out.println("get:"+System.currentTimeMillis()+"");
                ValueObj.value="";
                lock.notify();
            }
        }catch (InterruptedException e){
            e.printStackTrace();
        }
    }
}

public class ThreadProduce extends Thread{
    private Produce produce;

    public ThreadProduce(Produce produce) {
        this.produce = produce;
    }

    @Override
    public void run() {
        while (true){
            produce.setValue();
        }
    }
}

public class ThreadConsume extends Thread{
    private Consume consume;

    public ThreadConsume(Consume consume) {
        this.consume = consume;
    }

    @Override
    public void run() {
        while(true){
            consume.getValue();
        }
    }
}

public class Test {
    public static void main(String[] args) {
        String lock = new String("");
        Produce produce = new Produce(lock);
        Consume consume = new Consume(lock);
        ThreadProduce threadProduce = new ThreadProduce(produce);
        ThreadConsume threadConsume = new ThreadConsume(consume);
        threadProduce.start();
        threadConsume.start();
    }
}

结果:一个生产者和一个消费者相互通知,交替运行。
在这里插入图片描述

线程间通信:管道流(待研究)

PipedInputStream,PipedOutputStream,PipedReader,PipedWriter。

join方法(待研究)

很多情况下,主线程创建子线程并启动子线程,如果子线程要进行大量的耗时运算,可能主线程先销毁,子线程再销毁。如果主线程想等子线程销毁之后再销毁,则需要join方法。
添加ljoin相当于串行执行代码
join(long) 等待时长,和sleep类似,

public class MyThread extends Thread{
    @Override
    public void run() {
        try {
            System.out.println("子线程的值");
            Thread.sleep(2000);
        }catch (InterruptedException e){
            e.printStackTrace();
        }
    }
}

public class Test {
    public static void main(String[] args) {
        MyThread myThread = new MyThread();
        myThread.start();
        System.out.println("主线程的值");
    }
}

结果:
在这里插入图片描述
如果,想先拿到子线程的值再执行主线程,则使用join方法
在myThread.start()后面添加myThread.join()
结果:join的作用是使所属线程对象myThread正常执行run方法的任务,而使当前线程myThread进行无限期阻塞,等待线程myThread销毁后再继续执行myThread后面的代码。
join方法具有使线程排队运行的效果。
在这里插入图片描述

join方法与interrupt彼此遇到,不管先后顺序执行,当前线程出现异常

join(long time)

thread.join(long)方法中的参数用于设置等待时间,不管线程是否执行完毕,时间到了就尝试重新获取锁,获取成功,则当前线程会继续向后执行,没有获取成功,则一直尝试,直到获取成功为止。

public class MyThread extends Thread{
    @Override
    public void run() {
        try {
            System.out.println("子线程开始时间:"+System.currentTimeMillis());
            Thread.sleep(5000);
            System.out.println("子线程结束时间:"+System.currentTimeMillis());
        }catch (InterruptedException e){
            e.printStackTrace();
        }
    }
}

public class Test {
    public static void main(String[] args) {
        try {
            MyThread myThread = new MyThread();
            myThread.start();
            System.out.println("main开始时间:"+System.currentTimeMillis());
            myThread.join(2000);
//            Thread.sleep(2000);
            System.out.println("main结束时间:"+System.currentTimeMillis());
        }catch (InterruptedException e){
            e.printStackTrace();
        }
    }
}

结果:main线程与子线程同时执行,main线程抢占cpu执行打印,
子线程开始执行,打印、sleep(5000),接着执行join(2000),由于join正常执行run方法。2秒之后,子线程重新获取锁,执行三秒,然后子线程结束,打印信息。
在这里插入图片描述
如果是sleep方法
结果:
在这里插入图片描述

join(long)与sleep(long)

join(long)方法的功能在内部是使用wait(long)方法来实现的,所以join方法具有释放锁的特点。当执行wait时,当前线程的锁被释放,其他线程可以调用此线程的同步方法。
sleep方法会一直持有锁,并且没有释放锁的特点。
在这里插入图片描述

证明sleep方法具有不释放锁的特点

public class ThreadA extends Thread{
    private ThreadB threadB;

    public ThreadA(ThreadB threadB) {
        this.threadB = threadB;
    }

    @Override
    public void run() {
        try {
            synchronized (threadB){
                threadB.start();
                Thread.sleep(10000);
            }
        }catch (InterruptedException e){
            e.printStackTrace();
        }
    }
}

public class ThreadB extends Thread{
    @Override
    public void run() {
        try {
            System.out.println("B run begin:"+System.currentTimeMillis());
            Thread.sleep(5000);
            System.out.println("B run end :"+System.currentTimeMillis());
        }catch (InterruptedException e){
            e.printStackTrace();
        }
    }
    synchronized public void bService(){
        System.out.println("B bService :"+System.currentTimeMillis());
    }
}

public class ThreadC extends Thread{
    private ThreadB threadB;

    public ThreadC(ThreadB threadB) {
        this.threadB = threadB;
    }

    @Override
    public void run() {
        threadB.bService();
    }
}

public class Test {
    public static void main(String[] args) {
        try {
            ThreadB threadB = new ThreadB();
            ThreadA threadA = new ThreadA(threadB);
            threadA.start();
            Thread.sleep(1000);
            ThreadC threadC = new ThreadC(threadB);
            threadC.start();
        }catch (InterruptedException e){
            e.printStackTrace();
        }
    }
}

结果:线程threadA在执行sleep10秒的时候,一直持有threadB对象锁,线程threadB执行自己的代码块,时间到达10秒后,threadA释放锁,threadC获得threadB对象锁,才能执行同步方法。
在这里插入图片描述

证明join方法(有参,无参都可以)有释放锁的特点

在上一小节ThreadA中的 threadB.start();后面添加一行
threadB.join();
结果:由于线程threadA释放了线程threadB对象锁,线程threadC可以获得threadB对象锁,并且执行线程threadB的同步方法。
在这里插入图片描述

ThreadLocal类:

变量共享:所有线程公用一个变量;
ThreadLocal用于解决每个线程都有自己的私有变量。

每个线程都有自己的变量,类ThreadLocal的作用是将数据放入当前线程对象的Map中,该Map是Thread类的实例变量,类ThreadLocal自己不管理、不存储任何数据,他只是数据和Map的桥梁,用于将数据放入Map中。
执行流程:数据-》ThreadLocal-》currentThread-》Map
执行后每个线程中的Map存有自己的数据,Map的key存的是ThreadLocal对象,value是存储的值。每个Thread中Map的值只对当前线程可见,其他线程不可以访问当前线程对象中Map的值。当前线程销毁,Map随之销毁,Map中的数据如果没有被引用、没有被使用,则随时被GC回收。
线程、Map、数据之间的关系类比:
人(Thread)随身带有兜子(Map),兜子(Map)里面有东西(value),这样,Thread随身就有自己的数据,随时可以访问自己的数据。
Map的key不可以重复,一个ThreadLocal对象对应一个value。

可以新建一个类继承ThreadLocal来初始化默认值。initialValue方法。

InheritableThreadLocal可以实现继承值得特性。

必须使用可变对象数据类型(String不可变),才能使子父线程间相互感应属性值变化。

Lock对象的使用

lock比synchronized更加丰富
ReentrantLock类
ReentrantReadWriteLock类

使用ReentrantLock实现同步

public class MyService {
    private Lock lock = new ReentrantLock();
    public void tentM(){
        lock.lock();
        for (int i = 0; i < 2; i++) {
            System.out.println("线程:"+Thread.currentThread().getName()+":"+(i+1));
        }
        lock.unlock();
    }
}

public class MyThread extends Thread{
    private MyService service;

    public MyThread(MyService service) {
        this.service = service;
    }

    @Override
    public void run() {
        service.tentM();
    }
}

public class Test {
    public static void main(String[] args) {
        MyService myService = new MyService();
        MyThread myThread1 = new MyThread(myService);
        MyThread myThread2 = new MyThread(myService);
        MyThread myThread3 = new MyThread(myService);
        MyThread myThread4 = new MyThread(myService);
        MyThread myThread5 = new MyThread(myService);
        myThread1.start();
        myThread2.start();
        myThread3.start();
        myThread4.start();
        myThread5.start();
    }
}

结果:调用ReentrantLock对象的lock方法获取锁,调用unlock方法释放锁,这两个方法成对使用。
在这里插入图片描述

ReentrantLock多代码块间的同步性

public class MyService {
    private Lock lock = new ReentrantLock();
    public void tentA(){
        try {
            lock.lock();
            System.out.println("testA begin:"+Thread.currentThread().getName()+":"+System.currentTimeMillis());
            Thread.sleep(2000);
            System.out.println("testA end:"+Thread.currentThread().getName()+":"+System.currentTimeMillis());
        }catch (InterruptedException e){
            e.printStackTrace();
        }finally {
            lock.unlock();
        }
    }

    public void tentB(){
        try {
            lock.lock();
            System.out.println("testB begin:"+Thread.currentThread().getName()+":"+System.currentTimeMillis());
            Thread.sleep(2000);
            System.out.println("testB end:"+Thread.currentThread().getName()+":"+System.currentTimeMillis());
        }catch (InterruptedException e){
            e.printStackTrace();
        }finally {
            lock.unlock();
        }
    }
}

public class MyThreadA extends Thread{
    private MyService service;

    public MyThreadA(MyService service) {
        this.service = service;
    }

    @Override
    public void run() {
        service.tentA();
    }
}

public class MyThreadAA extends Thread{
    private MyService service;

    public MyThreadAA(MyService service) {
        this.service = service;
    }

    @Override
    public void run() {
        service.tentA();
    }
}

public class MyThreadB extends Thread{
    private MyService service;

    public MyThreadB(MyService service) {
        this.service = service;
    }

    @Override
    public void run() {
        service.tentB();
    }
}

public class MyThreadBB extends Thread{
    private MyService service;

    public MyThreadBB(MyService service) {
        this.service = service;
    }

    @Override
    public void run() {
        service.tentB();
    }
}

public class Test {
    public static void main(String[] args) {
        MyService myService = new MyService();
        try {
            MyThreadA threadA = new MyThreadA(myService);
            threadA.setName("threadA");
            threadA.start();
            MyThreadAA myThreadAA = new MyThreadAA(myService);
            myThreadAA.setName("threadAA");
            myThreadAA.start();
            Thread.sleep(1000);
            MyThreadB threadB = new MyThreadB(myService);
            threadB.setName("threadB");
            threadB.start();
            MyThreadBB myThreadBB = new MyThreadBB(myService);
            myThreadBB.setName("threadBB");
            myThreadBB.start();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

结果:与synchronized效果一样
在这里插入图片描述

使用await方法和signal方法实现wait/notiry机制

public class MyService {
    private Lock lock = new ReentrantLock();
    public Condition condition = lock.newCondition();
    public void await(){
        try {
            lock.lock();
            System.out.println("开始await:"+System.currentTimeMillis());
            condition.await();
        }catch (InterruptedException e){
            e.printStackTrace();
        }finally {
            lock.unlock();
        }
    }
    public void signal(){
            lock.lock();
            System.out.println("开始signal:"+System.currentTimeMillis());
            condition.signal();
            lock.unlock();
    }
}

public class MyThread extends Thread{
    private MyService service;

    public MyThread(MyService service) {
        this.service = service;
    }

    @Override
    public void run() {
        service.await();
    }
}

public class Test {
    public static void main(String[] args) throws InterruptedException {
        MyService myService = new MyService();
        MyThread myThread = new MyThread(myService);
        myThread.start();
        Thread.sleep(2000);
        myService.signal();
    }
}

结果:
Object中的方法wait、wait(long)、notify()、notifyAll()
=Condition中的方法await、await(long)、signal()、signalAll()
在这里插入图片描述

通知部分线程

public class MyService {
    private Lock lock = new ReentrantLock();
    private Condition conditionA = lock.newCondition();
    private Condition conditionB = lock.newCondition();
    public void awaitA(){
        try {
            lock.lock();
            System.out.println("开始awaitA:"+System.currentTimeMillis()+":"+Thread.currentThread().getName());
            conditionA.await();
            System.out.println("结束awaitA:"+System.currentTimeMillis()+":"+Thread.currentThread().getName());
        }catch (InterruptedException e){
            e.printStackTrace();
        }finally {
            lock.unlock();
        }
    }
    public void awaitB(){
        try {
            lock.lock();
            System.out.println("开始awaitB:"+System.currentTimeMillis()+":"+Thread.currentThread().getName());
            conditionB.await();
            System.out.println("结束awaitB:"+System.currentTimeMillis()+":"+Thread.currentThread().getName());
        }catch (InterruptedException e){
            e.printStackTrace();
        }finally {
            lock.unlock();
        }
    }
    public void signalAll_A(){
        lock.lock();
        System.out.println("开始signalAll_A:"+System.currentTimeMillis()+":"+Thread.currentThread().getName());
        conditionA.signalAll();
        lock.unlock();
    }
    public void signalAll_B(){
        lock.lock();
        System.out.println("开始signalAll_B:"+System.currentTimeMillis()+":"+Thread.currentThread().getName());
        conditionB.signalAll();
        lock.unlock();
    }
}

public class MyThreadA extends Thread{
    private MyService service;

    public MyThreadA(MyService service) {
        this.service = service;
    }

    @Override
    public void run() {
        service.awaitA();
    }
}

public class MyThreadB extends Thread{
    private MyService service;

    public MyThreadB(MyService service) {
        this.service = service;
    }

    @Override
    public void run() {
        service.awaitB();
    }
}

public class Test {
    public static void main(String[] args) {
        MyService myService = new MyService();
        try {
            MyThreadA threadA = new MyThreadA(myService);
            threadA.setName("threadA");
            threadA.start();
            MyThreadB threadB = new MyThreadB(myService);
            threadB.setName("threadB");
            threadB.start();
            Thread.sleep(1000);
            myService.signalAll_A();
            Thread.sleep(2000);
            myService.signalAll_B();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

结果:
在这里插入图片描述

生产者-消费者:一对一

public class MyService {
    private ReentrantLock lock = new ReentrantLock();
    private Condition condition = lock.newCondition();
    private boolean hasValue = false;
    public void set(){
        try {
            lock.lock();
            if(hasValue==true){
                condition.await();
            }
            System.out.println("setValue");
            hasValue = true;
            condition.signal();
        }catch (InterruptedException e){
            e.printStackTrace();
        }finally {
            lock.unlock();
        }
    }
    public void get(){
        try {
            lock.lock();
            if(hasValue==false){
                condition.await();
            }
            System.out.println("getValue");
            hasValue=false;
            condition.signal();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }
}

public class MyThreadA extends Thread{
    private MyService service;

    public MyThreadA(MyService service) {
        this.service = service;
    }

    @Override
    public void run() {
        for (int i = 0; i < 5; i++) {
            service.set();
        }
    }
}

public class MyThreadB extends Thread{
    private MyService service;

    public MyThreadB(MyService service) {
        this.service = service;
    }

    @Override
    public void run() {
        for (int i = 0; i < 5; i++) {
            service.get();
        }
    }
}

public class Test {
    public static void main(String[] args) {
        MyService myService = new MyService();
        MyThreadA myThreadA = new MyThreadA(myService);
        myThreadA.start();
        MyThreadB myThreadB = new MyThreadB(myService);
        myThreadB.start();
    }
}

结果:
在这里插入图片描述

生产者与消费者:多对多

public class MyService {
    private ReentrantLock lock = new ReentrantLock();
    private Condition condition = lock.newCondition();
    private boolean hasValue = false;
    public void set(){
        try {
            lock.lock();
            while (hasValue==true){
                System.out.println("进入set await");
                condition.await();
            }
            System.out.println("setValue");
            hasValue = true;
            condition.signal();
        }catch (InterruptedException e){
            e.printStackTrace();
        }finally {
            lock.unlock();
        }
    }
    public void get(){
        try {
            lock.lock();
            while (hasValue==false){
                System.out.println("进入get await");
                condition.await();
            }
            System.out.println("getValue");
            hasValue=false;
            condition.signal();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }
}

public class MyThreadA extends Thread{
    private MyService service;

    public MyThreadA(MyService service) {
        this.service = service;
    }

    @Override
    public void run() {
        for (int i = 0; i < 5; i++) {
            service.set();
        }
    }
}

public class MyThreadB extends Thread{
    private MyService service;

    public MyThreadB(MyService service) {
        this.service = service;
    }

    @Override
    public void run() {
        for (int i = 0; i < 5; i++) {
            service.get();
        }
    }
}

public class Test {
    public static void main(String[] args) {
        MyService myService = new MyService();
        MyThreadA[] myThreadA = new MyThreadA[3];
        MyThreadB[] myThreadB = new MyThreadB[3];

        for (int i = 0; i < 3; i++) {
            myThreadA[i] = new MyThreadA(myService);
            myThreadB[i] = new MyThreadB(myService);
            myThreadA[i].start();
            myThreadB[i].start();
        }
    }
}

结果:
在这里插入图片描述

公平锁与非公平锁

公平锁ReentraintLock(true):先到先得,每次获取锁之前都会检查队列中是否有排队等待的线程,没有才尝试获取锁,如果有则将当前线程加入到队列中。
非公平锁ReentraintLock(false):采用插队策略,一个线程获取锁之前首先去尝试获取锁而不是在队列中等待,如果获取成功,则插队成功,否则才将当前线程加入到队列中。

ReentrantLock的方法

ReentrantLock lock = new ReentrantLock();

1.public int getHoldCount()

查询当前线程保持此锁的个数,即调用lock方法的次数。执行lock,count+1,执行unlock,count-1

2.public int getWaitQueueLength(Condition condition)

返回等待与此锁相关的给定条件Condition的线程估计数,有几个对象同时执行了condigion的await方法,就返回几。

3.public final boolean hasQueuedThread(Thread thread)

查询指定的线程释放在等待获取此锁,即判断参数中的线程是否在等待队列中。

4.public final boolean hasQueuedThreads()

查询是否有线程在等待获取此锁,即等待队列中是否有等待的线程。

。。。。

ReentrantLock类 互斥锁,效率低,ReentrantReadWriteLock 读写锁效率高

ReentrantLock类具有完全互斥排他的效果,同一时间只有一个线程在执行ReentrantLock.lock()方法后面的任务,这样虽然保证了同时写实例变量的线程安全性,但效率很低。
所以出现了ReentrantReadWriteLock类,读操作不需要同步执行,提升运行速度。
读写锁有两个锁:一个是读操作的共享锁,另一个是写操作的排他锁。读锁之间不互斥,读锁与写锁互斥,写锁与写锁互斥,因此只要出现写锁,就会出现互斥同步的效果,读操作是指读取实例变量的值,写操作是指向实例变量写入值。

其他

public class MyService {
    private Lock lock = new ReentrantLock();
    public void test(){
        lock.lock();
        for (int i = 0; i < 5; i++) {
            System.out.println(Thread.currentThread().getName()+"---"+i);
        }
        lock.unlock();
    }
}
public class MyThread extends Thread {
    private MyService myService;
    public MyThread(MyService myService){
        super();
        this.myService = myService;
    }
    @Override
    public void run() {
        myService.test();
    }
}
public class Run {
    public static void main(String[] args) {
        MyService myService = new MyService();
        MyThread myThread1 = new MyThread(myService);
        MyThread myThread2 = new MyThread(myService);
        MyThread myThread3 = new MyThread(myService);
        MyThread myThread4 = new MyThread(myService);
        MyThread myThread5 = new MyThread(myService);
        myThread1.start();
        myThread2.start();
        myThread3.start();
        myThread4.start();
        myThread5.start();
    }
}

在这里插入图片描述
线程顺序可以乱,但是线程体里面的for语句没有打乱。

线程等待与唤醒:借助Condition对象实现

public class MyService {
    private Lock lock = new ReentrantLock();
    private Condition condition = lock.newCondition();
    public void test(){
        try {
            lock.lock();
            System.out.println("线程等待");
            condition.await();
            System.out.println("线程唤醒");
        }catch (InterruptedException e){
            e.printStackTrace();
        }finally {
            lock.unlock();
            System.out.println("解锁");
        }
    }
}
public class MyThread extends Thread {
    private MyService myService;
    public MyThread(MyService myService){
        super();
        this.myService = myService;
    }
    @Override
    public void run() {
        myService.test();
    }
}
public class Run {
    public static void main(String[] args) {
        MyService myService = new MyService();
        MyThread myThread1 = new MyThread(myService);
        MyThread myThread2 = new MyThread(myService);
        MyThread myThread3 = new MyThread(myService);
        MyThread myThread4 = new MyThread(myService);
        MyThread myThread5 = new MyThread(myService);
        myThread1.start();
        myThread2.start();
        myThread3.start();
        myThread4.start();
        myThread5.start();
    }
}

在这里插入图片描述
唤醒

public class MyService {
    private Lock lock = new ReentrantLock();
    private Condition condition = lock.newCondition();
    public void await(){
        try {
            lock.lock();
            System.out.println("线程"+Thread.currentThread().getName()+"等待");
            condition.await();
            System.out.println("线程"+Thread.currentThread().getName()+"继续执行");
        }catch (InterruptedException e){
            e.printStackTrace();
        }finally {
            lock.unlock();
        }
    }
    public void signal(){
        try {
            lock.lock();
            System.out.println("线程"+Thread.currentThread().getName()+"唤醒线程");
            condition.signal();
        }finally {
            lock.unlock();
        }
    }
}
public class MyThread extends Thread {
    private MyService myService;
    public MyThread(MyService myService){
        super();
        this.myService = myService;
    }
    @Override
    public void run() {
        myService.await();
    }
}
public class Run {
    public static void main(String[] args) throws InterruptedException {
        MyService myService = new MyService();
        MyThread myThread = new MyThread(myService);
        myThread.start();
        Thread.sleep(3000);
        myService.signal();
    }
}

在这里插入图片描述

await暂停线程原理

在这里插入图片描述
在这里插入图片描述
测试park方法

public class Run {
    public static void main(String[] args) throws InterruptedException, NoSuchFieldException, IllegalAccessException {
        Field f = Unsafe.class.getDeclaredField("theUnsafe");
        f.setAccessible(true);
        Unsafe unsafe = (Unsafe)f.get(null);
        System.out.println("begin"+System.currentTimeMillis());
//   1     unsafe.park(true,System.currentTimeMillis()+3000);  //毫秒写法
//   2     unsafe.park(true,0l);  //毫秒写法
//   3     unsafe.park(false,3000000000l);  //纳秒写法
//   4     unsafe.park(false,0l);  //纳秒写法
        System.out.println("  end"+System.currentTimeMillis());
    }
}

1234分别测试,发现除了4 会出现程序暂停运行,其他正常运行,这与UNSAFE.park(false, 0L);一样

分组唤醒

public class MyService {
    private Lock lock = new ReentrantLock();
    private Condition conditionA = lock.newCondition();
    private Condition conditionB = lock.newCondition();
    public void awaitA(){
        try {
            lock.lock();
            System.out.println("线程A"+"等待");
            conditionA.await();
            System.out.println("线程A"+"被唤醒");
        }catch (InterruptedException e){
            e.printStackTrace();
        }finally {
            lock.unlock();
        }
    }
    public void awaitB(){
        try {
            lock.lock();
            System.out.println("线程B"+"等待");
            conditionB.await();
            System.out.println("线程B"+"被唤醒");
        }catch (InterruptedException e){
            e.printStackTrace();
        }finally {
            lock.unlock();
        }
    }
    public void signalA(){
        try {
            lock.lock();
            System.out.println("线程"+Thread.currentThread().getName()+"唤醒线程A");
            conditionA.signal();
        }finally {
            lock.unlock();
        }
    }
    public void signalB(){
        try {
            lock.lock();
            System.out.println("线程"+Thread.currentThread().getName()+"唤醒线程B");
            conditionB.signal();
        }finally {
            lock.unlock();
        }
    }
}
public class MyThreadA extends Thread {
    private MyService myService;
    public MyThreadA(MyService myService){
        super();
        this.myService = myService;
    }
    @Override
    public void run() {
        myService.awaitA();
    }
}
public class MyThreadB extends Thread{
    private MyService myService;
    public MyThreadB(MyService myService){
        super();
        this.myService = myService;
    }
    @Override
    public void run() {
        myService.awaitB();
    }
}
public class Run {
    public static void main(String[] args) throws InterruptedException {
        MyService myService = new MyService();
        MyThreadA myThreadA = new MyThreadA(myService);
        myThreadA.start();
        MyThreadB myThreadB = new MyThreadB(myService);
        myThreadB.start();
        Thread.sleep(2000);
        myService.signalB();
    }
}

在这里插入图片描述

生产者消费者

public class MyService {
    private Lock lock = new ReentrantLock();
    private Condition condition = lock.newCondition();
    private boolean hasValue = false;   /*是否有货*/
    public void set(){
        try {
            lock.lock();
            if(hasValue==true){  //如果有货,不生产
                condition.await();
            }
            System.out.println("生产一个货物");
            hasValue = true;/*表示有货*/
            condition.signal();
        }catch (InterruptedException e){
            e.printStackTrace();
        }finally {
            lock.unlock();
        }
    }
    public void get(){
        try {
            lock.lock();
            if(hasValue==false){  //如果没货,不购买
                condition.await();
            }
            System.out.println("买到一个货物");
            hasValue = false;/*表示没货*/
            condition.signal();
        }catch (InterruptedException e){
            e.printStackTrace();
        }finally {
            lock.unlock();
        }
    }
}

public class MyThreadA extends Thread {
    private MyService myService;
    public MyThreadA(MyService myService){
        super();
        this.myService = myService;
    }
    @Override
    public void run() {
        for (int i = 0; i < 1000; i++) {
            myService.set();
        }
    }
}
public class MyThreadB extends Thread{
    private MyService myService;
    public MyThreadB(MyService myService){
        super();
        this.myService = myService;
    }
    @Override
    public void run() {
        for (int i = 0; i < 1000; i++) {
            myService.get();
        }
    }
}

public class Run {
    public static void main(String[] args) throws InterruptedException {
        MyService myService = new MyService();
        MyThreadA myThreadA = new MyThreadA(myService);
        myThreadA.start();
        MyThreadB myThreadB = new MyThreadB(myService);
        myThreadB.start();
    }
}

在这里插入图片描述

多生产对多消费

public class MyService {
    private Lock lock = new ReentrantLock();
    private Condition condition = lock.newCondition();
    private boolean hasValue = false;   /*是否有货*/
    private Integer count = 0;
    private Integer con = 0;
    public void set(){
        try {
            lock.lock();
            while (hasValue==true){//如果有货,也可以继续生产
                System.out.println("生产第"+count+1);
                count++;
                condition.await();
            }
            System.out.println("生产第"+count+1);
            count++;
            if(count>0){
                hasValue = true;/*表示有货*/
            }
            condition.signal();
        }catch (InterruptedException e){
            e.printStackTrace();
        }finally {
            lock.unlock();
        }
    }
    public void get(){
        try {
            lock.lock();
            while (hasValue==false){//如果没货,不购买
                condition.await();
            }
            System.out.println("消费第"+con+1);
            con++;
            if(con>=count){
                hasValue = false;/*表示没货*/
            }
            condition.signal();
        }catch (InterruptedException e){
            e.printStackTrace();
        }finally {
            lock.unlock();
        }
    }
}
public class MyThreadA extends Thread {
    private MyService myService;
    public MyThreadA(MyService myService){
        super();
        this.myService = myService;
    }
    @Override
    public void run() {
        for (int i = 0; i < 1000; i++) {
            myService.set();
        }
    }
}
public class MyThreadB extends Thread{
    private MyService myService;
    public MyThreadB(MyService myService){
        super();
        this.myService = myService;
    }
    @Override
    public void run() {
        for (int i = 0; i < 1000; i++) {
            myService.get();
        }
    }
}

public class Run {
    public static void main(String[] args) throws InterruptedException {
        MyService myService = new MyService();
        MyThreadA[] myThreadAS =  new MyThreadA[10];
        MyThreadB[] myThreadBS =  new MyThreadB[10];
        for (int i = 0; i < 10; i++) {
            myThreadAS[i] = new MyThreadA(myService);
            myThreadBS[i] = new MyThreadB(myService);
            myThreadAS[i].start();
            myThreadBS[i].start();
        }
    }
}

在这里插入图片描述

公平锁ReentrantLock(true)与非公平锁ReentrantLock(false)默认true

公平锁:先到先得,每次获取锁之前检查队列里面有没有排队等待的线程,没有才会尝试获取锁,有就将当前线程追加到队列中。
非公平锁:可以插队获取锁。先尝试获取锁,如果获取不到再加入队列。

其他方法

getHoldCount() 当前线程锁的个数,执行了几次lock
getQueueLength() 返回正在等待获取锁的线程估计数
getWaitQueueLength(Condition condition) 返回等待与此锁相关给定条件condition的线程估计数
boolean hasQueuedThread(Thread thread) 返回指定线程是否正在等待获取锁,是否在等待队列中。
boolean hasQueuedThreads() 是否有线程在等待队列中。

isLocked() 此锁是否有其他线程持有
tryLock() 如果发现其他线程持有该锁,返回false ,继续执行其他代码
tryLock(long timeout,TimeUnit unit) 如果发现其他线程持有该锁,返回false ,如果当前线程在指定时间内拿到锁,返回true,超时返回false,继续执行其他代码
await(long time ,TimeUnit unit),自动唤醒
awaitNanos(long time),自动唤醒 纳秒
awaitUntil(Date deadline) 指定的时间结束等待

线程按顺序执行业务

public class MyService {
    private Lock lock = new ReentrantLock();
    private Condition condition = lock.newCondition();
    volatile private int next = 1;
    public void testA(){
        try {
            lock.lock();
            while (next!=1){
                condition.await();
            }
            System.out.println(1);
            next = 2;
            condition.signalAll();
            lock.unlock();
        }catch (InterruptedException e){
            e.printStackTrace();
        }
    }
    public void testB(){
        try {
            lock.lock();
            while (next!=2){
                condition.await();
            }
            System.out.println(2);
            next = 1;
            condition.signalAll();
            lock.unlock();
        }catch (InterruptedException e){
            e.printStackTrace();
        }
    }
}
public class MyThreadA extends Thread {
    private MyService myService;
    public MyThreadA(MyService myService){
        super();
        this.myService = myService;
    }
    @Override
    public void run() {
        myService.testA();
    }
}
public class MyThreadB extends Thread{
    private MyService myService;
    public MyThreadB(MyService myService){
        super();
        this.myService = myService;
    }
    @Override
    public void run() {
       myService.testB();
    }
}
public class Run {
    public static void main(String[] args) throws InterruptedException {
        MyService myService = new MyService();
        for (int i = 0; i < 10; i++) {
            MyThreadA myThreadA = new MyThreadA(myService);
            myThreadA.start();
            MyThreadB myThreadB = new MyThreadB(myService);
            myThreadB.start();
        }
    }
}

在这里插入图片描述

ReentrantReadWriteLock 不实现Lock接口,单独的类

ReentrantLock 读的时候也是同步的。比如5个线程同时读取一个变量,每个线程执行1s,一共执行5s,但是由于是读操作,线程可以同时读取,只需要1s。因此出现读写锁。
读的时候可以异步执行。
读锁:又叫共享锁
写锁:又叫排它锁

传统ReentrantLock

public class MyService {
    private Lock lock = new ReentrantLock();
    private Condition condition = lock.newCondition();
    volatile private int next = 1;
    public void testA(){
        try {
            lock.lock();
            System.out.println("begin"+Thread.currentThread().getName()+"---"+System.currentTimeMillis());
            Thread.sleep(2000);
            System.out.println("读到一个数用时2s");
            System.out.println("  end"+Thread.currentThread().getName()+"---"+System.currentTimeMillis());
            lock.unlock();
        }catch (InterruptedException e){
            e.printStackTrace();
        }
    }
}
public class MyThreadA extends Thread {
    private MyService myService;
    public MyThreadA(MyService myService){
        super();
        this.myService = myService;
    }
    @Override
    public void run() {
        myService.testA();
    }
}
public class Run {
    public static void main(String[] args) throws InterruptedException {
        MyService myService = new MyService();
        MyThreadA myThreadA1 = new MyThreadA(myService);
        myThreadA1.start();
        MyThreadA myThreadA2 = new MyThreadA(myService);
        myThreadA2.start();
    }
}

在这里插入图片描述
我们发现每个线程用2s,一共用时4s,这效率很低

ReentrantReadWriteLock :分为读写互斥,写读互斥,读读异步,写写互斥,下面分别介绍

1.读读异步
public class MyService {
    private ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
    public void testA(){
        try {
            lock.readLock().lock();
            System.out.println("begin"+Thread.currentThread().getName()+"---"+System.currentTimeMillis());
            Thread.sleep(2000);
            System.out.println("读到一个数");
            System.out.println("  end"+Thread.currentThread().getName()+"---"+System.currentTimeMillis());
            lock.readLock().unlock();
        }catch (InterruptedException e){
            e.printStackTrace();
        }
    }
}
public class MyThreadA extends Thread {
    private MyService myService;
    public MyThreadA(MyService myService){
        super();
        this.myService = myService;
    }
    @Override
    public void run() {
        myService.testA();
    }
}
public class Run {
    public static void main(String[] args) throws InterruptedException {
        MyService myService = new MyService();
        MyThreadA myThreadA1 = new MyThreadA(myService);
        myThreadA1.start();
        MyThreadA myThreadA2 = new MyThreadA(myService);
        myThreadA2.start();
    }
}

在这里插入图片描述
根据结果我们发现,两个线程同时begin,并且只用了2s

2.写写互斥
public void testA(){
        try {
            lock.writeLock().lock();
            System.out.println("begin"+Thread.currentThread().getName()+"---"+System.currentTimeMillis());
            Thread.sleep(2000);
            System.out.println("修改一个数");
            System.out.println("  end"+Thread.currentThread().getName()+"---"+System.currentTimeMillis());
            lock.writeLock().unlock();
        }catch (InterruptedException e){
            e.printStackTrace();
        }
    }

在这里插入图片描述
写操作还是要同步。一个线程一个线程的运行

3.读写互斥
public class MyService {
    private ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
    public void read(){
        try {
            lock.readLock().lock();
            System.out.println("begin"+Thread.currentThread().getName()+"---"+System.currentTimeMillis());
            Thread.sleep(2000);
            System.out.println("读一个数");
            System.out.println("  end"+Thread.currentThread().getName()+"---"+System.currentTimeMillis());
            lock.readLock().unlock();
        }catch (InterruptedException e){
            e.printStackTrace();
        }
    }
    public void write(){
        try {
            lock.writeLock().lock();
            System.out.println("begin"+Thread.currentThread().getName()+"---"+System.currentTimeMillis());
            Thread.sleep(2000);
            System.out.println("修改一个数");
            System.out.println("  end"+Thread.currentThread().getName()+"---"+System.currentTimeMillis());
            lock.writeLock().unlock();
        }catch (InterruptedException e){
            e.printStackTrace();
        }
    }
}
public class MyThreadA extends Thread {
    private MyService myService;
    public MyThreadA(MyService myService){
        super();
        this.myService = myService;
    }
    @Override
    public void run() {
        myService.read();
    }
}
public class MyThreadB extends Thread{
    private MyService myService;
    public MyThreadB(MyService myService){
        super();
        this.myService = myService;
    }
    @Override
    public void run() {
       myService.write();
    }
}
public class Run {
    public static void main(String[] args) throws InterruptedException {
        MyService myService = new MyService();
        MyThreadA myThreadA1 = new MyThreadA(myService);
        myThreadA1.start();
        MyThreadB myThreadB2 = new MyThreadB(myService);
        myThreadB2.start();
    }
}

在这里插入图片描述

4.写读互斥
public class Run {
    public static void main(String[] args) throws InterruptedException {
        MyService myService = new MyService();
        MyThreadB myThreadB2 = new MyThreadB(myService);
        myThreadB2.start();
        MyThreadA myThreadA1 = new MyThreadA(myService);
        myThreadA1.start();
    }
}

在这里插入图片描述

总结:只要有写操作,就是互斥的。读读是异步的。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值