精华内容
下载资源
问答
  • 什么是线程死锁,? 产生死锁的四个必要条件? 解决线程死锁的办法是什么? 如何避免死锁? 什么是线程死锁? 首先是一个线程需要多把锁,并发的时候多个线程同时被阻塞,它们中的一个或者全部都在等待某个资源被...

    面试题

    1. 什么是线程死锁,?
    2. 产生死锁的四个必要条件?
    3. 解决线程死锁的办法是什么?
    4. 如何避免死锁?

    什么是线程死锁?

    首先是一个线程需要多把锁,并发的时候多个线程同时被阻塞,它们中的一个或者全部都在等待某个资源被释放。由于线程被无限期地阻塞,因此程序不可能正常终止。

    如下图所示,线程 A 持有资源 2,线程 B 持有资源 1,他们同时都想申请对方的资源,所以这两个线程就会互相等待而进入死锁状态。

    下面通过一个例子来说明线程死锁,代码模拟了上图的死锁的情况 (代码来源于《并发编程之美》):

    public class DeadLockDemo {
        private static Object resource1 = new Object();//资源 1
        private static Object resource2 = new Object();//资源 2
    
        public static void main(String[] args) {
            new Thread(() -> {
                synchronized (resource1) {
                    System.out.println(Thread.currentThread() + "get resource1");
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println(Thread.currentThread() + "waiting get resource2");
                    synchronized (resource2) {
                        System.out.println(Thread.currentThread() + "get resource2");
                    }
                }
            }, "线程 1").start();
    
            new Thread(() -> {
                synchronized (resource2) {
                    System.out.println(Thread.currentThread() + "get resource2");
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println(Thread.currentThread() + "waiting get resource1");
                    synchronized (resource1) {
                        System.out.println(Thread.currentThread() + "get resource1");
                    }
                }
            }, "线程 2").start();
        }
    }
    

    Output

    Thread[线程 1,5,main]get resource1
    Thread[线程 2,5,main]get resource2
    Thread[线程 1,5,main]waiting get resource2
    Thread[线程 2,5,main]waiting get resource1
    
    1. 线程 A 通过 synchronized (resource1)获得resource1 的监视器锁
    2. 通过Thread.sleep(1000);让线程A休眠1s为的是让线程B得到执行然后获取到resource2的监视器锁。
    3. 线程 A 和线程B休眠结束了都开始企图请求获取对方的资源,然后这两个线程就会陷入互相等待的状态,这也就产生了死锁。

    产生死锁的四个必要条件?

    • 互斥条件:该资源任意一个时刻只由一个线程占用。
    • 请求与保持条件:一个进程因请求资源而阻塞时,对已获得的资源保持不放。
    • 不剥夺条件:线程已获得的资源在末使用完之前不能被其他线程强行剥夺,只有自己使用完毕后才释放资源。
    • 循环等待条件:若干进程之间形成一种头尾相接的循环等待资源关系。

    如何避免死锁?

    • 破坏互斥条件: 这个条件我们没有办法破坏,因为我们用锁本来就是想让他们互斥的(临界资源需要互斥访问)。
    • 破坏请求与保持条件:
      一次性申请所有的资源。
    • 破坏不剥夺条件:
      占用部分资源的线程进一步申请其他资源时,如果申请不到,可以主动释放它占有的资源。
    • 破坏循环等待条件: 靠按序申请资源来预防。按某一顺序申请资源,释放资源则反序释放。破坏循环等待条件。

    我们只要破坏产生死锁的四个条件中的其中一个就可以了,一般情况下解决线程死锁通常的解决办法是先对锁申请进行排序。

    我们对线程 2 的代码修改成下面这样就不会产生死锁了

    new Thread(() -> {
                synchronized (resource1) {
                    System.out.println(Thread.currentThread() + "get resource1");
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println(Thread.currentThread() + "waiting get resource2");
                    synchronized (resource2) {
                        System.out.println(Thread.currentThread() + "get resource2");
                    }
                }
            }, "线程 2").start();
    

    Output

    Thread[线程 1,5,main]get resource1
    Thread[线程 1,5,main]waiting get resource2
    Thread[线程 1,5,main]get resource2
    Thread[线程 2,5,main]get resource1
    Thread[线程 2,5,main]waiting get resource2
    Thread[线程 2,5,main]get resource2
    
    Process finished with exit code 0
    

    我们分析一下上面的代码为什么避免了死锁的发生?

    线程 1 首先获得到 resource1 的监视器锁,这时候线程 2 就获取不到了。然后线程 1 再去获取 resource2 的监视器锁,可以获取到。然后线程 1 释放了对 resource1、resource2 的监视器锁的占用,线程 2 获取到就可以执行了。这样就破坏了破坏循环等待条件,因此避免了死锁。

    再举一个工作中死锁的例子(丁威老师的案例)

    import java.util.List;
    public class LockTest {
        
        // 例如这个方法上有一个事务注解
        public void updateBusiness(List<Long> goodsIds) {
    
            if(goodsIds == null || goodsIds.isEmpty()) {
                return;
            }
            IGoodsDao goodsDao = null;
    
            for(Long gid : goodsIds) {
                goodsDao.updateGoods(gid, 1);  // 将库存减去1,需要持有该记录的行锁
            }
        }
    }
    
    interface  IGoodsDao {
        // 减库存
        void updateGoods(Long goodsId, int nums);
    }
    
    1. 如果一个用户要购买商品ID 为 1,3 的商品
    2. 而另外一个用户需要购买同样的商品,但是在购物车中选择商品的顺序是 3,1
    3. 此时两个线程同时调用 updateBusiness 方法

    执行轨迹如下:

    这样就出现了死锁。

    避免死锁的方法:
    1. 数据库锁的加锁和解锁需要在同一个数据库连接里,否则会出现解锁失败
    2. 避免一个线程获取多个锁
    3. 尝试使用定时锁,lock.tryLock(timeOut)替换内部锁
    4. 避免一个锁内同时占用多个资源,尽量保证每一个锁内只占用一个资源

    针对上面的例子,通常的解决办法是,先对锁申请进行排序。

    // 例如这个方法上有一个事务注解
        public void updateBusiness(List<Long> goodsIds) {
    
            if(goodsIds == null || goodsIds.isEmpty()) {
                return;
            }
            IGoodsDao goodsDao = null;
    
            Collections.sort(goodsIds);
    
            for(Long gid : goodsIds) {
                goodsDao.updateGoods(gid, 1);  // 将库存减去1,需要持有该记录的行锁
            }
        }
    

    这样就不会出现死锁了。

    如果你觉得文章还不错,你的转发、分享、赞赏、点赞、留言就是对我最大的鼓励。 感谢您的阅读,我坚持原创,十分欢迎并感谢您的关注。

    原创不易,欢迎转发,关注公众号“码农进阶之路”,获取更多面试题,源码解读资料!
    在这里插入图片描述

    展开全文
  • 什么是线程阻塞?为什么会出现线程阻塞?

    万次阅读 多人点赞 2019-02-14 22:20:01
    什么是线程阻塞? 在某一时刻某一个线程在运行一段代码的时候,这时候另一个线程也需要运行,但是在运行过程中的那个线程执行完成之前,另一个线程是无法获取到CPU执行权的(调用sleep方法是进入到睡眠暂停状态,...

    什么是线程阻塞?

    在某一时刻某一个线程在运行一段代码的时候,这时候另一个线程也需要运行,但是在运行过程中的那个线程执行完成之前,另一个线程是无法获取到CPU执行权的(调用sleep方法是进入到睡眠暂停状态,但是CPU执行权并没有交出去,而调用wait方法则是将CPU执行权交给另一个线程),这个时候就会造成线程阻塞。

    为什么会出现线程阻塞?

    1.睡眠状态:当一个线程执行代码的时候调用了sleep方法后,线程处于睡眠状态,需要设置一个睡眠时间,此时有其他线程需要执行时就会造成线程阻塞,而且sleep方法被调用之后,线程不会释放锁对象,也就是说锁还在该线程手里,CPU执行权还在自己手里,等睡眠时间一过,该线程就会进入就绪状态,典型的“占着茅坑不拉屎”;

    2.等待状态:当一个线程正在运行时,调用了wait方法,此时该线程需要交出CPU执行权,也就是将锁释放出去,交给另一个线程,该线程进入等待状态,但与睡眠状态不一样的是,进入等待状态的线程不需要设置睡眠时间,但是需要执行notify方法或者notifyall方法来对其唤醒,自己是不会主动醒来的,等被唤醒之后,该线程也会进入就绪状态,但是进入仅需状态的该线程手里是没有执行权的,也就是没有锁,而睡眠状态的线程一旦苏醒,进入就绪状态时是自己还拿着锁的。等待状态的线程苏醒后,就是典型的“物是人非,大权旁落“;

    3.礼让状态:当一个线程正在运行时,调用了yield方法之后,该线程会将执行权礼让给同等级的线程或者比它高一级的线程优先执行,此时该线程有可能只执行了一部分而此时把执行权礼让给了其他线程,这个时候也会进入阻塞状态,但是该线程会随时可能又被分配到执行权,这就很”中国化的线程“了,比较讲究谦让;

    4.自闭状态:当一个线程正在运行时,调用了一个join方法,此时该线程会进入阻塞状态,另一个线程会运行,直到运行结束后,原线程才会进入就绪状态。这个比较像是”走后门“,本来该先把你的事情解决完了再解决后边的人的事情,但是这时候有走后门的人,那就会停止给你解决,而优先把走后门的人事情解决了;

    5.suspend() 和 resume() :这两个方法是配套使用的,suspend() 是让线程进入阻塞状态,它的解药就是resume(),没有resume()它自己是不会恢复的,由于这种比较容易出现死锁现象,所以jdk1.5之后就已经被废除了,这对就是相爱相杀的一对。

    展开全文
  • 二、什么是线程锁? 2.1 没有加锁时 2.2 锁加在普通方法上时,使用同一个对象调用。 2.3 锁加在普通方法上时,使用两个不同的对象调用。 2.4 锁加在静态方法上时,使用两个不同的对象调用。 三、释放锁是什么样的?...

    前言

    想写这篇文章的缘由呢,是因为我自己在了解线程锁,线程同步的这些概念,以及线程同步的这些方法。我在查的很多博客要么重复一样的东西,要么一些很不生动的理论,还有一些是直接抛一个案例。不能说不对,只是作为一个小白的时候,很难理解。所以我想尝试从一个小白的角度去介绍一下我暂时所了解的这部分内容。其实这写内容都是相互关联的,有可能看了第一点,不明白,但是看完后面的点,串起来之后才是明白到的
    注意: 如果你是做卷子答题,那还是不要cv这里的内容,因为这里只是在逼逼,或者举例说明,尝试从简单的例子去介绍这样一个听起来有点复杂的东西。

    一、什么是【线程同步】?

    我第一次看到这几个字,我的理解是几个线程在跑, 同步 同步不就是让他们跑的一样快。很遗憾,事实不是这样。
    【解释】当多个线程在跑 ,并且这些线程用到同一个资源时,需要保证资源的安全。举个很简单的例子 银行存款有一百万【共同资源】,现在有十个人【多条线程】 同时来取钱,每个人都想取十万。每个人都直接修改这个【共同资源】的数据,一瞬间全部完全取款。结果银行存款【共同资源】剩下100-10=90万(因为瞬间还没等别人处理完,都拿到这个资源,并且去修改,所以每个人拿到的原始值都是100,算出的结果都是剩下90),每个人也都拿到钱,凭空生出了九十万,这明显是不对的,这是不安全的。
    在这里插入图片描述
    所以需要有很多手段去保证这个【共同资源】的安全,让他们一个一个使用这个资源,第一个取完剩下九十万,第二个取完剩下八十万…最终全部取万剩下零元。
    在这里插入图片描述
    那么保证资源安全的这些手段,称之为线程同步

    二、什么是线程锁?synchronized

    线程锁,它其实是为了保证【共享资源】安全,而出现的一个概念。我们要保证资源的安全,所以要把资源加锁,有人在用的时候锁死,不用的时候,开锁
    好比如路边只有一个公共厕所,此时有5个人都要上厕所【共享资源】,有人在上厕所的时候就得把门锁死,要是不锁门,其他四个人也冲进去,多不安全,特别是漂亮的小女子,妈呀,我这口水,那场面多刺激。用完了得开锁,不把锁开起来后面几个人难道还得拉裤子?
    java中如何加锁?
    加锁的位置有好几个,也有的不同的关键字,这里先介绍一下synchronized 关键字有无作用在方法上的效果的一些比较。

    2.1 没有加锁时

    首先我们要测试没有使用锁时,同时运行同一个对象的普通方法效果如何。

    /**
     * 没有使用锁时,同时运行同一个对象的普通方法效果如何
     */
    public class TsetSyn1 {
        public static void main(String[] args) {
            TsetSyn1 ts = new TsetSyn1();
            new Thread(new Runnable() {
                @Override
                public void run() {
                    ts.test1();
                }
            }).start();
            new Thread(new Runnable() {
                @Override
                public void run() {
                    ts.test1();
                }
            }).start();
        }
        /**
         * 没有加锁的普通方法
         */
        public void test1() {
            System.out.println("普通方法执行");
            try {
                Thread.sleep(2000);//延时使得方法没有那么快运行结束
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("普通方法结束");
        }
    }
    

    可以看到执行结果

    普通方法执行
    普通方法执行
    普通方法结束
    普通方法结束
    

    由此可见,两个线程的方法是同时在跑的。虽然运行的是同一个方法,但是第二个线程运行方法是没有等待第一个线程运行完方法就开始运行了。

    2.2 锁加在普通方法上时,使用同一个对象调用。

    我们尝试把 synchronized 关键字加在普通方法上,使用同一个对象调用。

    /**
     * 把 synchronized 关键字加在普通方法上,使用同一个对象调用。
     */
    public class TestSyn2 {
        public static void main(String[] args) {
            TestSyn2 ts = new TestSyn2();
            new Thread(new Runnable() {
                @Override
                public void run() {
                    ts.test2();
                }
            }).start();
            new Thread(new Runnable() {
                @Override
                public void run() {
                    ts.test2();
                }
            }).start();
        }
        /**
         * 加锁的普通方法
         */
        public synchronized void test2() {
            System.out.println("普通加锁方法执行");
            try {
                Thread.sleep(2000);//延时使得方法没有那么快运行结束
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("普通加锁方法结束");
        }
    }
    

    可以看到执行结果

    普通加锁方法执行
    普通加锁方法结束
    普通加锁方法执行
    普通加锁方法结束
    

    此时我们可以看到加锁之后,同一个对象中,枷锁的方式,两个线程同时要使用时,需要等前一个线程使用结束后才可以执行这个方法。

    2.3 锁加在普通方法上时,使用两个不同的对象调用。

    /**
     * 把 synchronized 关键字加在普通方法上,使用两个不同的对象调用。
     */
    public class TestSyn3 {
        public static void main(String[] args) {
            TestSyn3 ts = new TestSyn3();
            TestSyn3 ts2 = new TestSyn3();
            new Thread(new Runnable() {
                @Override
                public void run() {
                    ts.test2();
                }
            }).start();
            new Thread(new Runnable() {
                @Override
                public void run() {
                    ts2.test2();
                }
            }).start();
        }
        /**
         * 加锁的普通方法
         */
        public synchronized void test2() {
            System.out.println("普通加锁方法执行");
            try {
                Thread.sleep(2000);//延时使得方法没有那么快运行结束
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("普通加锁方法结束");
        }
    }
    

    可以看到执行结果

    普通加锁方法执行
    普通加锁方法执行
    普通加锁方法结束
    普通加锁方法结束
    

    可以看到,此时的结果和使用一个对象调用方法,且没有加锁运行的结果是一样的。

    2.4 锁加在静态方法上时,使用两个不同的对象调用。

    /**
     * 把 synchronized 关键字加在static方法上,使用两个不同的对象调用。
     */
    public class TestSyn4 {
        public static void main(String[] args) {
            TestSyn4 ts = new TestSyn4();
            TestSyn4 ts2 = new TestSyn4();
            new Thread(new Runnable() {
                @Override
                public void run() {
                    ts.test2();
                }
            }).start();
            new Thread(new Runnable() {
                @Override
                public void run() {
                    ts2.test2();
                }
            }).start();
        }
        /**
         * 加锁的普通方法
         */
        public static synchronized void test2() {
            System.out.println("静态加锁方法执行");
            try {
                Thread.sleep(2000);//延时使得方法没有那么快运行结束
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("静态加锁方法结束");
        }
    }
    

    可以看到执行结果

    静态加锁方法执行
    静态加锁方法结束
    静态加锁方法执行
    静态加锁方法结束
    

    此时我们又可以看到,调用这个方法又需要等待了。这是因为,静态方法它是属于类本身的。所有对象共用这个方法,而不是每个对象各自拥有的。因此将它锁住,即使是其他对象,也需要等待它被释放锁的时候才可以使用。
    至于其他类锁什么的,还有其他关键字,这边暂不介绍,后面有时间再来补充修改一下,这边目的是认识一下锁的概念,让我们更好地阅读下文。

    三、释放锁是什么样的?sleep()和wait()的区别?

    我们在搜的很多文章也都会说道sleep和wait都是让程序睡一会儿,两者区别呢是前者不会释放锁,后者会释放锁。那么什么是释放锁呢。我们在前面其实也看到了没有释放锁的样子,因为我们前面用的都是sleep。
    接下来我就写一个例程来对比试验
    如何看出区别,在结果中对结果和类的内容进行讲解

    3.1 测试类TestSleepWait

    /**
     * 测试类
     */
    public class TestSleepWait {
        public static void main(String[] args) {
            Person testP = new Person(); //这个类里面有是个带锁的方法,目的是多线程时,只有一个线程能够使用
            OneThread t1 = new OneThread(testP);//创建的这两个线程争夺这个资源,
            OneThread t2 = new OneThread(testP);
            t1.start();
            t2.start();
        }
    }
    

    3.2 线程类OneThread

    /**
     * 线程中调用person类的synchronized修饰过的方法
     */
    public class OneThread extends Thread  {
        private Person curPerson = null;
        public OneThread(Person person){
            this.curPerson = person;
        }
    
        @Override
        public void run(){
            System.out.println(Thread.currentThread().getName() + " call start");
            curPerson.display();
            System.out.println(Thread.currentThread().getName() + " call end");
        }
    }
    

    3.3 重点区别类,带锁的方法Person

    /**
     * 方法逻辑,展示两种方法的区别
     */
    public class Person {
        public synchronized void display(){
            for(int i=1;i<=5;i++){
                System.out.println(Thread.currentThread().getName() + " " + i+"time["+ System.currentTimeMillis()/1000+"]startsleep");
                try{
                    Thread.sleep(2000);   // sleep是Thread类静态方法,不会释放对象锁
                }catch(Exception ex){
    
                }
                System.out.println(Thread.currentThread().getName() + " " + i+"time["+ System.currentTimeMillis()/1000+"]endsleep");
                if(i==3){
                    System.out.println(Thread.currentThread().getName() + " " + i+"time["+ System.currentTimeMillis()/1000+"]startwait-----------");
                    try{
                        this.wait(2000); // wait是Object类实例方法,会释放对象锁
                    }catch(Exception ex){
    
                    }
                    System.out.println(Thread.currentThread().getName() + " " + i+"time["+ System.currentTimeMillis()/1000+"]endwait-----------");
                }
            }
        }
    }
    

    可以看到执行结果

    Thread-0 call start
    Thread-0 1time[1610893403]startsleep
    Thread-1 call start
    Thread-0 1time[1610893405]endsleep
    Thread-0 2time[1610893405]startsleep
    Thread-0 2time[1610893407]endsleep
    Thread-0 3time[1610893407]startsleep
    Thread-0 3time[1610893409]endsleep
    Thread-0 3time[1610893409]startwait-----------
    Thread-1 1time[1610893409]startsleep
    Thread-1 1time[1610893411]endsleep
    Thread-1 2time[1610893411]startsleep
    Thread-1 2time[1610893413]endsleep
    Thread-1 3time[1610893413]startsleep
    Thread-1 3time[1610893415]endsleep
    Thread-1 3time[1610893415]startwait-----------
    Thread-0 3time[1610893415]endwait-----------
    Thread-0 4time[1610893415]startsleep
    Thread-0 4time[1610893417]endsleep
    Thread-0 5time[1610893417]startsleep
    Thread-0 5time[1610893419]endsleep
    Thread-0 call end
    Thread-1 3time[1610893419]endwait-----------
    Thread-1 4time[1610893419]startsleep
    Thread-1 4time[1610893421]endsleep
    Thread-1 5time[1610893421]startsleep
    Thread-1 5time[1610893423]endsleep
    Thread-1 call end
    

    解析

    我截取前面8行的部分,可以看到,两个线程都启动了,但是由于两个线程要跑的都是person类中的display方法,且这个方法用synchronized修饰过,我们在main方法中传的是同一个person对象给两个线程,因此这个display方法同时只能有一个线程使用
    从前面的部分,我们很明显看到,线程0和线程1都启动了,但是只有线程0一直在sleep,每次sleep2秒,这就说明调用sleep的时候这个方法没有被让出来,只不过整个程序都停在那边等。线程0依旧在使用display方法,线程1在等待display方法被释放锁

    Thread-0 call start
    Thread-0 1time[1610893403]startsleep
    Thread-1 call start
    Thread-0 1time[1610893405]endsleep
    Thread-0 2time[1610893405]startsleep
    Thread-0 2time[1610893407]endsleep
    Thread-0 3time[1610893407]startsleep
    Thread-0 3time[1610893409]endsleep
    

    接着我们可以看到线程0调用了wait()方法,紧接着就轮到了线程1开始调用sleep方法,同样没有释放出display这里就很明显的说明了,wait方法会释放锁,其他线程就可以来争抢这个资源了,剩下的部分一次类推。

    Thread-0 3time[1610893409]startwait-----------
    Thread-1 1time[1610893409]startsleep
    Thread-1 1time[1610893411]endsleep
    Thread-1 2time[1610893411]startsleep
    Thread-1 2time[1610893413]endsleep
    Thread-1 3time[1610893413]startsleep
    Thread-1 3time[1610893415]endsleep
    

    四、工程

    本章例程

    虽然synchronized已经能狗达到锁的功能,但是它有一些缺陷,因此有了Lock。下一章节中介绍了lock的内容。
    线程安全(二)Lock 什么是Lock线程锁?与synchronized区别在哪?Lock锁是如何实现等待通知的?如何实现线程顺序执行

    展开全文
  • 什么是线程?什么是进程?为什么要有线程?有什么关系与区别? 进程: 程序执行时的一个实例 每个进程都有独立的内存地址空间 系统进行资源分配和调度的基本单位 进程里的堆,是一个进程中最大的一块内存...

    什么是线程?什么是进程?为什么要有线程?有什么关系与区别?

    进程:

    • 程序执行时的一个实例

    • 每个进程都有独立的内存地址空间
    • 系统进行资源分配和调度的基本单位
    • 进程里的堆,是一个进程中最大的一块内存,被进程中的所有线程共享的,进程创建时分配,主要存放 new 创建的对象实例
    • 进程里的方法区,是用来存放进程中的代码片段的,是线程共享的
    • 在多线程 OS 中,进程不是一个可执行的实体,即一个进程至少创建一个线程去执行代码


          
    为什么要有线程?

          每个进程都有自己的地址空间,即进程空间。一个服务器通常需要接收大量并发请求,为每一个请求都创建一个进程系统开销大、请求响应效率低,因此操作系统引进线程。      

     


    线程:

    • 进程中的一个实体
    • 进程的一个执行路径
    • CPU 调度和分派的基本单位
    • 线程本身是不会独立存在
    • 当前线程 CPU 时间片用完后,会让出 CPU 等下次轮到自己时候在执行
    • 系统不会为线程分配内存,线程组之间只能共享所属进程的资源
    • 线程只拥有在运行中必不可少的资源(如程序计数器、栈)
    • 线程里的程序计数器就是为了记录该线程让出 CPU 时候的执行地址,待再次分配到时间片时候就可以从自己私有的计数器指定地址继续执行
    • 每个线程有自己的栈资源,用于存储该线程的局部变量和调用栈帧,其它线程无权访问

     

          
    关系:

    • 一个程序至少一个进程,一个进程至少一个线程,进程中的多个线程是共享进程的资源
    • Java 中当我们启动 main 函数时候就启动了一个 JVM 的进程,而 main 函数所在线程就是这个进程中的一个线程,也叫做主线程
    • 一个进程中有多个线程,多个线程共享进程的堆和方法区资源,但是每个线程有自己的程序计数器,栈区域

    如下图

          


    区别:

    •  本质:进程是操作系统资源分配的基本单位;线程是任务调度和执行的基本单位
    •  内存分配:系统在运行的时候会为每个进程分配不同的内存空间,建立数据表来维护代码段、堆栈段和数据段;除了 CPU 外,系统不会为线程分配内存,线程所使用的资源来自其所属进程的资源
    • 资源拥有:进程之间的资源是独立的,无法共享;同一进程的所有线程共享本进程的资源,如内存,CPU,IO 等
    •  开销:每个进程都有独立的代码和数据空间,程序之间的切换会有较大的开销;线程可以看做轻量级的进程,同一类线程共享代码和数据空间,每个线程都有自己独立的运行程序计数器和栈,线程之间切换的开销小
    • 通信:进程间 以IPC(管道,信号量,共享内存,消息队列,文件,套接字等)方式通信 ;同一个进程下,线程间可以共享全局变量、静态变量等数据进行通信,做到同步和互斥,以保证数据的一致性
    • 调度和切换:线程上下文切换比进程上下文切换快,代价小
    • 执行过程:每个进程都有一个程序执行的入口,顺序执行序列;线程不能够独立执行,必须依存在应用程序中,由程序的多线程控制机制控制
    • 健壮性:每个进程之间的资源是独立的,当一个进程崩溃时,不会影响其他进程;同一进程的线程共享此线程的资源,当一个线程发生崩溃时,此进程也会发生崩溃,稳定性差,容易出现共享与资源竞争产生的各种问题,如死锁等
    • 可维护性:线程的可维护性,代码也较难调试,bug 难排查


    进程与线程的选择:

    • 需要频繁创建销毁的优先使用线程。因为进程创建、销毁一个进程代价很大,需要不停的分配资源;线程频繁的调用只改变 CPU 的执行
    • 线程的切换速度快,需要大量计算,切换频繁时,用线程
    • 耗时的操作使用线程可提高应用程序的响应
    • 线程对 CPU 的使用效率更优,多机器分布的用进程,多核分布用线程
    • 需要跨机器移植,优先考虑用进程
    • 需要更稳定、安全时,优先考虑用进程
    • 需要速度时,优先考虑用线程
    • 并行性要求很高时,优先考虑用线程

     

    Java 编程语言中线程是通过 java.lang.Thread 类实现的。

    Thread 类中包含tid(线程id)、name(线程名称)、group(线程组)、daemon(是否守护线程)、priority(优先级) 等重要属性。

     


    【Java面试题与答案】整理推荐

     

    展开全文
  • 什么是线程

    千次阅读 2019-03-28 10:02:31
    1、什么是线程:程序中负责执行的哪个东东就叫做线程(执行路线,进程内部的执行序列),或者说是进程的子任务。 2、线程与进程的区别 进程是一个资源单位,而线程是它的一部分。 线程它是轻量级的(只有栈),没有...
  • 什么是进程? 什么是线程? 线程和进程之间的区别 进程和线程的选择取决于什么? 计算密集型任务以及I/O密集型任务 在python中,多进程和多线程的选择取决于什么
  • 什么是进程什么是线程?进程是指在系统中正在运行的一个应用程序;程序一旦运行就是进程进程可以认为是程序执行时的一个实例。进程是系统进行资源分配的独立实体, 且每个进程拥有独立的地址空间。一个进程无法直接...
  • 什么是进程?什么是线程?进程与线程的区别?

    千次阅读 多人点赞 2018-07-20 10:03:57
    什么是线程? 为什么要有线程? 进程与线程的区别? 进程与线程的选择取决条件? 什么是进程?什么是线程? 进程:进程是并发执行程序在执行过程中资源分配和管理的基本单位(资源分配的最小单位)。进程可以...
  • 什么是线程同步和线程异步?

    千次阅读 2019-05-31 13:41:39
    1、什么是线程同步和线程异步 线程同步:是多个线程同时访问同一资源,等待资源访问结束,浪费时间,效率不高 线程异步:访问资源时,如果有空闲时间,则可在空闲等待同时访问其他资源,实现多线程机制 异步处理...
  • 什么是线程安全和线程不安全

    千次阅读 2018-05-24 10:25:53
    什么是线程安全? 如果你的代码所在的进程中有多个线程在同时运行,而这些线程可能会同时运行这段代码。如果每次运行结果和单线程运行的结果是一样的,而且其他的变量的值也和预期的是一样的,就是线程安全的。 ...
  • 【多线程并发编程】六 什么是线程安全?

    千次阅读 多人点赞 2020-02-24 18:57:26
    在学习多线程的道路上,我们会经常看到线程安全这类词汇,面试官也经常问,本文就来说一说什么是线程安全。
  • 点击上方“码农进阶之路”,选择“设为星标”回复“面经”获取面试资料面试题什么是线程死锁,?产生死锁的四个必要条件?解决线程死锁的办法是什么?如何避免死锁?什么是线程死锁?首先是一个线程需...
  • 1.什么是线程安全问题 就是 多线程环境中 , 且存在数据共享 , 一个线程访问的共享 数据被其他线程修改了, 那么就发生了线程安全问题 , 整个访问过程中 , 无一共享的数据被其他线程修改了 就是线程安全的 程序中如果...
  • 什么是线程安全 什么是线程不安全

    万次阅读 2018-02-26 14:24:35
    之前在看数据数据结构 一直都有“线程安全或线程不安全的字眼”,之前一直不是很理解,今天在这里做个小结。... 线程不安全就是不提供数据访问保护,有可能出现多个线程先后更改数据造成所得到的数据脏数据...
  • 什么是线程安全?如何保证线程安全?

    千次阅读 多人点赞 2019-05-27 23:22:44
    什么是线程安全 参考: 《Java并发编程实践》中对线程安全的定义: 当多个线程访问一个对象时,如果不用考虑这些线程在运行时环境下的调度和交替执行,也不需要进行额外的同步,或者在调用方进行任何其他的协调操作...
  • 想要知道线程是否安全,首先要明白什么是线程,然后才能了解它是否处于安全状态。 1、什么是线程? 线程,有时被称为轻量进程(Lightweight Process,LWP),是程序执行流的最小单元。一个标准的线程由线程ID,当前...
  • 1. 为什么会有多线程? 2. 什么是线程安全?3. 怎么样保证线程安全?
  • 一:什么是线程 线程是和进程经常放在一起比较的两个概念。按照我的理解,线程和进程并没有本质的区别,都是对CPU调用的单位,其区别只是在于对CPU调用的时间尺度的大小。按照我浅显的理解,进程就是编译器编译...
  • 阿里面试题:ConcurrentHashMap为什么是线程安全的?

    千次阅读 多人点赞 2020-04-10 01:00:33
    阿里面试题:ConcurrentHashMap为什么是线程安全的? ConcurrentHashMap,其实是线程安全的HashMap,所以阅读ConcurrentHashMap,建议 先阅读一下两篇介绍HashMap的文章 你真的懂大厂面试题:HashMap吗? jdk1.7 ...
  • 2、什么是线程? 个进程内部的控制序列, 是进程的一个实体,是进程的一条执行路径。程也就是一个轻量级进程(仅仅是在linux系统中。在windows系统中,进程就是经常进程,线程就是线程),每个线程都有自己的线程...
  • 什么是线程同步

    千次阅读 2010-08-26 15:22:00
    什么是线程同步
  • 什么是线程安全

    千次阅读 2019-01-06 21:11:21
    java中的线程安全是什么:  就是线程同步的意思,就是当一个程序对一个线程安全的方法或者语句进行访问的时候,其他的不能再对他进行操作了,必须等到这次访问结束以后才能对这个线程安全的方法进行访问 什么线程...
  • 什么是线程安全,你真的了解吗?

    万次阅读 多人点赞 2018-09-20 11:52:06
    我们整天说线程安全,但是你对什么是线程安全真的了解嘛?说真的,我之前真的是了解甚微,那么我们今天就来聊聊这个问题。 再说什么是线程安全之前我们先来聊聊什么是进程。   1、什么是进程? 先来看一张图 ...
  • Java线程:什么是线程

    万次阅读 多人点赞 2017-08-04 14:47:11
     Java编写程序都运行在在Java虚拟机(JVM)中,在JVM的内部,程序的多任务通过线程来实现的。每用java命令启动一个java应用程序,就会启动一个JVM进程。在同一个JVM进程中,有且只有一个进程,就是它自己。在这个...
  • 什么是线程调度

    千次阅读 2018-03-04 11:26:32
     抢占式模式指的每个线程执行的时间线程切换的都由系统控制,(系统控制指的砸系统某种运行机制下,没跳线程都分同样的执行时间篇,也可能有些线程的时间片较长,某些深圳得不到执行片)这种情况下 一个线程堵塞不会...
  • 什么是线程?什么是进程?

    千次阅读 2018-08-28 11:05:57
    一、进程 进程指在系统中正在运行的一个应用程序,程序一旦运行就是进程。 进程系统进行资源分配的独立实体, ...线程是进程的一个实体,进程的一条执行路径。 线程是CPU独立运行和独立调度的基本单位。 ...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 85,057
精华内容 34,022
关键字:

什么是线程