多线程相关

多线程

进程和线程

进程与线程最主要的区别是它们是操作系统管理资源的不同方式的体现。 准确来说进程与线程属于衍生关系。 进程是操作系统执行程序的一次过程,在这个过程中可能会产生多个线程。

比如在使用QQ时,有窗口线程, 文字发送的线程,语音输入的线程。(可能不是很恰当)

由于系统在线程之间的切换比在进程之间的切换更高效率,所以线程也可以被视为轻量级进程。

并发和并行
  1. 并发:多个线程任务被一个cpu轮流执行。注意,这里并不是只允许一个cpu执行多任务,多个cpu执行也是可以的。 并发强调的是计算机应用程序有处理多个任务的能力。
  2. 并行:多个线程任务被多个cpu同时执行。这里也并不是只允许多个cpu处理多任务,一个cpu也是可以的, 只要cpu能在同一时刻处理多任务。并行强调的是计算机应用程序拥有同时处理多任务的能力。

总结: 并行包含并发。

多线程的利弊
  • 利:
    • 线程可以比作轻量级的进程,cpu在线程之间的切换比在进程之间的切换,耗费的资源要少的多。
    • 现在是多核cpu时代,意味着多个线程可以被多个cpu同时运行(并行),如果可以利用好多线程,那么可以编写出高并发的程序。
  • 弊:
    • 虽然线程带来的好处很多,但是并发编程并不容易,如果控制不好线程,那么就可能造成死锁,资源闲置,内存泄露等问题。
什么是上下文切换?

cpu是采用时间片的轮转制度,在多个线程之间来回切换运行的。 当cpu切换到另一个线程的时候,它会先保存当前线程执行的状态, 以便在下次切换回来执行时,可以重新加载状态,继续运行。 从保存线程的状态再到重新加载回线程的状态的这个过程就叫做上下文切换。

线程的优先级

在Java中可以通过Thread类的setPriority方法来设置线程的优先级, 虽然可以通过这样的方式来设置线程的优先级,但是线程执行的先后顺序并不依赖与线程的优先级。 换句话说就是,线程的优先级不保证线程执行的顺序。

线程的几种状态

见:jdk Thread类源码中的state枚举类

  NEW,RUNNABLE,BLOCKED,WAITING,TIMED_WAITING,TERMINATED
sleep方法和wait方法的区别
  1. sleep方法是Thread类的方法;而wait方法是Object类的方法。
  2. sleep方法会使当前线程让出cpu的调度资源,从而让其他线程有获得被执行的机会, 但是并会让当前线程释放锁对象(抱着锁睡觉); 而wait方法是让当前线程释放锁 并进入wait状态,** 不参与获取锁的争夺,从而让其他等待资源的线程有机会获取锁, 只有当其他线程调用notify或notifyAll方法是,被wait的线程才能重新与其他线程一起争夺资源。
stop,suspend,resume等方法为什么会被遗弃
  • stop: stop方法被弃用很好理解,因为stop方法是强行终止线程的执行, 不管线程的run方法是否执行完,资源是否释放完,它都会终止线程的运行,并释放锁。 显然,这在设计上就不合理。
  • suspend和resume: suspend方法用于阻塞一个线程,但并不释放锁, 而resume方法的作用只是为了恢复被suspend的线程。 假设A,B线程都争抢同一把锁,A线程成功的获得了锁, 然后被suspend阻塞了,却并没有释放锁,它需要其他线程来唤醒, 但此时B线程需要获得这把锁才能唤醒A,所以此时就陷入了死锁。
interrupt,interrupted,isInterrupted方法区别
  • interrupt: 这个方法并不是中断当前线程,而是给当前线程设置一个中断状态。
  • isInterrupted: 当线程调用interrupt方法后,线程就有了一个中断状态, 而使用isInterrupted方法就可以检测到线程的中断状态。
  • interrupted: 这个方法用于清除interrupt方法设置的中断状态。 如果一个线程之前调用了interrupt方法设置了中断状态, 那么interrupted方法就可以清除这个中断状态。
