JAVA-线程知识点补充

2021-03-12  本文已影响0人  超人TIGA
线程的新启方式有几种?

①继承Thread类
②实现Runnable接口
没有其他了,就算是实现callable接口,也是需要再包装成FutureTask,而FutureTask其实也只是实现了Runnable接口的而已。

线程状态
image.png 1、中间的运行态,分2种情况,分别是running和ready,其实就是CPU调度,如果CPU正在执行该线程,就是running,被剥夺或者等待就是ready。
2、在并发编程的时候,我们可能会调用线程的wait、join、notify、notifyAll等一系列方法,进行线程间的暂停和唤醒继续执行,达到并发的效果。
3、如果在wait、sleep等方法参数中填写时间,那就是超时的限制,避免线程无限等待。
4、阻塞状态,其实就是锁的获取。
注意:进入阻塞状态的情况,只有使用的是synchronize修饰符才会进入,如果使用的是显示锁,就不会了,只会进行等待。
死锁

达成死锁的条件:
1、有多个线程(T>=2),同时有多个资源(S>=2),同时资源要小于线程的数量(S<=T)
2、各个线程获取资源的顺序不一样
3、得到某个资源后,不再释放
举个例子就是:有2个小朋友A和B,同时又有一台游戏机和一包零食,A和B都想一边玩游戏一边吃零食;但是A优先选游戏,B优先选零食;各自得到一样之后,都想得到对方的,同时还不肯把自己已获得的给对方。这样的结果就是谁也无法既吃零食又玩游戏了。
那如何打破死锁?
其实主要是针对第2、3点,因为第1点基本都是跟业务有关,很多时候我们都不能主动改变。打破第2点很简单,调整获取资源的顺序就可以了,保证全部线程申请资源的顺序都一致。
要打破第3点,就需要用Lock接口来自己实现锁了,因为Lock提供了很多方法,使得我们可以对锁进行尝试获取、释放等操作。

/**
 *类说明:演示尝试拿锁解决死锁
 */
public class TryLock {
    private static Lock No13 = new ReentrantLock();//第一个锁
    private static Lock No14 = new ReentrantLock();//第二个锁

    //先尝试拿No13 锁,再尝试拿No14锁,No14锁没拿到,连同No13 锁一起释放掉
    private static void fisrtToSecond() throws InterruptedException {
        String threadName = Thread.currentThread().getName();
        Random r = new Random();
        while(true){
            if(No13.tryLock()){
                System.out.println(threadName
                        +" get 13");
                try{
                    if(No14.tryLock()){
                        try{
                            System.out.println(threadName
                                    +" get 14");
                            System.out.println("fisrtToSecond do work------------");
                            break;
                        }finally{
                            No14.unlock();
                        }
                    }
                }finally {
                    No13.unlock();
                }

            }
            Thread.sleep(r.nextInt(3));
        }
    }

    //先尝试拿No14锁,再尝试拿No13锁,No13锁没拿到,连同No14锁一起释放掉
    private static void SecondToFisrt() throws InterruptedException {
        String threadName = Thread.currentThread().getName();
        Random r = new Random();
        while(true){
            if(No14.tryLock()){
                System.out.println(threadName
                        +" get 14");
                try{
                    if(No13.tryLock()){
                        try{
                            System.out.println(threadName
                                    +" get 13");
                            System.out.println("SecondToFisrt do work------------");
                            break;
                        }finally{
                            No13.unlock();
                        }
                    }
                }finally {
                    No14.unlock();
                }

            }
            Thread.sleep(r.nextInt(3));
        }
    }

    private static class TestThread extends Thread{

        private String name;

        public TestThread(String name) {
            this.name = name;
        }

        public void run(){
            Thread.currentThread().setName(name);
            try {
                SecondToFisrt();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    public static void main(String[] args) {
        Thread.currentThread().setName("TestDeadLock");
        TestThread testThread = new TestThread("SubTestThread");
        testThread.start();
        try {
            fisrtToSecond();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}
活锁

活锁是啥?其实就是解决了上面的2或者3的死锁问题,但是也有几率资源被锁住,或者最终获取到资源的时间被拉长。例如:
线程A、B 资源锁1、2
A得1等2->全释放->A得1等2->全释放->A得1等2
B得2等1->全释放->B得2等1->全释放->B得2等1
这样下去就会没完没了了,解决方法就是,可以等一等,所以上面的代码中,稍微加入了Thread.sleep(r.nextInt(3));使得线程获得锁的时间点错开,提高资源锁的获得率,也就避免了活锁了。

ThreadLocal

线程本地变量,让每个线程都拥有一份自己的变量副本,实现线程间的数据隔离。


image.png

结构就是上图那样:
①每个线程,都会有一个ThreadLocalMap类型的变量,threadLocals
②在ThreadLocalMap类里面,有一个Entry[]类型的变量,table
③Entry类里面,存放的是键值对,key就是ThreadLocal类型,value就是object类型。

所以,每当在线程里面调用threadLocal的set方法时,首先会创建一个threadLocalMap的对象实例和一个Entry[]的空数组对象;然后以线程为key,把我们需要存储的值放到value里面。

上一篇下一篇

猜你喜欢

热点阅读