java基础-线程状态分析-lock初步使用

503 阅读2分钟

java基础-线程状态分析中,LockSupport.park()会使线程进入waiting状态,那么这个park方法是啥,查看源码发现其底层还是调用native方法(Unsafe.park()),此文只讲述park用法,cpp底层代码分析吃力。。。

根据Lock文档描述,每个线程有个许可,当获取不到线程许可的时候,线程是不可用的,即处于waiting或者(timed_waiting状态);当执行park后,只有执行unpark或者线程interrupt或者虚假唤醒

park许可只有0和大于0 。当线程的许可大于0,park不堵塞;unpark不叠加,当执行unpark会使许可大于0。

1. 2次park,不unpark,线程waiting
public class LockPark {
    private static int num = 0;

    public static void main(String[] args) {

        Thread thread = new Thread(() -> {
            while (num < 2) {
                System.out.println(Thread.currentThread().getName() + ":park begin");
                LockSupport.park();
                System.out.println(Thread.currentThread().getName() + ":park first");
                LockSupport.park();
                System.out.println(Thread.currentThread().getName() + ":park second");

            }
        });
        thread.start();

//        LockSupport.unpark(thread);
//        System.out.println(thread.getName() + ":unpark first");
        num++;
    }
}

image.png

2. 2次park,1次unpark

public class LockPark {
    private static int num = 0;

    public static void main(String[] args) {

        Thread thread = new Thread(() -> {
            while (num < 2) {
                System.out.println(Thread.currentThread().getName() + ":park begin");
                LockSupport.park();
                System.out.println(Thread.currentThread().getName() + ":park first");
                LockSupport.park();
                System.out.println(Thread.currentThread().getName() + ":park second");

            }
        });
        thread.start();

        LockSupport.unpark(thread);
        System.out.println(thread.getName() + ":unpark first");
        num++;
    }
}

image.png

3. 2次park,2次unpark
public class LockPark {
    private static int num = 0;

    public static void main(String[] args) throws InterruptedException {

        Thread thread = new Thread(() -> {
            while (num < 2) {
                System.out.println(Thread.currentThread().getName() + ":park begin");
                LockSupport.park();
                System.out.println(Thread.currentThread().getName() + ":park first");
                LockSupport.park();
                System.out.println(Thread.currentThread().getName() + ":park second");

            }
        });
        thread.start();

        LockSupport.unpark(thread);
        System.out.println(thread.getName() + ":unpark first");
        num++;
        //演示明显
        Thread.sleep(100);
        LockSupport.unpark(thread);
        num++;
        System.out.println(thread.getName() + ":unpark second");
    }
}

image.png

由 1 2 3示例可知 unpark每次授权一个,park就会唤醒

4. 2次unpark,2次park,执行顺序颠倒,可证明unpark不累积
public class LockPark {
    private static int num = 2;

    public static void main(String[] args) throws InterruptedException {

        Thread thread = new Thread(() -> {
            while (true) {
                if (num < 2) {
                    System.out.println(Thread.currentThread().getName() + ":park begin");
                    LockSupport.park();
                    System.out.println(Thread.currentThread().getName() + ":park first");
                    LockSupport.park();
                    System.out.println(Thread.currentThread().getName() + ":park second");
                }

            }
        });
        thread.start();

        LockSupport.unpark(thread);
        System.out.println(thread.getName() + ":unpark first");
        LockSupport.unpark(thread);
        System.out.println(thread.getName() + ":unpark second");
        num = 0;
        while (true){
            
        }
    }
}

image.png

5. 2次park,1次中断
public class LockPark {
    private static int num = 0;

