题目内容
习题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,难免会有一些阵痛。也因为第一次在这里写博客,想养成良好的习惯,希望尽量记录的详细点,对于这样一个简单的技术点,有点罗嗦,很久以后再来精简和斧正描述错误的图文吧:)