多线程相关方法

Thread相关方法

run() 与 start()

package com.yanxb.method.threadmethod;

import com.yanxb.util.MyTool;

/**
 * 测试 run()方法 和 start()方法
 *
 * */
public class TestRunAndStart {
    public static void main(String[] args) {
        test01();
    }

    /**
     * 直接调用 run 是在主线程中执行了 run,没有启动新的线程
     * 使用 start 是启动新的线程,通过新的线程间接执行 run 中的代码
     *
     * 输出
     * 2022-04-08T05:39:28.958Z	|	1649396368970	|	1	|	main	|	执行了...
     * 2022-04-08T05:39:28.971Z	|	1649396368971	|	13	|	t2	|	执行了...
     * */
    private static void test01() {
        Thread t1 = new Thread(() -> {
            MyTool.printTimeAndThread("执行了...");
        },"t1");

        Thread t2 = new Thread(() -> {
            MyTool.printTimeAndThread("执行了...");
        },"t2");

        t1.run();
        t2.start();
    }

}

yield() 与 setPriority()

package com.yanxb.method.threadmethod;

/**
 * 测试 yield 方法 与设置优先级
 * */
public class TestYieldAndPriority {
    public static void main(String[] args) {
        // test01();
        test02();
    }

    /**
     * 测试yield方法
     * 1.调用 yield 会让当前线程从 Running 进入 Runnable 就绪状态,然后调度执行其它线程
     * 2.具体的实现依赖于操作系统的任务调度器
     *
     * 输出:
     *  ---->2 150426
     *  ---->1 347692
     * */
    private static void test01() {
        Runnable task1 = () -> {
            int count = 0;
            for (;;) {
                System.out.println("---->1 " + count++);
            }
        };
        Runnable task2 = () -> {
            int count = 0;
            for (;;) {
                Thread.yield();
                System.out.println(" ---->2 " + count++);
            }
        };
        Thread t1 = new Thread(task1, "t1");
        Thread t2 = new Thread(task2, "t2");
        t1.start();
        t2.start();
    }

    /**
     * 测试设置线程优先级
     * 1.线程优先级会提示(hint)调度器优先调度该线程,但它仅仅是一个提示,调度器可以忽略它
     * 2.如果 cpu 比较忙,那么优先级高的线程会获得更多的时间片,但 cpu 闲时,优先级几乎没作用
     *
     * 输出
     * ---->1 57615
     * ---->2 118569
     * */
    private static void test02() {
        Runnable task1 = () -> {
            int count = 0;
            for (;;) {
                System.out.println("---->1 " + count++);
            }
        };
        Runnable task2 = () -> {
            int count = 0;
            for (;;) {
                System.out.println(" ---->2 " + count++);
            }
        };
        Thread t1 = new Thread(task1, "t1");
        Thread t2 = new Thread(task2, "t2");
        t1.setPriority(Thread.MIN_PRIORITY);
        t2.setPriority(Thread.MAX_PRIORITY);
        t1.start();
        t2.start();
    }

}

sleep()

package com.yanxb.method.threadmethod;

import com.yanxb.util.MyTool;

import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;

/**
 * 特点:
 * 调用 sleep 会让当前线程从 Running 进入 Timed Waiting 状态(阻塞)
 * 其它线程可以使用 interrupt 方法打断正在睡眠的线程,这时 sleep 方法会抛出 InterruptedException
 * 睡眠结束后的线程未必会立刻得到执行
 * 建议用 TimeUnit 的 sleep 代替 Thread 的 sleep 来获得更好的可读性
 * sleep方法不会释放锁
 *
 * */
public class TestSleep {
    public static void main(String[] args) {
        // test01();
        // test02();
        // test03();
        test04();
    }

    /**
     * sleep方法不会释放锁测试
     * 2022-04-07T00:05:22.456Z	|	1649289922471	|	12	|	t1	|	sleep == 前str
     * 2022-04-07T00:05:23.481Z	|	1649289923481	|	12	|	t1	|	sleep == 后str
     * 2022-04-07T00:05:23.481Z	|	1649289923481	|	13	|	t2	|	t2str
     * */
    private static void test04() {
        String str = "str";
        new Thread(() -> {
            synchronized (str){
                MyTool.printTimeAndThread("sleep == 前" + str);
                try {
                    Thread.currentThread().sleep(1000);
                } catch (InterruptedException e) {
                }
                MyTool.printTimeAndThread("sleep == 后" + str);
            }
        },"t1").start();
        new Thread(() -> {
            synchronized (str){
                MyTool.printTimeAndThread("t2 == " + str);
            }
        },"t2").start();
    }

