Java线程Thread类

Thread方法

start()

start()用来启动一个线程,当调用start方法后,系统才会开启一个新的线程来执行用户定义的子任务,在这个过程中,会为相应的线程分配需要的资源

run()

run()方法是不需要用户来调用的,当通过start方法启动一个线程之后,当线程获得了CPU执行时间,便进入run方法体去执行具体的任务。注意,继承Thread类必须重写run方法,在run方法中定义具体要执行的任务。

sleep()

sleep相当于让线程睡眠,交出CPU,让CPU去执行其他的任务。
但是有一点要非常注意,sleep方法不会释放锁,也就是说如果当前线程持有对某个对象的锁,则即使调用sleep方法,其他线程也无法访问这个对象。

当线程睡眠时间满后,不一定会立即得到执行,因为此时可能CPU正在执行其他的任务。所以说调用sleep方法相当于让线程进入阻塞状态

yield()

调用yield方法会让当前线程交出CPU权限,让CPU去执行其他的线程。它跟sleep方法类似,同样不会释放锁。但是yield不能控制具体的交出CPU的时间,另外,yield方法只能让拥有相同优先级的线程有获取CPU执行时间的机会。

注意,调用yield方法并不会让线程进入阻塞状态,而是让线程重回就绪状态,它只需要等待重新获取CPU执行时间,这一点是和sleep方法不一样的。

join()

调用thread.join方法,则main方法会等待该thread线程执行完毕或者等待一定的时间。如果调用的是无参join方法,则等待thread执行完毕,如果调用的是指定了时间参数的join方法,则等待参数指定的时间。

当调用thread1.join()方法后,main线程会进入等待,然后等待thread1执行完之后再继续执行。
相当与thread1线程插入到当前执行的线程中,当前线程释放占有的锁,进入阻塞状态,CPU执行thread1。

实际上调用join方法是调用了Object的wait方法,wait方法会让线程进入阻塞状态,并且会释放线程占有的锁,并交出CPU执行权限,所以join方法同样会让线程释放对一个对象持有的锁。

interrupt()

单独调用interrupt方法可以使得处于阻塞状态的线程抛出一个异常,它可以用来中断一个正处于阻塞状态的线程;另外,通过interrupt方法和isInterrupted()方法来停止正在运行的线程。
通过interrupt方法可以中断处于阻塞状态的线程。
直接调用interrupt方法不能中断正在运行中的线程。

isInterrupted()能够中断正在运行的线程,因为调用interrupt方法相当于将中断标志位置为true,那么可以通过调用isInterrupted()判断中断标志是否被置位来中断线程的执行。

MyThread thread = test.new MyThread();
 thread.start();
 .....
 thread.interrupt();

class MyThread extends Thread{
    @Override
    public void run() {
        int i = 0;
        while(!isInterrupted() && i<Integer.MAX_VALUE){    //!isInterrupted()判断终端标志
            System.out.println(i+" while循环");
            i++;
        }
    }
}

一般会在MyThread类中增加一个属性 isStop来标志,通过设置标志位的值来控制相关逻辑中的过程

setDaemon和isDaemon

设置线程是否成为守护线程和判断线程是否是守护线程。

守护线程和用户线程的区别在于:守护线程依赖于创建它的线程,而用户线程则不依赖
举个简单的例子:如果在main线程中创建了一个守护线程,当main 方法运行完毕之后,守护线程也会随着消亡。而用户线程则不会,用户线程会一直运行直到其运行完毕。在JVM中,像垃圾收集器线程就是守护线程

线程间协作

wait(),notify(),notifyall()

Object类中三个方法:

public final native void notify();
public final native void notifyAll();
public final native void wait(long timeout) throws InterruptedException;

1、wait()、notify()和notifyAll()方法是本地方法,并且为final方法,无法被重写。
2、调用某个对象的wait()方法能让当前线程阻塞,并且当前线程必须拥有此对象的monitor(即锁)
3、调用某个对象的notify()方法能够唤醒一个正在等待这个对象的monitor的线程,如果有多个线程都在等待这个对象的monitor,则只能唤醒其中一个线程(随机);
4、调用notifyAll()方法能够唤醒所有正在等待这个对象的monitor的线程;

Q&A:为何这三个不是Thread类声明中的方法,而是Object类中声明的方法?
由于每个对象都拥有monitor(即锁),所以让当前线程等待某个对象的锁,当然应该通过这个对象来操作了。而不是用当前线程来操作,因为当前线程可能会等待多个线程的锁,如果通过线程来操作,就非常复杂了

wait方法

