java线程
进程和线程
进程:进程就是运行中的程序,启动一个程序后,操作系统就是为该进程分配一个内存空间。 线程:线程是由进程创建的,是进程的一个实体。一个进程可以拥有多个线程。可以理解为一个进程包含一个或者多个线程。
进程:
并发与并行
1 并发:同一时刻,多个任务交替执行,造成一种貌似同时的错觉。单核cpu实现的多任务就是并发。
2 并行:同一时刻,多个任务同时执行,多核cpu可实现并行。
线程的状态
线程初始状态(NEW):
当前线程处于线程被创建出来但没有被调用start()
,当前一个继承Thread类的实例化
线程运行状态(RUNNABLE)
线程被调用了start()
等待运行的状态。
一般实现Runnable接口 或者 继承Thread类的类,实例化一个对象出来,线程就进入了初始状态
Thread thread = new Thread()
线程运行状态(RUNNABLE)
线程被调用了start()
等待运行的状态,其中thread.start()
方法的源码中,会去调用start0()
方法,而start0()
是private native void start0();
线程阻塞状态(BLOCKED)
需要等待锁释放或者说获取锁失败时,线程阻塞
@Override public void run()
{
synchronized (BlockedThread.class)
{
while (true){ }
}
}
}
WAITING
这个状态下是指线程拥有了某个锁之后, 调用了他的wait方法, 等待其他线程/锁拥有者调用 notify / notifyAll 一遍该线程可以继续下一步操作, 这里要区分 BLOCKED 和 WATING 的区别, 一个是在临界点外面等待进入, 一个是在临界点里面wait等待别人notify, 线程调用了join方法 join了另外的线程的时候, 也会进入WAITING状态, 等待被他join的线程执行结束
TERMINATED
这个状态下表示 该线程的run方法已经执行完毕了, 基本上就等于死亡了(当时如果线程被持久持有, 可能不会被回收) 超时等待状态(TIMED_WAITING),也叫限期等待,可以在指定的时间后自行返回而不是像 WAITING 那样一直等待。它和线程等待状态(WAITING)状态 非常相似,区别就是方法的参数传入限制时间,在 Timed Waiting
状态时会等待超时,之后由系统唤醒,
join
Thread类中的join方法的主要作用能让线程之间的并行执行变为串行执行,当前线程等该加入该线程后面,等待该线程终止
public static void main(String[] args) {
Thread thread = new Thread();
thread.start();
thread.join();
...
}
上面一个例子表示,程序在main主线程中调用thread线程的join方法,意味着main线程放弃CPU时间片(主线程会变成 WAITING 状态),并返回thread线程,继续执行直到线程thread执行完毕,换句话说在主线程执行过程中,插入thread线程,还得等thread线程执行完后,才轮到主线程继续执行
如果查看JDKthread.join()
底层实现,会发现其实内部封装了wait(),notifyAll()
park/unpark
LockSupport.park() 挂起当前线程;LockSupport.unpark(暂停线程对象) 恢复某个线程
当程序调用LockSupport.park()
,会让当前线程A的线程状态会从 RUNNABLE 变成 WAITING,然后main主线程调用LockSupport.unpark(thread)
,让指定的线程即线程A,从 WAITING 回到 RUNNABLE 。我们可以发现 park/unpark
和wait/notify/notifyAll
很像,但是他们有以下的区别:
-
wait,notify 和 notifyAll 必须事先获取对象锁,而 unpark 不必
-
park、unpark 可以先 unpark ,而 wait、notify 不能先 notify,必须先wait
-
unpark 可以精准唤醒某一个确定的线程。而 notify 只能随机唤醒一个等待线程,notifyAll 是唤醒所以等待线程,就不那么精确
TERMINATED
这个状态下表示 该线程的run方法已经执行完毕了, 基本上就等于死亡了(当时如果线程被持久持有, 可能不会被回收)
线程常用的方法
1 setName //设置线程名称,使之与参数name相同 2 getName //返回线程的名称 3 start //是该线程开始执行;java虚拟机底层调用该线程的start0()方法 4 run //调用该线程的run方法 5 setPriority //更改线程的优先级 6 getPriority //获取线程的优先级 7 sleep //在指定的毫秒数内让当前正在执行的线程休眠(暂停执行) 8 interrupt //中断线程,不是停止,一般用于正在休眠的线程 9 线程优先级:MAX_PRIORITY MIN_PRIORITY NORM_PRIORITY
互斥锁
1 Java语言中,引入了对象互斥锁的概念,来保证共享数据操作的完整性。
2 每个对象都应于一个可称为 "互斥锁" 的标记,这个标记用来保证在任一时刻,只能有一个线程访问该对象。
3 关键字synchronized来与对象的互斥锁联系。当某个对象用synchronized修饰时,表明该对象在任一时刻只能由一个线程访问。
4 同步的局限性:导致程序执行效率降低。
5 同步方法(非静态的)的锁可以是this,也可以是其他对象(要求是同一个对象)。
6 同步方法(静态的)的锁为当前类本身。
线程的死锁
多个线程都占用了对方的锁资源,但不肯想让,导致了死锁,在编程中一定要避免死锁的发生。
释放锁
会释放锁的情况
1 当前线程的同步方法、同步代码块执行结束。例如上完厕所,完事出来。
2 当前线程在同步代码块、同步方法中遇到break、return。例如没有正常的完事,经理叫他修改bug,不得已出来。
3 当前线程在同步代码块、同步方法中出现了未处理的Error或Exception,导致异常结束。例如没有正常的完事,突然地震了,急忙跑出来。
4 当前线程在同步代码块、同步方法中执行了线程对象的wait()方法,当前线程暂停并释放锁。例如一直蹲着,突然没了感觉,一直拉不出来,出来等会再进去。
不会释放锁的情况
1 当前线程在同步代码块、同步方法时,程序调用Thread.sleep()、Thread.yield()方法暂停当前线程的执行,不会释放锁。上厕所,太困了,在坑位上迷了一会。
2 当前线程在同步代码块、同步方法时,其他线程调用了该线程的suspend()方法将线程挂起,线程不会释放锁。应尽量避免使用suspend
转载自:https://juejin.cn/post/7267475679824937023