    private static void test03() {
        ReentrantLock lock = new ReentrantLock();
        Condition condition = lock.newCondition();
        lock.lock();
        try {
            // while(条件不满足) {
            while (lock.toString() != null) {
                try {
                    //让当前线程等待
                    condition.await();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }finally {
            lock.unlock();
        }

        new Thread(() -> {
            //唤醒工作线程
            condition.signalAll();
        },"Other Thread").start();
    }

    /**
     * obj.wait() 与 其他线程结合使用 obj.notifyAll()唤醒工作线程方式
     * */
    private static void test02() {
        TestSleep testSleep = new TestSleep();
        synchronized(testSleep) {
            // while(条件不满足) {
            while(testSleep.toString() != null) {
            try {
                testSleep.wait();
            } catch(InterruptedException e) {
                e.printStackTrace();
            }
            }
            //业务逻辑
        }

        // 其他线程唤醒工作线程
        Thread thread = new Thread(() -> {
            testSleep.notifyAll();
        }, "其他线程");
    }

    /**
     * 限制cpu的使用率防止空转
     * 在没有利用 cpu 来计算时,不要让 while(true) 空转浪费 cpu,这时可以使用 yield 或 sleep 来让出 cpu 的使用权给其他程序
     * 其他方式实现:
     *  可以用 wait 或 条件变量达到类似的效果
     *  不同的是,后两种都需要加锁,并且需要相应的唤醒操作,一般适用于要进行同步的场景
     *  sleep 适用于无需锁同步的场景
     * */
    private static void test01() {
        while(true) {
            try {
                Thread.sleep(50);
                //业务逻辑
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

interrupt() 与 isInterrupted()

package com.yanxb.method.threadmethod;

import com.yanxb.util.MyTool;

import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.LockSupport;

import static com.yanxb.util.MyTool.sleep;

/**
 * 打断方法 interrupt()
 *
 * interrupt() 打断线程
 *      如果被打断线程正在 sleep,wait,join会导致被打断的线程抛出InterruptedException,并清除打断标记;
 *      如果打断的正在运行的线程,则会设置打断标记 ;park 的线程被打断,也会设置打断标记
 *      打断正在运行的线程,是不会让对应线程立即停止的
 * isInterrupted()  判断是否被打断  不会清除 打断标记
 * interrupted() 判断当前线程是否被打断 会清除打断标记
 * */
public class TestInterrupt {
    public static void main(String[] args) throws InterruptedException {
        // test01();

        // test02();

        // test03();

        // test04();

    }

    /**
     * 如果打断标记已经是 true, 则park会失效
     * 执行 park 会先阻塞,当主线程睡眠时间到打断t1线程后,会将park置为失效
     * */
    private static void test04() {
        Thread t1 = new Thread(() -> {
            for (int i = 0; i < 3; i++) {
                MyTool.printTimeAndThread("park...");
                LockSupport.park();
                MyTool.printTimeAndThread("打断状态:" + Thread.currentThread().isInterrupted());
            }
        });
        t1.start();
        sleep(1);
        t1.interrupt();
    }

    /**
     *打断 park 线程, 不会清空打断状态
     * */
    private static void test03() {
        Thread t1 = new Thread(() -> {
            MyTool.printTimeAndThread("park...");
            LockSupport.park();
            MyTool.printTimeAndThread("unpark...");
            MyTool.printTimeAndThread("打断状态:" + Thread.currentThread().isInterrupted());
        }, "t1");
        t1.start();
        sleep(0.5);
        t1.interrupt();
        MyTool.printTimeAndThread("打断状态:" + t1.isInterrupted());
    }

    /**
     * 测试打断正在运行的线程:
     * 打断正在运行的线程,是不会让对应线程立即停止的(将死循环里的break注释掉即可证明)
     *
     * 输出:
     * 2022-04-06T06:10:09.678Z	|	1649225409688	|	1	|	main	|	t1线程被打断前,打断标记为==false
     * 2022-04-06T06:10:09.688Z	|	1649225409688	|	1	|	main	|	t1线程被打断后,打断标记为==true
     * 2022-04-06T06:10:09.688Z	|	1649225409688	|	12	|	t1	|	当前线程被打断,退出循环,打断标记为==true
     * */
    private static void test02() throws InterruptedException {
        Thread t1 = new Thread(() -> {
            while (true) {
                Thread thread = Thread.currentThread();
                boolean interrupted = thread.isInterrupted();
                MyTool.printTimeAndThread("当前线程被打断,退出循环,打断标记为==" + interrupted);
                if (interrupted){
                    MyTool.printTimeAndThread("当前线程被打断,退出循环,打断标记为==" + interrupted);
                    break;
                }
            }
        }, "t1");

        t1.start();
        TimeUnit.SECONDS.sleep(1);
        MyTool.printTimeAndThread("t1线程被打断前,打断标记为==" + t1.isInterrupted());
        t1.interrupt();
        MyTool.printTimeAndThread("t1线程被打断后,打断标记为==" + t1.isInterrupted());

    }


    /**
     * sleep,wait,join 这几个方法都会让线程进入阻塞状态
     * 测试打断 sleep,wait,join 的线程 -> 报异常,且sleep()会清空打断标记,在调用interrupt()可以重置打断标记,在获取时即可获得true
     *
     * 输出:
     * 打断前 -- main 获取thread 打断状态为false
     * 打断后 -- main 获取thread 打断状态为false
     * t1 e 线程 打断状态false
     * */
    private static void test01() throws InterruptedException {
        Thread thread = new Thread(() -> {
            try {
                TimeUnit.SECONDS.sleep(2);
            } catch (InterruptedException e) {
                MyTool.printTimeAndThread("t1 e 线程 打断状态" + Thread.currentThread().isInterrupted());
                e.printStackTrace();
                //在这里调用interrupt()方法可以重置打断标记,但其他线程获取不到此打断标记
                Thread.currentThread().interrupt();
                boolean interrupted = Thread.currentThread().isInterrupted();
                MyTool.printTimeAndThread("t1 e 线程 调用interrupt() 打断状态" + interrupted);
            }
        }, "t1");

        thread.start();
        MyTool.printTimeAndThread("打断前 -- main 获取thread 打断状态为" + thread.isInterrupted());
        TimeUnit.SECONDS.sleep(1);
        thread.interrupt();
        // 结果为false -- interrupt打断sleep线程,打断标记会被清除
        TimeUnit.SECONDS.sleep(1);
        boolean interrupted = thread.isInterrupted();
        MyTool.printTimeAndThread("打断后 -- main 获取thread 打断状态为" + interrupted);
    }
}

join()

package com.yanxb.method.threadmethod;

import com.yanxb.util.MyTool;

import java.util.concurrent.TimeUnit;

/**
 * 测试join() --> 等待结果返回
 * */
public class TestJoin {
    static int i = 0;

    public static void main(String[] args) {
        // test01();
        test02();
    }

    /**
     * 测试使用join()获取另外一个线程的执行结果
     *
     * 输出
     * 2022-04-08T06:01:47.018Z	|	1649397707030	|	12	|	t1	|	执行业务逻辑...
     * 2022-04-08T06:01:48.042Z	|	1649397708042	|	1	|	main	|	i 的值为 10
     * */
    private static void test01() {
        Thread t1 = new Thread(() -> {
            MyTool.printTimeAndThread("执行业务逻辑...");
            try {
                TimeUnit.SECONDS.sleep(1);
            } catch (InterruptedException e) {
            }
            i = 10;
        }, "t1");
        t1.start();
        try {
            t1.join();
        } catch (InterruptedException e) {}
        MyTool.printTimeAndThread("i 的值为 " + i);
    }

    /**
     * 测试使用join(long time)获取超时时另外一个线程的执行结果
     *
     * 输出
     * 2022-04-08T06:07:36.419Z	|	1649398056434	|	12	|	t1	|	执行业务逻辑...
     * 2022-04-08T06:07:37.434Z	|	1649398057434	|	1	|	main	|	i 的值为 0
     * 2022-04-08T06:07:38.436Z	|	1649398058436	|	12	|	t1	|	返回结果...
     * 2022-04-08T06:07:39.441Z	|	1649398059441	|	1	|	main	|	i 的值为 10
     * */
    private static void test02() {
        Thread t1 = new Thread(() -> {
            MyTool.printTimeAndThread("执行业务逻辑...");
            try {
                TimeUnit.SECONDS.sleep(2);
            } catch (InterruptedException e) { }
            MyTool.printTimeAndThread("返回结果...");
            i = 10;
        }, "t1");
        t1.start();
        try {
            t1.join(1000);
        } catch (InterruptedException e) {}
        MyTool.printTimeAndThread("i 的值为 " + i);
        try {
            TimeUnit.SECONDS.sleep(2);
        } catch (InterruptedException e) {}
        MyTool.printTimeAndThread("i 的值为 " + i);
    }
}

LockSupport相关方法

park() 和 unpark(Thread t)

package com.yanxb.method.othermethod;

import com.yanxb.util.MyTool;

import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.LockSupport;

/**
 *  测试 park 让线程等待、unPark 唤醒线程 类
 *
 *  与 obj 中的 wait、 notify/notifyAll 的区别
 *  1.wait,notify 和 notifyAll 必须配合 Object Monitor 一起使用,而 park,unpark 不必
 *  2.park & unpark 是以线程为单位来【阻塞】和【唤醒】线程,而 notify 只能随机唤醒一个等待线程,notifyAll 是唤醒所有等待线程,就不那么【精确】
 *  3.park & unpark 可以先 unpark,而 wait & notify 不能先 notify
 *  4.控制的粒度会更细。JUC包下的很多类都是使用Unsafe(提供CAS操作)和LockSupport(提供park/unpark操作)实现
 *
 * 参考链接:
 *  https://www.cnblogs.com/zhizhizhiyuan/p/4966827.html
 *  https://blog.youkuaiyun.com/weixin_39687783/article/details/85058686
 * */
public class TestPark {
    
    public static void main(String[] args) throws InterruptedException {
        // test01();

        // test02();

        // test03();

        // test04();
    }

    /**
     * 打断 park 线程:
     * 打断 park 线程, 不会清空打断状态(如果被打断后,打断状态就为true; 如果清空打断状态,则打断状态还为false)
     *
     * 如果打断标记已经是 true, 则 park 会失效
     * */
    private static void test04() throws InterruptedException {
        Thread t1 = new Thread(() -> {
            for (int i = 0; i < 3; i++) {
                MyTool.printTimeAndThread("t1 park..." + i + Thread.currentThread().isInterrupted());
                LockSupport.park();
                MyTool.printTimeAndThread("打断状态:" + Thread.currentThread().isInterrupted());
            }
        },"t1");
        t1.start();
        TimeUnit.SECONDS.sleep(1);
        t1.interrupt();
    }

    /**
     * 打断park()中的线程 -- 不会清空打断标记
     * */
    private static void test03() throws InterruptedException {
        Thread t1 = new Thread(() -> {
            MyTool.printTimeAndThread("t1 打断状态唤醒...前");
            MyTool.printTimeAndThread("t1 打断状态:"+ Thread.currentThread().isInterrupted());
            LockSupport.park();
            MyTool.printTimeAndThread("t1 打断状态唤醒...后");
            MyTool.printTimeAndThread("t1 打断状态:"+ Thread.currentThread().isInterrupted());
        }, "t1");
        t1.start();
        MyTool.printTimeAndThread("main 打断前 获取 t1打断状态:" + t1.isInterrupted());
        TimeUnit.SECONDS.sleep(1);
        t1.interrupt();
        MyTool.printTimeAndThread("main 打断后 获取 t1打断状态:" + t1.isInterrupted());
    }


    /**
     * 在t1线程执行代码前,在其他线程内先给与t1一个许可(也就是执行LockSupport.unpark(t1))
     * 则t1线程在执行park()方法时不会被阻塞等待,而是直接执行后面的代码
     * */
    private static void test02() {
        Thread t1 = new Thread(() -> {
            try {
                TimeUnit.SECONDS.sleep(1);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            LockSupport.park();
            MyTool.printTimeAndThread("park...后的代码");
        }, "t1");

        t1.start();
        MyTool.printTimeAndThread("main 打断t1  park...前");
        LockSupport.unpark(t1);
        MyTool.printTimeAndThread("main 打断t1  park...后");

    }

    /**
     * 先让t1线程等待,在主线程中睡眠两秒唤醒t1线程,让t1线程继续执行
     * */
    private static void test01() throws InterruptedException {
        Thread t1 = new Thread(() -> {
            MyTool.printTimeAndThread("park...前");
            LockSupport.park();
            MyTool.printTimeAndThread("park...后");
        }, "t1");

        t1.start();
        MyTool.printTimeAndThread("打断 t1 park...前");
        TimeUnit.SECONDS.sleep(1);
        LockSupport.unpark(t1);
        MyTool.printTimeAndThread("打断 t1 park...后");
    }
}

原理

  • 每个线程都有自己的一个 Parker 对象,由三部分组成 _counter , _cond 和 _mutex

情况一:先park(),在unPark(Thread t)

在这里插入图片描述

  • 图一步骤说明
    1.当前线程调用 Unsafe.park() 方法
    2.检查 _counter ,本情况为 0,这时,获得 _mutex 互斥锁
    3.线程进入 _cond 条件变量阻塞
    4.设置 _counter = 0
  • 图二步骤说明
    1.调用 Unsafe.unpark(Thread_0) 方法,设置 _counter 为 1
    2.唤醒 _cond 条件变量中的 Thread_0
    3.Thread_0 恢复运行
    4.设置 _counter 为 0

情况二:先unPark(Thread t),在park()

在这里插入图片描述- 上图步骤说明
1.调用 Unsafe.unpark(Thread_0) 方法,设置 _counter 为 1
2.当前线程调用 Unsafe.park() 方法
3.检查 _counter ,本情况为 1,这时线程无需阻塞,继续运行
4.设置 _counter 为 0

Object相关方法

wait() 和 notify()/notifyAll()

package com.yanxb.method.othermethod;

import com.yanxb.util.MyTool;
import lombok.extern.slf4j.Slf4j;

/**
 * 测试 obj 中的wait() 和 notify / notifyAll方法类
 *
 * 与 park 和 unpark 的区别:
 *   1.wait,notify 和 notifyAll 放在synchronized使用,必须配合 Object Monitor 一起使用,而 park,unpark 不必
 *   2.park & unpark 是以线程为单位来【阻塞】和【唤醒】线程,而 notify 只能随机唤醒一个等待线程,notifyAll 是唤醒所有等待线程,就不那么【精确】
 *   3.park & unpark 可以先 unpark,而 wait & notify 不能先 notify
 * */
@Slf4j(topic = "c.TestWaitNotify")
public class TestWaitNotify {
    final static Object obj = new Object();

    public static void main(String[] args) {

        new Thread(() -> {
            synchronized (obj) {
                log.debug("执行....");
                try {
                    obj.wait(); //让线程在obj上一直等待下去
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                log.debug("其它代码....");
            }
        },"t1").start();

        new Thread(() -> {
            synchronized (obj) {
                log.debug("执行....");
                try {
                    obj.wait(); // 让线程在obj上一直等待下去
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                log.debug("其它代码....");
            }
        },"t2").start();

        // 主线程两秒后执行
        MyTool.sleep(1);
        log.debug("唤醒 obj 上其它线程");
        synchronized (obj) {
           // obj.notify(); // 唤醒obj上一个线程
           obj.notifyAll(); // 唤醒obj上所有等待线程
        }
    }
}

原理

原理图

在这里插入图片描述

  • Owner 线程发现条件不满足,调用 wait 方法,即可进入 WaitSet 变为 WAITING 状态
  • BLOCKED 和 WAITING 的线程都处于阻塞状态,不占用 CPU 时间片
  • BLOCKED 线程会在 Owner 线程释放锁时唤醒
  • WAITING 线程会在 Owner 线程调用 notify 或 notifyAll 时唤醒,但唤醒后并不意味者立刻获得锁,仍需进入EntryList 重新竞争

API说明

  • obj.wait() 让进入 object 监视器的线程到 waitSet 等待
  • obj.notify() 在 object 上正在 waitSet 等待的线程中挑一个唤醒
  • obj.notifyAll() 让 object 上正在 waitSet 等待的线程全部唤醒
  • wait() 方法会释放对象的锁,进入WaitSet等待区,从而让其他线程就机会获取对象的锁。无限制等待,直到notify 为止
  • wait(long n) 有时限的等待, 到 n 毫秒后结束等待,或是被notify

sleep(long n) 和 wait(long n) 的区别

  • sleep 是 Thread 方法,而 wait 是 Object 的方法
  • sleep 不需要强制和 synchronized 配合使用,但 wait 需要和 synchronized 一起用
  • sleep 在睡眠的同时,不会释放对象锁的,但 wait 在等待的时候会释放对象锁

与park() 与unpark(Thread t)区别

  • wait,notify 和 notifyAll 必须配合 Object Monitor 一起使用,而 park,unpark 不必
  • park & unpark 是以线程为单位来【阻塞】和【唤醒】线程,而 notify 只能随机唤醒一个等待线程,notifyAll 是唤醒所有等待线程,就不那么【精确】
  • park & unpark 可以先 unpark,而 wait & notify 不能先 notify

ReentrantLock相关方法

基本特性测试方法

package com.yanxb.method.othermethod;

import com.yanxb.util.MyTool;

import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.ReentrantLock;

/**
 * ReentrantLock测试类
 * 与 obj 使用 synchronized 关键字加锁的对比
 *  1.ReentrantLock可中断
 *  2.ReentrantLock可设置超时时间
 *  3.ReentrantLock可设置公平锁(也就是先到先得)
 *  4.ReentrantLock可以支持多个条件变量(类似于synchronized关联对象中minitor中的waitSet)
 *
 *  公平性与条件变量的使用没有总结在该类中
 * */
public class TestReentrantLock {

    private static ReentrantLock rl = new ReentrantLock();

    public static void main(String[] args) {
        // test01();

        // test03();

        // test04();

        // test05();

        // test06();

        test07();
    }

    /**
     * 获取锁超时测试 -- 超时时间 tryLock(long timeout, TimeUnit unit)
     *
     * 被打断场景时 输出
     * 2022-04-10T02:25:32.298Z	|	1649557532312	|	1	|	main	|	main 获取到了锁
     * 2022-04-10T02:25:32.312Z	|	1649557532312	|	12	|	t1	|	t1 线程执行方法启动
     * 2022-04-10T02:25:32.312Z	|	1649557532312	|	12	|	t1	|	等待获取锁时被打断
     * java.lang.InterruptedException
     * 	at java.util.concurrent.locks.AbstractQueuedSynchronizer.tryAcquireNanos(AbstractQueuedSynchronizer.java:1245)
     * 	at java.util.concurrent.locks.ReentrantLock.tryLock(ReentrantLock.java:442)
     * 	at com.yanxb.method.othermethod.TestReentrantLock.lambda$test07$0(TestReentrantLock.java:46)
     * 	at java.lang.Thread.run(Thread.java:745)
     *
     * 	获取到锁是输出
     * 	2022-04-10T02:26:17.139Z	|	1649557577148	|	1	|	main	|	main 获取到了锁
     *  2022-04-10T02:26:17.148Z	|	1649557577148	|	12	|	t1	|	t1 线程执行方法启动
     *  2022-04-10T02:26:18.157Z	|	1649557578157	|	12	|	t1	|	t1 获取了锁
     * */
    private static void test07() {
        Thread thread = new Thread(() -> {
            MyTool.printTimeAndThread("t1 线程执行方法启动 ");
            try {
                if (! rl.tryLock(2, TimeUnit.SECONDS)) {
                    MyTool.printTimeAndThread("t1 未获取到锁");
                    return;
                }
            } catch (InterruptedException e) {
                MyTool.printTimeAndThread("等待获取锁时被打断");
                e.printStackTrace();
                return;
            }
            try {
                MyTool.printTimeAndThread("t1 获取了锁");
            }finally {
                rl.unlock();
            }
        }, "t1");

        rl.lock();
        MyTool.printTimeAndThread("main 获取到了锁");
        thread.start();
        try {
            // thread.interrupt();
            MyTool.sleep(1);
        }finally {
            rl.unlock();
        }
    }

    /**
     * 获取锁超时测试 -- 立即超时 tryLock
     *
     * 输出
     * 2022-04-10T02:20:45.585Z	|	1649557245600	|	1	|	main	|	main 获取到了锁
     * 2022-04-10T02:20:45.601Z	|	1649557245601	|	12	|	t1	|	t1 线程执行方法启动
     * 2022-04-10T02:20:45.601Z	|	1649557245601	|	12	|	t1	|	t1 未获取到锁
     * */
    private static void test06() {
        Thread thread = new Thread(() -> {
            MyTool.printTimeAndThread("t1 线程执行方法启动 ");
            if (! rl.tryLock()) {
                MyTool.printTimeAndThread("t1 未获取到锁");
                return;
            }
            try {
                MyTool.printTimeAndThread("t1 获取了锁");
            }finally {
                rl.unlock();
            }
        }, "t1");

        rl.lock();
        MyTool.printTimeAndThread("main 获取到了锁");
        thread.start();
        try {
            MyTool.sleep(1);
        }finally {
            rl.unlock();
        }
    }

    /**
     * 获取锁时可打断性测试
     * lockInterruptibly()方法获取锁是不可打断的
     *
     * 输出
     * 2022-04-10T02:08:51.621Z	|	1649556531636	|	1	|	main	|	main 获取到了锁
     * 2022-04-10T02:08:52.645Z	|	1649556532645	|	12	|	t1	|	t1 被打断
     * java.lang.InterruptedException
     * 	at java.util.concurrent.locks.AbstractQueuedSynchronizer.doAcquireInterruptibly(AbstractQueuedSynchronizer.java:898)
     * 	at java.util.concurrent.locks.AbstractQueuedSynchronizer.acquireInterruptibly(AbstractQueuedSynchronizer.java:1222)
     * 	at java.util.concurrent.locks.ReentrantLock.lockInterruptibly(ReentrantLock.java:335)
     * 	at com.yanxb.method.othermethod.TestReentrantLock.lambda$test05$0(TestReentrantLock.java:33)
     * 	at java.lang.Thread.run(Thread.java:745)
     * */
    private static void test05() {
        Thread thread = new Thread(() -> {
            try {
                rl.lockInterruptibly();
                //这里的代码不会被执行,因为在获取锁的过程中被其他线程打断了
                MyTool.printTimeAndThread("t1 线程执行方法 ");
            } catch (InterruptedException e) {
                MyTool.printTimeAndThread("t1 被打断 ");
                e.printStackTrace();
                return;
            }
            try {
                //这里的代码不会被执行,因为在获取锁的过程中被其他线程打断了
                MyTool.printTimeAndThread("获取到了锁");
            }finally {
                rl.unlock();
            }
        }, "t1");

        rl.lock();
        MyTool.printTimeAndThread("main 获取到了锁");
        thread.start();
        MyTool.sleep(1);
        try {
            //这里打断t1获取锁的操作,从而控制线程的执行过程
            thread.interrupt();
        }finally {
            rl.unlock();
        }
    }

    /**
     * 获取锁时可打断性测试
     * 使用lock()方法获取锁是不可打断的
     *
     * 输出
     * 2022-04-10T02:06:49.391Z	|	1649556409411	|	1	|	main	|	main 获取到了锁
     * 2022-04-10T02:06:50.415Z	|	1649556410415	|	12	|	t1	|	t1 线程执行方法
     * 2022-04-10T02:06:50.415Z	|	1649556410415	|	12	|	t1	|	t1 获取到了锁
     * */
    private static void test04() {
        Thread thread = new Thread(() -> {
            rl.lock();
            MyTool.printTimeAndThread("t1 线程执行方法 ");
            try {
                MyTool.printTimeAndThread("t1 获取到了锁");
            }finally {
                rl.unlock();
            }
        }, "t1");

        rl.lock();
        MyTool.printTimeAndThread("main 获取到了锁");
        thread.start();
        MyTool.sleep(1);
        try {
            //执行打断操作
            thread.interrupt();
        }finally {
            rl.unlock();
        }
    }

    /**
     * 可重入性测试:
     * 可重入是指同一个线程如果首次获得了这把锁,那么因为它是这把锁的拥有者,因此有权利再次获取这把锁如果是不可重入锁,那么第二次获得锁时,自己也会被锁挡住
     *
     * 输出
     * 2022-04-10T01:30:18.496Z	|	1649554218561	|	1	|	main	|	执行了test01 方法...
     * 2022-04-10T01:30:18.561Z	|	1649554218561	|	1	|	main	|	执行了test02 方法...
     * */
    private static void test01() {
        rl.lock();
        try {
            MyTool.printTimeAndThread("执行了test01 方法...");
            test02();
        } finally {
            rl.unlock();
        }
    }

    private static void test02() {
        rl.lock();
        try {
            MyTool.printTimeAndThread("执行了test02 方法...");
        } finally {
            rl.unlock();
        }
    }


    public static void test03() {
        Thread thread = new Thread(() -> {
            try {
                rl.lockInterruptibly();
                MyTool.printTimeAndThread("t1 线程执行方法 ");
            } catch (InterruptedException e) {
                MyTool.printTimeAndThread("t1 被打断 ");
                e.printStackTrace();
                return;
            }
            try {
                MyTool.printTimeAndThread("获取到了锁");
            } finally {
                rl.unlock();
            }
        }, "t1");

        rl.lock();
        MyTool.printTimeAndThread("main 获取到了锁");
        MyTool.sleep(1);
        thread.start();
        try {
            //执行打断操作
            thread.interrupt();
        } finally {
            rl.unlock();
        }
    }
}

条件变量await() 与 signalAll()/signal()

package com.yanxb.method.othermethod;

import com.yanxb.util.MyTool;

import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;

/**
 * 测试Reentrant类条件变量类
 * 条件变量:
 *      synchronized 中也有条件变量,就是 waitSet 阻塞队列,当条件不满足时进入 waitSet 等待
 *      ReentrantLock 的条件变量比 synchronized 强大之处在于,它是支持多个条件变量的,
 *      这就好比synchronized 是那些不满足条件的线程都在一个阻塞队列里等待唤醒
 *      而 ReentrantLock 支持多个阻塞队列,如可知设置为:有业务A的阻塞队列、有业务B的阻塞队列..
 *      这些不同的条件变量都是通过ReentrantLock对象获取
 *
 * 使用要点
 *      1.await 前需要获得锁
 *      2.await 执行后,会释放锁,进入 conditionObject 等待
 *      3.await 的线程被唤醒(或打断、或超时)取重新竞争 lock 锁
 *      4.等待await和唤醒signalAll、signal操作都是通过条件变量控制的
 *      5.竞争 lock 锁成功后,从 await 后继续执行
 *
 * */
public class TestReentrantCondition {
    static ReentrantLock lock = new ReentrantLock();
    static Condition waitCigaretteQueue = lock.newCondition();
    static Condition waitbreakfastQueue = lock.newCondition();
    static volatile boolean hasCigrette = false;
    static volatile boolean hasBreakfast = false;

    public static void main(String[] args) {

        new Thread(() -> {
            lock.lock();
            try {
               while (!hasCigrette){
                   MyTool.printTimeAndThread("条件不满足,进入条件变量中等待,并释放锁...");
                   try {
                       waitCigaretteQueue.await();
                   } catch (InterruptedException e) {
                       e.printStackTrace();
                   }
               }
                MyTool.printTimeAndThread("条件满足,执行业务逻辑...");
            }finally {
                lock.unlock();
            }
        },"t1").start();

        new Thread(() -> {
            lock.lock();
            try {
                while (!hasBreakfast){
                    MyTool.printTimeAndThread("条件不满足,进入条件变量中等待,并释放锁...");
                    try {
                        waitbreakfastQueue.await();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                MyTool.printTimeAndThread("条件满足,执行业务逻辑...");
            }finally {
                lock.unlock();
            }
        },"t2").start();

        MyTool.sleep(1);
        send01();
        MyTool.sleep(1);
        send02();
    }


    static void send01(){
        lock.lock();
        try {
            MyTool.printTimeAndThread("改变t1线程中的判断条件");
            hasCigrette = Boolean.TRUE;
            //唤醒t1对应条件变量中的所有线程
            waitCigaretteQueue.signalAll();
        }finally {
            lock.unlock();
        }
    }

    static void send02(){
        lock.lock();
        try {
            MyTool.printTimeAndThread("改变t2线程中的判断条件");
            hasBreakfast = Boolean.TRUE;
            //唤醒t1对应条件变量中的单个线程(这里只是记录下唤醒条件变量里线程的API,一个或多个时的不同API,没有实际意义)
            waitbreakfastQueue.signal();
        }finally {
            lock.unlock();
        }
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

似寒若暖

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值