- 阻塞状态:
如果一个线程执行了sleep(睡眠)、suspend(挂起)等方法,失去所占用资源之后,该线程就从运行状态进入阻塞状态。在睡眠时间已到或获得设备资源后可以重新进入就绪状态。可以分为三种:
-
等待阻塞:运行状态中的线程执行 wait() 方法,使线程进入到等待阻塞状态。
-
同步阻塞:线程在获取 synchronized 同步锁失败(因为同步锁被其他线程占用)。
-
其他阻塞:通过调用线程的 sleep() 或 join() 发出了 I/O 请求时,线程就会进入到阻塞状态。当sleep() 状态超时,join() 等待线程终止或超时,或者 I/O 处理完毕,线程重新转入就绪状态。(注意,sleep是不会释放持有的锁)
-
死亡状态:
一个运行状态的线程完成任务或者其他终止条件发生时,该线程就切换到终止状态。
实现方式
1继承Thread
class RunnableDemo extends Thread {
@Override
public void run() {
for (int i = 0; i < 100; i++) {
System.out.println(“当前为” + i);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
public class TestThread extends Thread{
public static void main(String [] args){
RunnableDemo test = new RunnableDemo();
test.start();
}
}
2.实现Runnable
class RunnableDemo implements Runnable {
@Override
public void run() {
for (int i = 0; i < 100; i++) {
System.out.println(“当前为” + i);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
public class TestThread extends Thread{
public static void main(String [] args){
RunnableDemo test = new RunnableDemo();
Thread t = new Thread(test);
t.start();
}
}
尽量选择runable
1):适合多个相同的程序代码的线程去处理同一个资源
2):可以避免java中的单继承的限制
3):增加程序的健壮性,代码可以被多个线程共享,代码和数据独立
4):线程池只能放入实现Runable或callable类线程,不能直接放入继承Thread的类
多线程关键字使用
1 | public void start() |
2 | public void run() |
3 | public final void setName(String name) |
4 | public final void setPriority(int priority) |
5 | public final void setDaemon(boolean on) |
6 | public final void join(long millisec) |
7 | public void interrupt() |
8 | public final boolean isAlive() |
9 | public static void yield() |
10 | public static void sleep(long millisec) |
11 | public static boolean holdsLock(Object x) |
12 | public static Thread currentThread() |
13 | public static void dumpStack() |
sleep
线程睡眠的原因:线程执行的太快,或需要强制执行到下一个线程,睡眠后进入runnable中,但是睡眠完毕后不一定立即执行,需要重新争夺cpu,除非拥有更高的优先级,可以使用interrupt()方法打断,抛出InterruptedException 异常。
class RunnableDemo2 implements Runnable {
@Override
public void run() {
for (int i = 0; i < 100; i++) {
System.out.println(“当前为” + i);
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
扩展:Java线程调度是Java多线程的核心,只有良好的调度,才能充分发挥系统的性能,提高程序的执行效率。但是不管程序员怎么编写调度,只能最大限度的影响线程执行的次序,而不能做到精准控制。因为使用sleep方法之后,线程是进入阻塞状态的,只有当睡眠的时间结束,才会重新进入到就绪状态,而就绪状态进入到运行状态,是由系统控制的,我们不可能精准的去干涉它,所以如果调用Thread.sleep(1000)使得线程睡眠1秒,可能结果会大于1秒。
yield
线程让步 根据上图 可以得知 是直接进入准备状态。本线程和其他线程一样机会获取cpu资源。使用yield()的目的是让同样优先级的线程之间能适当的轮转执行。
class RunnableDemo implements Runnable {
@Override
public void run() {
for (int i = 0; i < 50; i++) {
System.out.println(Thread.currentThread().getName() + i);
if (i ==30) {
Thread.yield();
}
}
}
}
public class TestThread extends Thread{
public static void main(String [] args) throws InterruptedException {
RunnableDemo runnable1=new RunnableDemo();
RunnableDemo runnable2=new RunnableDemo();
Thread t1 = new Thread(runnable1,“线程1-”);
Thread t2 = new Thread(runnable1,“线程2-”);
t1.start();
t2.start();
}
}
注意:yield不会抛出异常
通过运行 我们发现 可以线程t1又抢到了资源 依然执行t1 或者t2抢到资源
join
会保证join线程完 先执行
在很多情况下,主线程生成并起动了子线程,如果子线程里要进行大量的耗时的运算,主线程往往将于子线程之前结束,但是如果主线程处理完其他的事务后,需要用到子线程的处理结果,也就是主线程需要等待子线程执行完成之后再结束,这个时候就要用到join()方法了。】
如下图 主线程在t1和t2之前完成了
class RunnableDemo implements Runnable {
@Override
public void run() {
for (int i = 0; i < 10; i++) {
System.out.println(Thread.currentThread().getName() + i);
}
}
}
public class TestThread extends Thread{
public static void main(String [] args) throws InterruptedException {
System.out.println(Thread.currentThread().getName()+“main开始”);
RunnableDemo runnable1=new RunnableDemo();
RunnableDemo runnable2=new RunnableDemo();
Thread t1 = new Thread(runnable1,“线程1-”);
Thread t2 = new Thread(runnable1,“线程2-”);
t1.start();
t2.start();
System.out.println(Thread.currentThread().getName()+ “main结束!”);
}
}
修改如下 可以发现主线程在最后完成
public class TestThread extends Thread{
public static void main(String [] args) throws InterruptedException {
System.out.println(Thread.currentThread().getName()+“main开始”);
RunnableDemo runnable1=new RunnableDemo();
RunnableDemo runnable2=new RunnableDemo();
Thread t1 = new Thread(runnable1,“线程1-”);
Thread t2 = new Thread(runnable1,“线程2-”);
t1.start();
t2.start();
t1.join();
t2.join();
System.out.println(Thread.currentThread().getName()+ “main结束!”);
}
}
其原理是:调用了先获取到子线程的锁然后调用wait方法来实现的,因为当子线程运行结束后会调用notifyall所以主线程会被唤醒并且再次获取到子线程的锁继续运行。
interrupt
https://segmentfault.com/a/1190000018191053。
https://www.cnblogs.com/kaituorensheng/p/10644267.html
wait/notify/notifyall
如图,调用wait(),会使线程休眠,使该线程处于等待池(丢失锁),直到notify()/notifyAll(),会唤醒线程,线程被唤醒被放到锁定池,其他线程释放同步锁使线程回到可运行状态(Runnable)
注意:
1.Obj.wait(),与Obj.notify()必须要与synchronized(Obj)一起使用(在synchronized(Obj){…}语句块内).也就是wait,与notify是针对已经获取了Obj锁进行操作,并且,这三个关键字针对的是同一个监视器(某对象的监视器)。
2.因为wait之后会放开锁,所以,其他线程可以进入同步块执行。直到有其它线程调用对象的notify()唤醒该线程。才干继续获取对象锁。并继续执行。
2.notify()方法,唤醒在此对象监视器上等待的单个线程。如果多个线程在等待 那么会随机选择一个唤醒 而notifyAll()会全部唤醒
4.notify()可以唤醒wait后线程 但是实在本synchronized执行完在唤醒 唤醒后的线程和其他线程一起争夺资源
public class WaitNotify {
public static void main(String[] args) {
final Object A = new Object();
final Object B = new Object();
Thread t1 = new Thread(“t1-thread”) {
@Override
public void run() {
synchronized (A) {
System.out.println(Thread.currentThread().getName() + “拿到 A 的监视器锁”);
System.out.println(Thread.currentThread().getName() + “尝试获取 B 的监视器锁”);
try {
System.out.println(Thread.currentThread().getName() + “休眠 2s,不释放 A 的监视器锁”);
TimeUnit.SECONDS.sleep(2);
System.out.println(Thread.currentThread().getName() + “挂起自己,释放 A 的监视器锁”);
A.wait();
System.out.println(Thread.currentThread().getName() + “被唤醒,等待获取 B 的监视器锁”);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (B) {
System.out.println(Thread.currentThread().getName() + “拿到 B 的监视器锁”);
B.notify();
}
}
}
};
Thread t2 = new Thread(“t2-thread”) {
@Override
public void run() {
synchronized (B) {
System.out.println(Thread.currentThread().getName() + “拿到 B 的监视器锁”);
System.out.println(Thread.currentThread().getName() + “尝试获取 A 的监视器锁”);
synchronized (A) {
System.out.println(Thread.currentThread().getName() + “拿到 A 的监视器锁”);
try {
System.out.println(Thread.currentThread().getName() + “休眠 2s,不释放 A 的监视器锁”);
TimeUnit.SECONDS.sleep(2);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + “挂起自己,释放 A 的监视器锁,唤醒 t0”);
A.notify();
}
try {
System.out.println(Thread.currentThread().getName() + “休眠 2s,不释放 B 的监视器锁”);
TimeUnit.SECONDS.sleep(2);
System.out.println(Thread.currentThread().getName() + “挂起自己,释放 B 的监视器锁”);
B.wait();
System.out.println(Thread.currentThread().getName() + “被唤醒”);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
};
t1.start();
t2.start();
}
}
t2-thread拿到 B 的监视器锁
t2-thread尝试获取 A 的监视器锁
t1-thread拿到 A 的监视器锁
t1-thread尝试获取 B 的监视器锁
t1-thread休眠 2s,不释放 A 的监视器锁
t1-thread挂起自己,释放 A 的监视器锁
t2-thread拿到 A 的监视器锁
t2-thread休眠 2s,不释放 A 的监视器锁
t2-thread挂起自己,释放 A 的监视器锁,唤醒 t0
t2-thread休眠 2s,不释放 B 的监视器锁
t1-thread被唤醒,等待获取 B 的监视器锁
t2-thread挂起自己,释放 B 的监视器锁
t1-thread拿到 B 的监视器锁
t2-thread被唤醒
优先级
static int MAX_PRIORITY:线程可以具有的最高优先级,取值为10。
static int MIN_PRIORITY: 线程可以具有的最低优先级,取值为1。
static int NORM_PRIORITY: 分配给线程的默认优先级,取值为5,主线程默认的优先级。
t.setPriority(1); 设置优先级 数字越高 先执行线程的机会越多
public class TestThread extends Thread{
public static void main(String [] args) throws InterruptedException {
RunnableDemo test1 = new RunnableDemo();
Thread t = new Thread(test1,“线程1-”);
Thread t2 = new Thread(test1,“线程2-”);
Thread t3 = new Thread(test1,“线程3-”);
t.setPriority(1);
t2.setPriority(8);
t3.setPriority(9);
t.start();
t2.start();
t3.start();
}
}
同步
–
线程安全问题:多个线程同时进行,会发现与单线程运行结果不同(经典的卖票问题)
class RunnableDemo implements Runnable {
int x = 10;
@Override
public void run() {
while (x>0){
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
–x;
System.out.println(Thread.currentThread().getName() + x);
}
}
}
public class TestThread extends Thread{
public static void main(String [] args) throws InterruptedException {
RunnableDemo test1 = new RunnableDemo();
Thread t = new Thread(test1,“线程1-”);
Thread t2 = new Thread(test1,“线程2-”);
Thread t3 = new Thread(test1,“线程3-”);
t.start();
t2.start();
t3.start();
}
}
处理办法1:synchronized关键字 会保证当前只有一个线程调用 但是运行效率会变低
class RunnableDemo implements Runnable {
int x = 10;
@Override
public synchronized void run() {
while (x>0){
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
–x;
System.out.println(Thread.currentThread().getName() + x);
}
}
}
自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。
深知大多数Java工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!
因此收集整理了一份《2024年Java开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Java开发知识点,真正体系化!
由于文件比较大,这里只是将部分目录大纲截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且后续会持续更新
如果你觉得这些内容对你有帮助,可以添加V获取:vip1024b (备注Java)
最后
码字不易,觉得有帮助的可以帮忙点个赞,让更多有需要的人看到
又是一年求职季,在这里,我为各位准备了一套Java程序员精选高频面试笔试真题,来帮助大家攻下BAT的offer,题目范围从初级的Java基础到高级的分布式架构等等一系列的面试题和答案,用于给大家作为参考
以下是部分内容截图
一个人可以走的很快,但一群人才能走的更远。不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎扫码加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!
)]
[外链图片转存中…(img-nXqmRODQ-1712888813739)]
[外链图片转存中…(img-4E2YPzQB-1712888813739)]
[外链图片转存中…(img-rc1QYtBB-1712888813740)]
[外链图片转存中…(img-NoUwanZU-1712888813740)]
[外链图片转存中…(img-zo4BrhTG-1712888813740)]
既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Java开发知识点,真正体系化!
由于文件比较大,这里只是将部分目录大纲截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且后续会持续更新
如果你觉得这些内容对你有帮助,可以添加V获取:vip1024b (备注Java)
[外链图片转存中…(img-9oIsdkz4-1712888813740)]
最后
码字不易,觉得有帮助的可以帮忙点个赞,让更多有需要的人看到
又是一年求职季,在这里,我为各位准备了一套Java程序员精选高频面试笔试真题,来帮助大家攻下BAT的offer,题目范围从初级的Java基础到高级的分布式架构等等一系列的面试题和答案,用于给大家作为参考
以下是部分内容截图
[外链图片转存中…(img-9q0xCP6w-1712888813741)]
一个人可以走的很快,但一群人才能走的更远。不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎扫码加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!
[外链图片转存中…(img-iGeRKN9w-1712888813741)]