银行业务调度系统篇
我觉得,总的逻辑是要在main方法中体现的,那么在这个银行业务调度系统中,main方法要做的事情是什么呢,我们来将以前的类的功能来分析一下:
NumberManager类:号码管理器,用来生成来获取服务的新的客户,其中有生成号码的方法,和拿走服务客户的方法;小技巧是使用remove类似队列,先到先服务;
NumberMerchine类:来服务的人需要一个机器为他们来分类,当然设计成单列模式,是因为只需要一个机器来管理这三个对象,含有这三个号码管理器对象的实例以及获取实的方法
WindowService类:具体实现各窗口的服务,那么各窗口要完成什么服务,在这个类中要有体现,这是要对面向对象程序设计的理解啊:两个属性,窗口的类型;窗口的编号;方法一个执行过程start,执行一个线程,根据窗口类型,接受编号,获取服务
常量类和枚举:一个不大不小的工程,涉及了多少变量或者常量,为了方便查询和修改,一个必要的常量类是应该设计的;比如说服务时间,固定常量;
关于线程和Synchronized关键字:
复习到了线程,我对这方面的知识稍微有点模糊,所以复习了一下以前的知识;
对于两个独立的调用线程的方法,方法未加修饰,当在调用两个线程的start方法时,唯一的区别是这两个方法的调用顺序是不确定的,一旦调用方法就会完全执行完,这是唯一的区别,并不会乱序:
package com.jianjian;
import java.util.concurrent.ThreadFactory;
public class ThreadTest1
{
public static void main(String[] args)
{
new thread1().start();
new thread2().start();
}
}
class thread1extends Thread
{
public void run()
{
for(int i = 0; i< 100; i++)
{
System.out.println("李四吃了"+ i+"个馒头");
}
}
}
class thread2extends Thread
{
@Override
public void run()
{
for(int i = 0; i < 100; i++)
{
System.out.println("张三喝了"+i+"杯牛奶");
}
}
}
打印结果只会出现两个输出语句先后顺序的不同
Synchronized关键字,同步的, synchronized关键字可以修饰方法,还有静态代码块(static block)
Java中的每一个对象都有一把锁(lock),只是平时状态下,锁是开着的,当被synchronized关键字所修饰时,就将这把锁激活,当一个对象被多个线程所调用时,由于线程的行进度的不确定性,可能会引发方法内很多问题,而synchronized关键字就是只允许方法每一次只能允许一个线程访问,只有当线程访问结束,或者抛出异常时,才允许下一个线程的介入:
synchronized修饰方法的对象会上锁,如果一个类的多个方法都被synchronized修饰时,当一个线程未访问完毕其中的一个synchronized方法时,其他的线程是不能访问任何该对象的任何一个synchronized方法的,除非你生成了两个对象!
下面我们来一步步的分析:
首先 当一个类中的两个方法都没有被synchronized修饰时,这两个方法的打印将是随机进行的:
package thread;
public class ThreadTest10
{
public static void main(String[]args)
{
Demo demo1 = new Demo();
ThreadTest11test1 = new ThreadTest11(demo1);
ThreadTest12test2 = new ThreadTest12(demo1);
test1.start();
test2.start();
}
}
class Demo
{
public void method1() throwsInterruptedException
{
for(int i = 0 ; i < 15; i ++)
{
Thread.sleep((long)(Math.random()*1000));
System.out.println("hello:"+ i );
}
}
public void method2() throwsInterruptedException
{
for(int i = 0 ; i <15; i ++)
{
Thread.sleep((long)(Math.random()*1000));
System.out.println("world:"+i);
}
}
}
class ThreadTest11extends Thread
{
private Demodemo;
public ThreadTest11(Demo demo)
{
this.demo = demo;
}
@Override
public void run()
{
try
{
demo.method1();
}
catch(InterruptedException e)
{
// TODO Auto-generatedcatch block
e.printStackTrace();
}
}
}
class ThreadTest12extends Thread
{
private Demodemo;
public ThreadTest12(Demo demo)
{
this.demo = demo;
}
@Override
public void run()
{
try
{
demo.method2();
}
catch(InterruptedException e)
{
// TODO Auto-generatedcatch block
e.printStackTrace();
}
}
}
现在我要为上面的两个方法加上synchronized关键字,使之变得有序,如果只加上一个的话,是没有意义的,还是乱序,因为同步方法,可以和非同步方法同时运行;
加上后将会输出有序;先输出hello 或者 world;
银行业务调度系统的NumberManager类用到了单列模式:那么如何验证和创建单列模式呢?
创建单列模式的步骤
1. 构造方法私有化,私有化构造方法,则别的类就不能创建他;
2. 既然不能创建对象,那么也就是无法使用其中的非静态方法了,只能使用静态方法获取这个唯一的实例;
3. 所以应该有一个静态方法返回自己的对象,而这个对象应该是成员变量,传入静态方法中,肯定是私有静态的了
下面是一个完整的单列模式
package com.jianjian;
public class SingletonTest
{
// 构造方法私有化
private SingletonTest()
{
};
// 生成私有实例对象
private static SingletonTest single =new SingletonTest();
// 构造静态方法,获取私有实例对象
public static SingletonTestgetInstance()
{
returnsingle;
}
}
如何验证单例模式
以前说过,一个对象可以生成多个实例,比如每次使用new来生成相同对象的实例,虽然每个实例都是指向同一个对象,但是各个实例之间是独立的,也就是生成了两个对象,这就不是单例模式了;可以通过boolean类型的输出来验证
public class BooleanTest
{
public static void main (String [] args)
{
Parent parent1 = new Parent ();
Parent parent2 = new Parent ();
System.out.println (parent1 == parent2);
}
}
class Parent
{
public void method ()
{
}
}
输出的结果是false;也就是生成的对象不是同一个对象;
使用private关键字和static来生成单例模式
面试的时候,写个单例模式出来;
public class SingletonTest
{
public static void main (String[] args)
{
Parent parent1 = Parent.method ();
Parent parent2 = Parent.method ();
System.out.println (parent1 == parent2);//验证是否为同一个对象
}
}
class Parent
{
static Parent parent = new Parent ();//知识点;这里将实例定义为static 是为了使下面的静态方法可以访问;
//静态的方法只能访问静态的变量
private Parent()
{
}
public static Parent method ( )//定义一个返回类型为Parent的方法,将生成的Parent实例返回给主方法,这样就避免了
//在主方法中生成对象;就完成了单例模式;通过验证可以知道二者生成的对象是同一个对象,指向同一个对象;
{
return parent;
}
}
static可以不用生成对象,而直接使用类的名字去调用类中的方法;
细节错误,一个ArrayList对象储存Integer类型,
如果定义一个方法返回的是int 类型
比如
publicint method ()
{
return list.remove(0)
}
你觉得这样做对不对,确实remove返回的是当前的储存值,如果集合为空的话,那又会怎么样呢,集合为空则remove(0)返回的就是一个异常,你把异常转换成了int,你说成吗。所以写成Integer,如果是空,就返回Integer = null,也是一个对象
服务窗口类写完后,感觉要了自己半条命,我觉得其中涉及到太多的知识点了,首先是方法的重构refector,记住,抽取方法的时候Extract Method ,会让你选择抽出方法后的位置,destination ,有可能是内部类中,所以选择外部类或者内部类吧;
三种类型的窗口,应该使用代码重用,只有部分不同
调试程序的时候出现了异常,什么异常呢,下标越界,为什么会下表越界呢;
Exception in thread"main" java.lang.IndexOutOfBoundsException: Index: 0, Size: 0
atjava.util.ArrayList.RangeCheck(ArrayList.java:547)
atjava.util.ArrayList.remove(ArrayList.java:387)
atcom.heima.jianjian.Test.main(Test.java:12)
没生成号之前你就去取号了,或者,取得比生的还快,于是你写了
public synchronized Integer fetchServiceNumber()
{
return lineQueueNumber.remove(0);//拿走服务号,一举两得
}
要考虑到null的情况,应该这样写
public synchronized Integer fetchServiceNumber()
{
Integer Null = null;
if(lineQueueNumber.size() >0)
return lineQueueNumber.remove(0);//拿走服务号,一举两得
else
returnNull;满足返回的是一个对象就可以了
}
然后于是,你成功了!!!!!!!!!!!
但是呢,还是有一点不完美,打印输出有点乱,这其实是涉及到了线程的选择问题;
被Synchronized关键字修饰的方法,每次只能被一个线程调用,(比如说生成号码,和取号码,不能三个一起来取和生吧)
由于线程选择的问题,最好把输出或者输入写在索的后面,也就是带关键字方法的后面,这样就显得输出干净一点:
Integer clientNum =NumberMerchine.getMerchineInstance()
.getCommonClient().fetchServiceNumber();
System.out.println(windowNum +"号" +windowType + "正在等待服务;");
为什么乱序:cup在执行完生成号码的线程后,本来该执行打印的语句,但是它有可能发现其他的线程空置,去调用其他符合条件的线程了。所以才会乱序;
如果你想让线程和输出一起出来的话,就要把这两个都锁住,但是
有可能死锁;
交通灯管理系统篇
匿名内部中的方法要想访问外部类的成员变量,则外部类的成员变量必须是final的,如果名字相同的话,访问的形式是
OutClass.this.name
void execute(Runnablecommand) 另起一个线程来实现新增车辆功能使用新特性来世先线程调用车辆功能
* 看一下这个方法接受的是一个Runnable接口对象,显然是要用一个实现接口的匿名内部类;
* 必须重写其中的run方法,在run方法中实现,增加车辆的操作
*/
// 把任务交给一个线程池,在线程池中找一个线程来执行这个功能,
小技巧,对于枚举构造枚举的情况,可以将枚举中的枚举变成字符串,在需要变回枚举时使用方法
Enum.valueof(Stringenum)将会返回枚举类型
设计一个Lamp类来表示一个交通灯,每个交通灯都维护一个状态,亮(绿),不亮(红),每个交通灯要有变亮或者变黑的方法,并且能返回自己的亮黑状态
总共有12条路线,所以,系统中总共要产生12个交通灯,右拐弯的路线本来不受灯的控制,但是为了让程序采用统一的处理方式,故假设出四个右拐弯的灯,只是这些灯永远为常亮。
除了右拐弯方向的其他八条路线的灯,他们是两两成对的,可以归为4组,所以,在编程处理时,只要从这四组中各取一个等,对这四个等一次轮番变亮,与这四个方向灯方向对应的灯则随之变化,因此Lampl类中要有一个变量来记住自己相反方向的灯,在一个Lamp对象的变亮和变黑方法中,路对应方向的灯也变亮和变黑,每个灯黑时,都伴随这下一个灯的变亮,Lamp类中还用一个变量来记住自己的下一个灯。
无论在程序的什么地方去获得某个方向的灯时,每次获得的都是同一个实例对象,所以Lamp类改用枚举来做hi很方便的,永远都只代表12个方向的灯的实例对象。