Java多线程基础知识

守护线程与用户线程

Java中线程分为两类:守护线程和用户线程。

用户线程:JVM启动是会调用main函数,main函数所在线程就是用户线程,还有就是我们自己手动开启的线程,Thread等

守护线程:JVM内部会启动好多守护线程,像垃圾回收线程等。

区别:

     1、最后一个非守护线程结束时,JVM会正常退出,不管是否有守护线程。(只要有一个用户线程未结束,JVM都不会停止)

如何创建守护线程:

/**
 * 测试守护线程和用户线程
 */
public class ThreadTest {
    public static void main(String[] args) {
        Thread daemonThread = new Thread(new Runnable() {
            @Override
            public void run() {
                for(;;){}
            }
        });
        daemonThread.setDaemon(true);   //调用setDaemon即可这样就能设置为守护线程
        daemonThread.start();          
        System.out.print("main is over");//因为是守护线程,执行完这一步的时候,守护线程就会停止,退出JVM
    }
}

原理:main线程运行结束后,JVM会自动启动一个叫做DestroyJavaVM的线程,该线程会等待所有用户线程结束后终止JVM进程

总结:如果希望主线程结束后JVM马上退出,那么就把线程创建为守护线程,如果希望子线程结束后,JVM才结束,就使用用户线程。


线程创建的三个方法:

1、继承Thread类,重写run方法,无返回值,可以在子类

2、实现Runable接口,重写run方法,无返回值

3、实现Callable接口,重写call方法,然后包装在FutureTask类里面交给Thread执行,有返回值


常见的线程方法:   

wait()等待方法

  当一个线程调用一个共享变量的wait方法的时候,该调用线程会被阻塞挂起,直到其他线程notify或者notifyAll方法返回; 如果其他线程调用了该线程的interrupt方法,则线程会抛出InterruptedException异常

   虚假唤醒:虚假唤醒是指在wait后,不是通过notify系列方法或者中断返回,这种唤醒就称为唤醒(怎么触发的?我理解为操作系统层面误触发的,个人见解)这时候就需要我们写一个循环去不断判断唤醒条件是否被满足,防止虚假唤醒。如:

 synchronized(obj){
     while(条件不满足){
        obj.wait();
    }
 }

 wait(long timeout) :调用该方法后,如果线程在timeout时间内没有被唤醒,就会自动超时唤醒。

 

notify()唤醒方法

  该方法能唤醒一个在该共享变量上调用了wait系列方法后被挂起的线程,一个共享变量上可能有多个线程等待,具体唤醒哪个是随机的,notifyAll()方法会唤醒所有线程,然后它们去竞争共享变量。

 

join()方法

  这个方法会让调用这个方法的线程(当前线程)阻塞,等待调用join方法的对象执行完成返回后,变回就绪状态。如:在主线程里面启动A线程,那么在主线程内部调用A.join()后,主线程被阻塞,等待A线程运行完退出后,主线程才恢复就绪状态。

 

线程睡眠的sleep()方法

  调用这个方法的线程会让出指定时间的执行权,就是在这段时间不参与CPU调度,但是这时候获取的监视器资源(锁等)是不会让出的,指定时间过后变会就绪状态,参与CPU调度,获取到CPU资源后就能继续运行了(时间到了也不一定马上运行,还是得等到获取到CPU资源)。如果在sleep过程被调用interrupt方法,则会在sleep处抛出异常,而且清除中断标记,因此有些时候我们可能需要在异常处理上重新标记中断,否则这个中断无法被捕获。

 

让出CPU执行权的yield()方法

   yield()方法可以让出当前线程的当前时间片剩余时间,返回就绪状态。这时,线程调度器会从线程就绪队列中获取一个优先级高的线程(当前让时间的线程也能又重新获取)来获取CPU执行权。

 

线程的中断:

1、void interrupt() : 中断线程,这个方法会把线程的中断标志设置为true,然后立即返回。这时线程不会马上中断,它会继续执行。如果执行该方法前,线程处于wait系列函数、join、sleep方法被阻塞挂起时,该中断线程会在调用这些方法的地方爆出InterrputedException

2、boolean isInterrupted():检测当前线程(跟哪个线程对象无关,在哪个线程调用,就返回当前线程的中断标志)是否被中断,如果是返回true,不是返回false

3、boolean interrupted():static方法,检测当前线程是否被中断,如果是清除中断标记(与上一个方法的不同地方),然后返回true,如果不是则返回false

为了让大家理解三者的区别,可以看看这个案例,思考一下:

/**
 * 理解interrupt() isInterrupted() interrupted()区别
 */
public class ThreadInterruptTest1 {
    public static void main(String[] args) {
        Thread threadOne = new Thread(new Runnable() {
            @Override
            public void run() {
                for (;;){

                }
            }
        });
        //启动子线程
        threadOne.start();
        //设置子线程中断标志,立即返回,无返回值
        threadOne.interrupt();
        //获取中断标志
        System.out.println("isInterrupted:" + threadOne.isInterrupted());
        //获取中断标志并重置
        System.out.println("interrupted: " + threadOne.interrupted());
        //获取中断标志并重置, 这里跟上面的一样,都是获取主线程的中断标志,所以都是false
        System.out.println("interrupted: " + Thread.interrupted());
        //获取子线程的中断标志
        System.out.println("isInterrputed: " + threadOne.isInterrupted());

        //结果如下:
        //isInterrupted:true
        //interrupted: false
        //interrupted: false
        //isInterrputed: true
    }
}

 

setPriority()方法设置线程优先级

  通过这个方法可以设置线程优先级,Java里面线程优先级由 1 到 10 ,数字越大优先级越高,对资源的抢占能力越强,但是并不能保证什么时候都是高优先级的线程优先获取资源。下面三个是线程内置的三个静态表量,默认为优先级为 5。.

/**
     * The minimum priority that a thread can have.
     */
    public final static int MIN_PRIORITY = 1;

   /**
     * The default priority that is assigned to a thread.
     */
    public final static int NORM_PRIORITY = 5;

    /**
     * The maximum priority that a thread can have.
     */
    public final static int MAX_PRIORITY = 10;

理解线程上下文切换

  在多线程编程中,一般线程个数都大于CPU个数,而CPU每一时刻只能被一个线程使用,因此CPU资源分配采用时间片轮转的策略。当线程当前的时间片使用完成后,会让出CPU,等待下一个时间片到来,继续运行,这时候切换的时候,就要记住自己上一次运行到了哪个地方,即保存当前线程的执行现场,当再次执行时根据保存的执行现场来恢复执行现场。

   线程上下文切换时机:用完时间片处于就绪状态,当前线程被其他线程中断。

 如何减少上下文切换:

  1、无所并发编程:采取一些办法避免使用锁,如根据ID按照Hash算法取模分段,不同线程处理不同数据段。

  2、CAS算法:使用Java的Atomic包的CAS算法来更新数据,不需要加锁(我的一篇文章有介绍CAS,感兴趣可以点进来)

  3、使用最少线程:避免创建不需要的线程。

  4、协程:在单线程里实现多任务调度,并在单线程里维持多个任务区间的切换。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值