c语言开发网站教程/长春网站提升排名
我们都知道.start()可以启动多线程,使线程进入就绪状态,但是不一定会立即执行。
以下的代码,如果别人问你是A线程先启动,还是B线程先启动,那就要注意他说的启动是什么含义。因为start翻译成中文就是启动。.start()也叫启动线程
A线程先启动,还是B线程先启动?
答案有两个:但说的都是同一件事
答案1:代码从上往下执行的,A线程先启动(指先调用了.start()方法),B线程再启动(后调用.start()方法),但AB线程中的run方法的执行顺序是不确定的,可能A先,可能B先
答案2.随机的,可能A先启动,可能B先(这个启动指的就是run方法的调用了)
大部分人说的启动都是第二种意思:指的是run方法的调用。
我个人偏向第一种解释:把线程的启动和执行概念分开比较好。
.start()方法调用表示启动线程,进入就绪状态。线程具体什么时候执行,不确定。
结论:
多线程的创建/启动是按顺序执行的,主线程会从上往下执行。
多线程的执行顺序是不确定的,谁抢到谁执行
代码验证,循环启动10个线程,每个线程输出当前线程的名字,这里我取了个巧,将i的值设置成当前线程的名字
public class Test {public static void main(String[] args) throws InterruptedException {int i;for (i = 1; i <= 10; i++) {new Thread(() -> {System.out.println(Thread.currentThread().getName());}, String.valueOf(i)).start();System.out.println("此线程已启动,当前线程是" + i);}}
}
虽然每次输出结果都不一样,但万变不离其宗,结论都是一样的:
- 多线程的启动是按顺序执行的,主线程会从上往下依次启动线程
- 虽然多线程的执行顺序是不确定的,但有一点可以确定:线程要先启动后才有机会执行。换句话说,当cpu从main线程切换到其他线程时,会在已启动的线程中,随机抽一个线程来执行。线程要是启动的慢,肯定轮不到你来执行。
此线程已启动,当前线程是1
1
此线程已启动,当前线程是2
此线程已启动,当前线程是3
此线程已启动,当前线程是4
3
2
此线程已启动,当前线程是5
此线程已启动,当前线程是6
此线程已启动,当前线程是7
4
此线程已启动,当前线程是8
5
此线程已启动,当前线程是9
此线程已启动,当前线程是10
6
7
9
10
8Process finished with exit code 0
解释清楚很简单,线程有五个状态:新建、就绪、运行、阻塞、死亡.
- new Thread(),该线程就是新建状态。创建线程和启动线程都是顺序执行的,未创建的线程当然没有资格去抢cpu执行权。
- .start()方法使线程进入就绪状态,这个线程有资格执行。就绪状态中的线程去抢cpu执行权,获取到cpu执行权后,线程进入运行状态,开始调用自己的run方法。
看的更明显一点:
public class Test {public static void main(String[] args) throws InterruptedException {int i;for (i = 1; i <= 10; i++) {System.out.println("main线程:准备创建线程" + i);new Thread(() -> {System.out.println("main线程已切换到其余线程:"+Thread.currentThread().getName()+"已抢到cpu执行权");}, String.valueOf(i)).start();System.out.println("main线程:"+i+"线程已创建,"+i+"线程进入就绪状态");}System.out.println("main线程执行完毕");}
}
main线程准备创建线程4时,还没创建就被打断了。就绪状态的线程中有1,2,3。线程1抢到cpu执行权。4-10线程此时还没创建呢,抢的资格都没有。
这个例子也说明了,如果有其他线程进入就绪状态,main线程随时都有可能被打断
main线程:准备创建线程1
main线程:1线程已创建,1线程进入就绪状态
main线程:准备创建线程2
main线程:2线程已创建,2线程进入就绪状态
main线程:准备创建线程3
main线程:3线程已创建,3线程进入就绪状态
*main线程:准备创建线程4 ####注意这行
已切换到其余线程:1已抢到cpu执行权
main线程:4线程已创建,4线程进入就绪状态
main线程:准备创建线程5
main线程:5线程已创建,5线程进入就绪状态
main线程:准备创建线程6
main线程:6线程已创建,6线程进入就绪状态
main线程:准备创建线程7
main线程:7线程已创建,7线程进入就绪状态
main线程:准备创建线程8
main线程:8线程已创建,8线程进入就绪状态
main线程:准备创建线程9
已切换到其余线程:2已抢到cpu执行权
已切换到其余线程:3已抢到cpu执行权
main线程:9线程已创建,9线程进入就绪状态
main线程:准备创建线程10
main线程:10线程已创建,10线程进入就绪状态
main线程执行完毕
已切换到其余线程:4已抢到cpu执行权
已切换到其余线程:5已抢到cpu执行权
已切换到其余线程:6已抢到cpu执行权
已切换到其余线程:8已抢到cpu执行权
已切换到其余线程:7已抢到cpu执行权
已切换到其余线程:10已抢到cpu执行权
已切换到其余线程:9已抢到cpu执行权
Process finished with exit code 0
- 写代码会经常看到一种现象。先启动的线程大概率会先执行。
- 多线程的执行顺序是不确定的,随机的,谁抢到谁执行,这句话是有前提的。前提是这10个线程都已经进入就绪状态了,大家都在同一起跑线上,那么抢cpu执行权就是随机抢的。
- 但也有可能别的线程还没创建或者还没启动时,你就已经先启动先进入就绪状态,此时其他线程还没就绪状态,无法跟你抢cpu执行权,近水楼台先得月。大家都不在同一起跑线上,随机那也只是进入就绪状态中的线程中随机。遇到这种现象,明白就好。
应该解释的很清楚了。
多线程之间按顺序调用
来自b站视频尚硅谷周阳老师-juc
前面提到,多线程之间是随机调用的。可以顺序调用吗?其实也是可以的。
/*** 多线程之间按顺序调用,实现A->B->C* 三个线程启动,要求如下:* AA打印5次,BB打印10次,CC打15次* AA打印5次,BB打印10次,CC打15次* 重复以上过程来10轮** 1.高聚低合前提下,线程操作资源类* 2.判断/干活/通知* 3.多线程交互中,必须要防止多线程的虚假唤醒,也即(判断只用while,不能用if)* 4.标志位*/
如何实现A->B->C?思路很简单,最开始只允许A线程进入,BC线程即使进入也得等着。然后A精准唤醒B,B精准唤醒C
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;class ShareSource {private int number=1; //1:A 2:B 3:Cprivate Lock lock=new ReentrantLock();private Condition condition=lock.newCondition(); //类似钥匙private Condition condition2=lock.newCondition();private Condition condition3=lock.newCondition();Condition[] conditions={condition,condition2,condition3};//功能都在资源类上,体现了高内聚public void print(int x){lock.lock();try {//判断while (number!=x){ //number初始值是1//有三个线程,谁等?这里需要思考一下//一开始,1!=1,只有A线程能进来干活,BC线程进来就等着。System.out.println("等待并释放锁的索引:"+(x-1));conditions[x-1].await();}//干活for (int i = 1; i <=number*5; i++) {System.out.println(Thread.currentThread().getName()+"\t"+i);}//通知number++;if (number > 3) {number=1; //123123循环x}conditions[number-1].signal(); //A线程精准唤醒B线程,B线程钥匙的数组索引是1}catch (Exception e){e.getStackTrace();}finally {System.out.println((x-1)+"的索引释放了锁");lock.unlock();}}
}/*** 多线程之间按顺序调用,实现A->B->C* 三个线程启动,要求如下:* AA打印5次,BB打印10次,CC打15次* AA打印5次,BB打印10次,CC打15次* 重复以上过程来10轮** 1.高聚低合前提下,线程操作资源类* 2.判断/干活/通知* 3.多线程交互中,必须要防止多线程的虚假唤醒,也即(判断只用while,不能用if)* 4.标志位*/public class ThreadOrderAccess {public static void main(String[] args) throws InterruptedException {ShareSource shareSource = new ShareSource();new Thread(() -> {for (int i = 1; i <=10 ; i++) {shareSource.print(1);}}, "AA").start();new Thread(() -> {for (int i = 1; i <=10 ; i++) {shareSource.print(2);}}, "BB").start();new Thread(() -> {for (int i = 1; i <=10 ; i++) {shareSource.print(3);}}, "CC").start();}
}
以上把打印方法写在了一起,更简单的写法是print方法写成三个
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;class ShareSource {private int number=1; //1:A 2:B 3:Cprivate Lock lock=new ReentrantLock();private Condition condition=lock.newCondition(); //类似钥匙,一把锁三把钥匙private Condition condition2=lock.newCondition();private Condition condition3=lock.newCondition();public void print5(){lock.lock();try {while (number != 1) {//如果number!= 1,说明不是A系统干活condition.await();}for (int i = 1; i <=5 ; i++) {System.out.println(Thread.currentThread().getName()+"\t"+i);}number=2;//A线程通知B线程condition2.signal();}catch (Exception e){e.getStackTrace();}finally {lock.unlock();}}public void print10(){lock.lock();try {while (number != 2) {//如果number!= 2,说明不是B系统干活condition2.await();}for (int i = 1; i <=10 ; i++) {System.out.println(Thread.currentThread().getName()+"\t"+i);}number=3;condition3.signal();}catch (Exception e){e.getStackTrace();}finally {lock.unlock();}}public void print15(){lock.lock();try {while (number != 3) {//如果number!= 3,说明不是C系统干活condition3.await();}for (int i = 1; i <=15 ; i++) {System.out.println(Thread.currentThread().getName()+"\t"+i);}number=1;condition.signal();}catch (Exception e){e.getStackTrace();}finally {lock.unlock();}}
}public class ThreadOrderAccess2 {public static void main(String[] args) throws InterruptedException {ShareSource shareSource = new ShareSource();new Thread(() -> {for (int i = 1; i <=10 ; i++) {shareSource.print5();}}, "A").start();new Thread(() -> {for (int i = 1; i <=10 ; i++) {shareSource.print10();}}, "B").start();new Thread(() -> {for (int i = 1; i <=10 ; i++) {shareSource.print15();}}, "C").start();}
}