    public static void main(String[] args)  {

        Thread thread = new Thread(() -> {
            while (true) {
                if (num < 2) {
                    System.out.println(Thread.currentThread().getName() + ":park begin");
                    LockSupport.park();

                    System.out.println(Thread.currentThread().getName() + ":park first");
                    LockSupport.park();

                    System.out.println(Thread.currentThread().getName() + ":park second");
                    System.out.println(Thread.currentThread().getName() + ":interrupt first,状态是:"+Thread.currentThread().isInterrupted());

                    try {
                        sleep(1000000);
                    } catch (InterruptedException e) {
                       //抛出异常后,会将状态置位为false

                    }
                    System.out.println(Thread.currentThread().getName() + ":interrupt second,状态是:"+Thread.currentThread().isInterrupted());
//
                    LockSupport.park();
                    System.out.println(Thread.currentThread().getName() + ":park second");
                }

            }
        });
        thread.start();

        try {
            sleep(500);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        thread.interrupt();


        while (true){

        }
    }
}

image.png

一. Reentrantlock初步使用

LockSupport.park在AQS中使用,ReentrantLock会使用AQS,此文先分析ReentrantLock,AQS后面再分析,常用的两个锁有Reentrantlock(互斥锁)和ReentrantReadWriteLock(读共享,写互斥,读写互斥

1. Reentrantlock互斥

public class ReentrantlockOne {
    public static void main(String[] args) throws InterruptedException {
        ReentrantLock lock = new ReentrantLock();

        Thread threadOne = new Thread(() -> {
            lock.lock();
            try {
                System.out.println(Thread.currentThread().getName() + "获取到线程");
                Thread.sleep(100000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                lock.unlock();
            }
        });
        threadOne.start();

        Thread threadTwo = new Thread(() -> {
            lock.lock();
            try {
                System.out.println(Thread.currentThread().getName() + "获取到线程");
                Thread.sleep(100000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                lock.unlock();
            }
        });
        threadTwo.start();

        Thread.sleep(100);
        System.out.println(threadOne.getName()+ "状态是:" + threadOne.getState());
        System.out.println(threadTwo.getName() + "状态是:" + threadTwo.getState());
    }
}

image.png

2. ReentrantReadWriteLock读共享

public class ReentrantReadWriteLockOne {
    public static void main(String[] args) throws InterruptedException {
        ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
        ReentrantReadWriteLock.ReadLock readLock = lock.readLock();
        ReentrantReadWriteLock.WriteLock writeLock = lock.writeLock();

        Thread threadOne = new Thread(() -> {
            readLock.lock();
            try {
                System.out.println(Thread.currentThread().getName() + "获取到线程");
                Thread.sleep(100000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                readLock.unlock();
            }
        });
        threadOne.start();

        Thread threadTwo = new Thread(() -> {
            readLock.lock();
            try {
                System.out.println(Thread.currentThread().getName() + "获取到线程");
                Thread.sleep(100000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                readLock.unlock();
            }
        });
        threadTwo.start();

        Thread.sleep(100);
        System.out.println(threadOne.getName() + "状态是:" + threadOne.getState());
        System.out.println(threadTwo.getName() + "状态是:" + threadTwo.getState());
    }
}

image.png

3. ReentrantReadWriteLock写互斥

public class ReentrantReadWriteLockOne {
    public static void main(String[] args) throws InterruptedException {
        ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
        ReentrantReadWriteLock.ReadLock readLock = lock.readLock();
        ReentrantReadWriteLock.WriteLock writeLock = lock.writeLock();

        Thread threadOne = new Thread(() -> {
            writeLock.lock();
            try {
                System.out.println(Thread.currentThread().getName() + "获取到线程");
                Thread.sleep(100000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                writeLock.unlock();
            }
        });
        threadOne.start();

        Thread threadTwo = new Thread(() -> {
            writeLock.lock();
            try {
                System.out.println(Thread.currentThread().getName() + "获取到线程");
                Thread.sleep(100000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                writeLock.unlock();
            }
        });
        threadTwo.start();

        Thread.sleep(100);
        System.out.println(threadOne.getName() + "状态是:" + threadOne.getState());
        System.out.println(threadTwo.getName() + "状态是:" + threadTwo.getState());
    }
}

image.png

4. ReentrantReadWriteLock 读写互斥

public class ReentrantReadWriteLockOne {
    public static void main(String[] args) throws InterruptedException {
        ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
        ReentrantReadWriteLock.ReadLock readLock = lock.readLock();
        ReentrantReadWriteLock.WriteLock writeLock = lock.writeLock();

        Thread threadOne = new Thread(() -> {
            writeLock.lock();
            try {
                System.out.println(Thread.currentThread().getName() + "获取到线程");
                Thread.sleep(100000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                writeLock.unlock();
            }
        });
        threadOne.start();
        Thread.sleep(100);
        Thread threadTwo = new Thread(() -> {
            readLock.lock();
            try {
                System.out.println(Thread.currentThread().getName() + "获取到线程");
                Thread.sleep(100000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                readLock.unlock();
            }
        });
        threadTwo.start();
        //此处睡眠是为了能获取waiting状态
        Thread.sleep(100);
        System.out.println(threadOne.getName() + "写锁状态是:" + threadOne.getState());
        System.out.println(threadTwo.getName() + "读锁状态是:" + threadTwo.getState());
    }
}

image.png

二. Reentrantlock实现生产者消费者

使用condition和await实现

public class ConsumerAndProducer {
    private static int num = 6;
    private static List<Integer> list = new ArrayList<>();

    public static void main(String[] args) {
        ReentrantLock lock = new ReentrantLock();
        Condition producer = lock.newCondition();
        Condition consumer = lock.newCondition();

        //生产者组
        for (int i = 0; i < 5; i++) {
            new Thread(() -> {
                //死循环一直生产
                while (true) {
                    lock.lock();
                    try {
                        while (list.size() == num) {
                            System.out.println(Thread.currentThread().getName() + "生产已经满了:" + list.size());
                            producer.await();
                        }
                        int object = (int) (Math.random() * 6);
                        System.out.println(Thread.currentThread().getName() + "生产:" + object);
                        list.add(object);
                        consumer.signalAll();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    } finally {
                        lock.unlock();
                    }
                }
            }).start();
        }

        //消费者组
        for (int i = 0; i < 5; i++) {
            new Thread(() -> {
                while (true) {
                    lock.lock();
                    try {
                        while (list.size() == 0) {
                            System.out.println(Thread.currentThread().getName() + "消费已经空了:" + list.size());
                            consumer.await();
                        }
                        int object = list.get(0);
                        list.remove(0);
                        System.out.println(Thread.currentThread().getName() + "消费:" + object);
                        producer.signalAll();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    } finally {
                        lock.unlock();
                    }
                }
            }).start();
        }
    }
}

image.png