调用某个对象的wait()方法,当前线程必须拥有这个对象的monitor(即锁),因此调用wait()方法必须在同步块或者同步方法中进行(synchronized块或者synchronized方法)。

调用某个对象的wait()方法,相当于让当前线程交出此对象的monitor,然后进入等待状态,等待后续再次获得此对象的锁(Thread类中的sleep方法使当前线程暂停执行一段时间(让出CPU执行权限),从而让其他线程有机会继续执行,但它并不释放对象锁);

notify方法

notify()方法能够唤醒一个正在等待该对象的monitor的线程,当有多个线程都在等待该对象的monitor的话,则只能唤醒其中一个线程,具体唤醒哪个线程则不得而知。

调用某个对象的notify()方法,当前线程也必须拥有这个对象的monitor,因此调用notify()方法必须在同步块或者同步方法中进行(synchronized块或者synchronized方法)。

nofityAll()方法

能够唤醒所有正在等待该对象的monitor的线程,

★注意:
notify()和notifyAll()方法只是唤醒等待该对象的monitor的线程,并不决定哪个线程能够获取到monitor。
一个线程被唤醒不代表立即获取了对象的monitor,只有等调用完notify()或者notifyAll()并退出synchronized块,释放对象锁后,其余线程才可获得锁执行。


Condition

用来替代传统的Object的wait()、notify()实现线程间的协作更加安全和高效。
Condition是个接口,基本的方法就是await()signal()方法;
Condition**依赖于Lock接口**,生成一个Condition的基本代码是lock.newCondition()
调用Condition的await()和signal()方法,都必须在lock保护之内,就是说必须在lock.lock()和lock.unlock之间才可以使用
  Conditon中的await()对应Object的wait();
  Condition中的signal()对应Object的notify();
  Condition中的signalAll()对应Object的notifyAll()。


线程的生命活动周期之内各方法调用及状态变换的关系

这里写图片描述

用户线程(User Thread)与守护线程(Daemon Thread)

java中有两类线程:User Thread(用户线程)Daemon Thread(守护线程)
任何一个守护线程都是整个JVM中所有非守护线程的保姆:Daemon的作用是为其他线程的运行提供便利服务

只要当前JVM实例中尚存在任何一个非守护线程没有结束,守护线程就全部工作;只有当最后一个非守护线程结束时,因为没有了被守护者,Daemon也就没有工作可做了,也就没有继续运行程序的必要了,守护线程随着JVM一同结束工作

Thread daemonTread = new Thread(); 
daemonThread.setDaemon(true);  // 设定 daemonThread 为 守护线程,默认false(非守护线程)
daemonThread.isDaemon();// 验证当前线程是否为守护线程,返回 true 则为守护线程 

☆注意:
1) thread.setDaemon(true)必须在thread.start()之前设置,否则会跑出一个IllegalThreadStateException异常。你不能把正在运行的常规线程设置为守护线程。
(2) Daemon子线程也是Daemon线程。
(3) 不是所有的应用都可以分配给Daemon来进行服务,比如读写操作或者计算逻辑,因为你不可能知道在所有的User完成之前,Daemon是否已经完成了预期的服务任务。

JRE判断程序是否执行结束的标准是所有的前台执线程行完毕了,而不管后台线程的状态。

定义:守护线程–也称“服务线程”,在没有用户线程可服务时会自动离开。
优先级:守护线程的优先级比较低,用于为系统中的其它对象和线程提供服务。
设置:通过setDaemon(true)来设置线程为“守护线程”。
示例: 垃圾回收线程就是一个经典的守护线程,当我们的程序中不再有任何运行的
Thread,程序就不会再产生垃圾,垃圾回收器也就无事可做,所以当垃圾回收线程是
JVM上仅剩的线程时,垃圾回收线程会自动离开。

为什么要用守护线程?
我 们知道静态变量是ClassLoader级别的,如果Web应用程序停止,这些静态变量也会从JVM中清除。但是线程则是JVM级别的,如果你在Web 应用中启动一个线程,这个线程的生命周期并不会和Web应用程序保持同步。也就是说,即使你停止了Web应用,这个线程依旧是活跃的。正是因为这个很隐晦 的问题,所以很多有经验的开发者不太赞成在Web应用中私自启动线程。

Spring 为JDK Timer和Quartz Scheduler所提供的TimerFactoryBean和SchedulerFactoryBean能够和Spring容器的生命周期关联,在 Spring容器启动时启动调度器,而在Spring容器关闭时,停止调度器。而如果你在程序中直接使用Timer或Scheduler,如不 进行额外的处理(手动关闭),将会出现这一问题。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值