多线程之两个线程交替打印的问题分析
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
多线程之两个线程交替打印的问题分析
场景⼀
在我们⾯试中经常会有这么⼀个场景,就是我们⽤线程A输出“A”字符,有线程B输出“B”字符,交替进⾏,要求A线程执⾏完任务输出:“A线程打印
完了”,B线程执⾏完任务输⼊:“B线程打印完了”,最后有主线程输出⼀句话“我打印完了”!
当你看到这个场景时,有点多线程经验的⼈肯定会感觉很容易,然后有可能进⾏下⾯的实现,上代码:
//A线程类
public class ThreadA extends Thread {
private TestThread testThread;
public ThreadA(TestThread testThread){
this.testThread=testThread;
}
@Override
public void run(){
for(int i=0;i<5;i++){
testThread.printStr("A");
}
System.out.println("a线程打印完了");
}
}
//B线程类
public class ThreadB extends Thread {
private TestThread testThread;
public ThreadB(TestThread testThread){
this.testThread=testThread;
}
@Override
public void run(){
for(int i=0;i<5;i++){
testThread.printStr("B");
}
System.out.println("b线程打印完了");
}
}
//测试类
public class TestThread {
public static void main(String[] args){
TestThread testThread=new TestThread();
ThreadA threadA = new ThreadA(testThread);
ThreadB threadB = new ThreadB(testThread);
threadA.setName("threadA");
threadB.setName("threadB");
threadB.setPriority(2)//这个⽬的是将B线程的优先级降低,让A线程先执⾏,使得在打印的时候先打印“A”,但是我要说但是,在实际测试中你会发现输出的结果并不会像我们预想的⼀样,具体什么原因造成的我们下⾯会解释 threadB.start();
threadA.start();
try {
threadA.join();
threadB.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("交替打印完成");
}
public synchronized void printStr(String str){
String name = Thread.currentThread().getName();
if("A".equals(str)){
System.out.println(name+"-----"+"A");
}else if("B".equals(str)){
System.out.println(name+"-----"+"B");
}
try {
notify();
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
上⾯的代码猛⼀看很完美,但是如果你真正执⾏过之后你会发现出现下⾯的结果,“A”和“B”字符是交替打印了,但是我们想要的三句话,只输出了⼀句,并且
你的idea⼀直处在运⾏状态:
其实如果我们仔细分析代码就不难发现为什么程序⼀直处在没有结束运⾏的状态,并且只打印了⼀句话⽽不是三句话:再出去最后⼀个threadB-----B的时
候结合代码你会发现这是线程B会执⾏nofity唤醒A线程,然后⾃⼰进⼊wait状态,A线程被唤醒后执⾏A线程的run⽅法,但是现在A线程中的run⽅法的for已经
执⾏晚了,也就是没法进⼊for循环体了,所以就直接执⾏了打印语句,这也就是最后⼀⾏打印的原因,⽽因为A线程没有进⼊for循环体,所以没有调⽤同步
⽅法printStr,也就没有办法调⽤⾥⾯的notify来唤醒B线程,那么B线程也就没有办法执⾏打印语句了,⼀直处在wait状态,也就是B线程⼀致处在没有结束的
状态,那⾃然⽽然主线程也不会输出打印语句。
还记得我们在上⾯的代码中说的⼀个线程优先级的问题吗?如果你对这段代码执⾏多次的情况你会发现,第⼀次打印的可不都是“A”字符,还有可能
是“B”,这是为什么呢?
其实这主要使我们对线程优先级理解的⼀个误区,对于线程优先级我们通常的理解是具有较⾼优先级的线程会优先执⾏,其实这句话的理解应该是,在
多线程竞争资源时具有较⾼优先级的线程会有更⼤的概率获取资源,也就是说并不是每次都是它先获取资源,⽽是在⼤量多次的资源竞争中,它获取到资源
的次数会⽐低优先级的线程多。
说了⼀些题外话,我们还是要给出正确的交替打印的代码,供⼤家参考,其实⽅式很多,这⾥只是简单列出⼀种:
其实只需要代码稍微改动就可以解决问题,看代码:
//A线程类
public class ThreadA extends Thread {
private TestThread testThread;
public ThreadA(TestThread testThread){
this.testThread=testThread;
}
@Override
public void run(){
int count=0;
for(int i=0;i<5;i++){
count++;
testThread.printStr(count,"A");
}
System.out.println("a线程打印完了");
}
}
//B线程类
public class ThreadB extends Thread {
private TestThread testThread;
public ThreadB(TestThread testThread){
this.testThread=testThread;
}
@Override
public void run(){
int count=0;
for(int i=0;i<5;i++){
count++;
testThread.printStr(count,"B");
}
System.out.println("b线程打印完了");
}
}
//测试类
public class TestThread {
public static void main(String[] args){
TestThread testThread=new TestThread();
ThreadA threadA = new ThreadA(testThread);
ThreadB threadB = new ThreadB(testThread);
threadB.setPriority(1);
threadA.setName("threadA");
threadB.setName("threadB");
threadB.start();
threadA.start();
try {
threadA.join();
threadB.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("交替打印完成");
}
public synchronized void printStr(int count,String str){
String name = Thread.currentThread().getName();
if("A".equals(str)){
System.out.println(name+"-----"+"A");
}else if("B".equals(str)){
System.out.println(name+"-----"+"B");
}
try {
notify();
if(count!=5){
wait();
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
场景⼆
因为我们上⾯的交替输出的场景过于简单,我们稍微改变⼀下,变成对⼀个字符串'adasdfsafwfvgs'两个线程交替输出字符,最后要求A线程执⾏完任务输出:“A线程打印完了”,B线程执⾏完任务输⼊:“B线程打印完了”,最后有主线程输出⼀句话“我打印完了”!
@Data
public class TestThread {
volatile static boolean open=false;
volatile static int index=0;
static String s="adasdfsafwfvgs";
private ThreadA threadA;
private ThreadB threadB;
public TestThread(){
this.threadA=new ThreadA();
this.threadB=new ThreadB();
}
public class ThreadB extends Thread {
@Override
public void run(){
while(index<s.length()){
if(open){
System.out.println(Thread.currentThread().getName()+"-----"+s.charAt(index++));
open=false;
}
}
System.out.println("b线程打印完了");
}
}
public class ThreadA extends Thread {
@Override
public void run(){
while(index< s.length()){
if(!open){
System.out.println(Thread.currentThread().getName()+"-----"+s.charAt(index++));
open=true;
}
}
System.out.println("a线程打印完了");
}
}
}
测试类:
@RunWith(SpringRunner.class)
@SpringBootTest(classes = KafkaClientApplication.class) public class TestThreadTest {
/**
* 测试使⽤两个线程对字符串交替输出字符的代码
*/
@Test
public void test(){
TestThread testThread=new TestThread();
TestThread.ThreadA threadA = testThread.getThreadA(); TestThread.ThreadB threadB = testThread.getThreadB(); threadA.setName("threadA");
threadB.setName("threadB");
threadA.start();
threadB.start();
try {
threadA.join();
threadB.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("我打印完了");
}
}
测试结果:。