JAVA疯狂讲义多线程的练习题——习题16.1习题实现回顾

本文通过一个具体的习题,展示了如何使用Java实现多线程间的通信。习题要求创建两个线程,一个打印1到52的数字,另一个打印a到z的字母,且必须按特定顺序交错打印。文章详细介绍了实现过程中的思考与技巧,包括线程同步、异常处理等。

 

题目内容

习题18.1: 写2个线程,其中一个打印1-52,另一个打印a-z,打印顺序应该是12a34b56c……5152,该习题需要利用多线程通信的知识

题目分析

此题目要有利用多线程的只是打印一个能控制序列的字符串,有两个首要的提示:

1、使用多线程;

2、并且使用多线程通信;

初步的思路构想是至少有两个线程类,并且他们之间能相互通信,被协调的调用

仔细阅读完本章的正文后,可以知道,需要增加一个业务类,具体执行线程体种需要执行的单一任务,这样也符合单一职责原则。

所以,我自己参考正文多线程通信的方式,定义了四个单独的java类文件:

NumThread.java——用于打印数字的线程

AbcThread.java——用于打印字母的线程

PrintObj.java——操作执行打印任务的对象

PracThread1.java——调用线程开始的主类

正文中还讲到利用传统线程通信的方式隐式实现线程控制和利用condition进行显式线程控制,两种方法均可。我自己实现了传统模式,后面以传统通信模式的内容继续总结。condition方式成为作业在回复里给出代码和心得。

程序活动图

 

上面的图实际是按照最终的运行结果来写的活动图,可能不符合多线程程序的表达方式,后面如果阅读到相关的资料再来修改吧。

实现过程中的经验总结

1、是否需要import java.util.concurrent.locks.* ?

积累:阅读正文时,对这个很模糊不知道什么时候该加,实际动手以后才知道传统的使用关键自来保证线程同步的方式是默认就支持的,不需要引入任何库,这句话只需要在显式进行lock和condtion时才需要。

2、InterruptedException

 积累:任何线程操作都需要捕获InterruptedException这个异常。一般就是打印下调用栈:ex.printStackTrace

3、字母表的初始化

积累:实现中,很笨的写了一个字母表的字符串数组,多写了很多双引号。了解了下有其他的代替方法,比如写一个字符串(至少不用打那么多引号),然后用 str.split("")方法转换成一个char型的数组,用于最后的循环打印

4、编译

积累:很久没有使用手动编译(其实也就两个月左右),对编译多个文件有点生疏,实际动手了才又知道,如果一个工程里多个Java文件要编译,可以先编译被依赖的类,然后编译使用的类。当然,最好的是几个文件放在一个package中一起编译

5、业务代码的小技巧

积累:首先是单一职责原则,两个线程都是在打印,只是打印的内容不同,可以在一个类里写两个不同的打印方法;

另外,打印数字时,作业要求打印连续两个数字后再打印字符。一开始,我很笨的用循环变量去控制,但又一想,这样可能不符合此题目对线程通信控制的要求。于是才想到了在业务打印方法种使用判断去控制释放同步标志位的方式,即打印奇数时不切换标志位,让当前线程一直保持运行占用状态,打印下一个偶数,然后才置位释放。

 

 if (i%2==0)
 {
      flag = false;
      notifyAll();
 }
       

6、默认实现通信同步的方式是在方法前加入synchronized关键字,这个关键字要放在public后面,返回类型关键字的前面

贴下源代码:

PrintObj.java

import java.util.concurrent.*;


public class PrintObj
{
    public PrintObj() {}
    //定义同步标志位 
    private boolean flag = true ; // true 就打印 number , flase 就打印 Abc
    //printNum
    public synchronized void printNum(int i)
    {
      try{
       if(!flag) 
       {
        //此时正在打印字母,等待同步锁释放
         wait();
       }
       //print num
       System.out.println(i);
       //根据打印要求,当奇数时不释放锁,当偶数时才释放锁
       if (i%2==0)
       {
           flag = false;
           notifyAll();
       }
       }
       catch (InterruptedException ex)
       {
         ex.printStackTrace();
       }


    }

    //printAbc 同样的实现
    public synchronized void printAbc(String abcSqueue) 
    {
        try{
        if(flag)
        {
            //正在打印数字,等待同步锁释放
            wait();
        }
        System.out.println(abcSqueue);
        flag = true ;
        notifyAll();
        }
        catch (InterruptedException ey)
        {
           ey.printStackTrace();
        }
    }

NumThread.java


public class NumThread extends Thread
{
    private PrintObj  printer  = new PrintObj();
    public NumThread(PrintObj printer)
    {
       this.printer = printer; 
    }
    
    //void run
    public void run()
    {
        for(int i = 1; i < 53; i++)
        {
            printer.printNum(i);
        }
    }
}

AbcThread.java

public class AbcThread extends Thread
{
    private PrintObj printer = new PrintObj();
    private String[] abcString = {"a","b","c","d","e","f","g","h","i","j","k","l","m","n","o","p","q","r","s","t","u","v","w","x","y","z"};
    public AbcThread(PrintObj printer)
    {
        this.printer = printer;
    }
    // void run
    public void run()
    {
        for(int i = 0; i< abcString.length; i++)
        {
            printer.printAbc(abcString[i]);
        }    
    }
    
}

PracThread1.java

public class PracThread1
{
    public static void main(String[] args)
    {
        PrintObj printer  = new PrintObj();
        new NumThread(printer).start();
        new AbcThread(printer).start();
    }
}

结束

作为一个不得章法的人,把ruby作为工作语言以后,再回头过来学习JAVA,难免会有一些阵痛。也因为第一次在这里写博客,想养成良好的习惯,希望尽量记录的详细点,对于这样一个简单的技术点,有点罗嗦,很久以后再来精简和斧正描述错误的图文吧:)

 

 

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值