一,线程的生命周期,线程有哪些状态
1.线程通常有五种状态:创建,就绪 运行 阻塞和死亡状态。
2.阻塞的情况又分为三种:
(1)、等待阻塞:运行的线程执行wait方法,该线程会释放占用的所白资源,JVM会把该线程放入"等待池"中,进入这个状态后,是不能自动唤醒的,必须依靠其他线程调用nolify或nolifyAII方法才能被唤,wait是objecl类的方治
(2)、同步阻塞:运行的线程在获取对象的同步锁时,若该同步锁被别的线程占用,则JVM会把该线程放入“锁池"中。
(3)、其他阻塞:运行的线程执行sleep成join儿法,或者发出了i/o请求时,JVM会把该线程置为阻塞状态,"sleep”状态超时、join等待线程终止或者超时、或者i/o处理完毕时,线程重新转入就绪状态。sleep是Thread类的方法
状态说明:
1).新建状态(New):新创建了一个线程对象。
2).就绪状态(Runnable):线程对象创建后,其他线程调用了该对象的start方法。该状态的线程位于可运行线程池中,变得可运行,等待获取CPU的使用权。
3).运行状态(Running):就绪状态的线程获取了CPU,执行程序代码。
4).阻塞状态(Blocked):阻塞状态是线程因为某种原因放弃CPU使用权,暂时停止运行。直到线程进入就绪状态,才有机会转到运行状态。
5).死亡状态(Dead):线程执行完了或者因异常退出了run方法,该线程结束生命周期。
二,sleep()、wait()、join()、yield()的区别
1.锁池
所有需要竞争同步锁的线程都会放在锁池当中,比如当前对象的锁已经被其中一个线程得到,则其他线程需要在这个锁池进行等待,当前面的线程释放同步锁后锁池中的线程去竞争同步锁,当某个线程得到后会进入就绪队列进行等待cpu资源分配。
2.等待池
当我们调用wait ()方法后,线程会放到等待池当中,等待池的线程是不会去竞争同步锁。只有调用了notify ()或notifyAll()后等待池的线程才会开始去竞争锁,notify()是随机从等待池选出一个线程放到锁池,而notifyAll()是将等待池的所有线程放到锁池当中。
3.sleep和wait的区别:
1)、sleep 是 Thread 类的静态本地方法,wait 则是 Object 类的本地方法。
2)、sleep方法不会释放lock,但是wait会释放,而且会加入到等待队列中。
sleep就是把cpu的执行资格和执行权释放出去,不再运行此线程,当定时时间结束再取回cpu资源,参与cpu的调度,获取到cpu资源后就可以继续运行了。而如果s1eep时该线程有锁,那么s1eep不会释放这个锁,而是把锁带着进入了冻结状态,也就是说其他需要这个锁的线程根本不可能获取到这个锁。也就是说无法执行程序。如果在睡眼期问其他线程调用了这个线程的interrupt方法,那么这个线程也会抛出interruptexception异常返回,这点和wait是一样的。
3)、sleep方法不依赖于同步器synchronized,但是wait需要依赖synchronized关键字。
4)、sleep不需要被唤醒(休眠之后推出阻塞),但是wait需要(不指定时间需要被别人中断)
5)、sleep 一般用于当前线程休眠,或者轮循暂停操作,wait 则多用于多线程之间的通信。
6)、sleep 会让出 CPU 执行时间且强制上下文切换,而 wait 则不一定,wait后可能还是有机会重新竞争到锁继续执行的。
4.yiela和join的区别
1).yield()执行后线程吉接进入就绪状态,马上释放了cpu的执行权,但是依然保留了cpu的执行资格,所以有可能cpu下次进行线程调度还会让这个线程获取到执行权继续执行
2).join()执行后线行进入阻塞状态,例如在线行B中调用线行A的join(),那线行B会进入到阻塞队列,直到线程A结束或中断线程
public static void main (String[] args) throws InterruptedException {
Thread tl = new thread(new Runnable()){
@override
public void run(){
try {
Thread.sleep(3000);
}catch (InterruptedException e){
e.printstackTrace();
}
system.out.printin("22222222");
});
t1.start();
t1. join0;
//这行代码必须要等t1全部执行完毕,才会执行
system.out.printin("1111");
}
22222222
1111
三,说说你对线程安全的理解
不是线程安全、应该是内存安全,堆是共享内存,可以被所有线程访问
当多个线程访问一个对象时,如果不用进行额外的同步控制或其他的协调操作,调用这个对象的行为都可以获得正确的结果,我们就说这个对象是线程安全的
堆是进程和线程共有的空间,分全局堆和局部堆。全局堆就是所有没有分配的空间,局部堆就是用户分配的空间。堆在操作系统对进程初始化的时候分配,运行过程中也可以向系统要额外的堆,但是用完了要还给操作系统,要不然就是内存泄漏。I
在java中,堆是Java虚拟机所管理的内存中最大的一块,是所有线程共享的一块内存区域,在虚拟机启动时创建。堆所存在的内存区域的唯一目的就是存放对象实例,几乎所有的对象实例以及数组都在这里分配内存。
栈是每个线程独有的,保存其运行状态和局部自动变量的。栈在线程开始的时候初始化,每个线程的栈互相独立因此,栈是线程安全的。操作系统在切换线程的时候会自动切换栈。栈空间不需要在高级语言里面显式的分配和释放。
目前主流操作系统都是多任务的,即多个进程同时运行。为了保证安全,每个进程只能访问分配给自己的内存空间,而不能访问别的进程的,这是由操作系统保障的。
在每个进程的内存空间中都会有一块特殊的公共区域,通常称为堆(内存)。进程内的所有线程都可以访问到该区域,这就是造成问题的潜在原因。