join方法

join方法的作用是让指定线程加入到当前线程中执行。

假如在main方法里面创建一个线程A执行,并调用A的join方法, 那么当前线程就是main,指定的A线程就会在main之前执行, 等A执行完后,才会继续执行main。

    public static void main(String[] args) throws Exception
    {
        Thread a = new Thread(()->
        {
            try
            {
                TimeUnit.SECONDS.sleep(1);

            }catch (Exception e){}

            System.out.println("thread join");
        });
        a.start();

        //a会在main线程之前执行
        a.join();

        System.out.println("main");
    }

join方法的底层是wait方法,调用A线程(子线程)的join方法实际上是让main线程wait, 等A线程执行完后,才能继续执行后面的代码。

yield方法

yield属于Thread的静态方法, 它的作用是让当前线程让出cpu调度资源。不会释放锁

yield方法其实就和线程的优先级一样,你虽然指定了, 但是最后的结果不由得你说了算, 即使调用了yield方法,最后仍然可能是这个线程先执行, 只不过说别的线程可能先执行的机会稍大一些。

yield 和 sleep的区别

yield方法:让出CPU执行权

yield()方法是Thread的静态方法,它的作用是放弃当前的CPU资源,将它让给其他优先级更高的线程去占用CPU执行时间。但放弃时间不确定,有可能刚刚放弃,马上又获得CPU时间片。这里需要注意的是yield()方法和sleep方法一样,线程并不会让出锁,和wait方法不同

sleep与yield方法的区别在于,当线程调用sleep方法时调用线程会被阻塞挂起指定的时间,在这期间线程调度器不会去调度该线程.而调用yield方法时,线程只是让出自己剩余的时间片,并没有被阻塞挂起,而是处于就绪状态,线程调度器下一次调度时就有可能调度到当前线程执行 。

notify和notifyAll的区别

先说两个概念:锁池和等待池

  • 锁池:假设线程A已经拥有了某个对象(注意:不是类)的锁,而其它的线程想要调用这个对象的某个synchronized方法(或者synchronized块),由于这些线程在进入对象的synchronized方法之前必须先获得该对象的锁的拥有权,但是该对象的锁目前正被线程A拥有,所以这些线程就进入了该对象的锁池中。
  • 等待池:假设一个线程A调用了某个对象的wait()方法,线程A就会释放该对象的锁后,进入到了该对象的等待池中

Reference:java中的锁池和等待池

然后再来说notify和notifyAll的区别

  • 如果线程调用了对象的 wait()方法,那么线程便会处于该对象的等待池中,等待池中的线程不会去竞争该对象的锁
  • 当有线程调用了对象的 notifyAll()方法(唤醒所有 wait 线程)或 notify()方法(只随机唤醒一个 wait 线程),被唤醒的的线程便会进入该对象的锁池中,锁池中的线程会去竞争该对象锁。也就是说,调用了notify后只要一个线程会由等待池进入锁池,而notifyAll会将该对象等待池内的所有线程移动到锁池中,等待锁竞争
  • 优先级高的线程竞争到对象锁的概率大,假若某线程没有竞争到该对象锁,它还会留在锁池中,唯有线程再次调用 wait()方法,它才会重新回到等待池中。而竞争到对象锁的线程则继续往下执行,直到执行完了 synchronized 代码块,它会释放掉该对象锁,这时锁池中的线程会继续竞争该对象锁。

Reference:线程间协作:wait、notify、notifyAll

综上,所谓唤醒线程,另一种解释可以说是将线程由等待池移动到锁池,notifyAll调用后,会将全部线程由等待池移到锁池,然后参与锁的竞争,竞争成功则继续执行,如果不成功则留在锁池等待锁被释放后再次参与竞争。而notify只会唤醒一个线程。

notify 有可能引起死锁问题, 应该使用notifyAll函数来唤醒线程.

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值