基础补充和加强——希望对后来的人有所帮助
銀行業(yè)務(wù)調(diào)度系統(tǒng)篇
我覺(jué)得,總的邏輯是要在main方法中體現(xiàn)的,那么在這個(gè)銀行業(yè)務(wù)調(diào)度系統(tǒng)中,main方法要做的事情是什么呢,我們來(lái)將以前的類(lèi)的功能來(lái)分析一下:
NumberManager類(lèi):號(hào)碼管理器,用來(lái)生成來(lái)獲取服務(wù)的新的客戶,其中有生成號(hào)碼的方法,和拿走服務(wù)客戶的方法;小技巧是使用remove類(lèi)似隊(duì)列,先到先服務(wù);
NumberMerchine類(lèi):來(lái)服務(wù)的人需要一個(gè)機(jī)器為他們來(lái)分類(lèi),當(dāng)然設(shè)計(jì)成單列模式,是因?yàn)橹恍枰粋€(gè)機(jī)器來(lái)管理這三個(gè)對(duì)象,含有這三個(gè)號(hào)碼管理器對(duì)象的實(shí)例以及獲取實(shí)的方法
WindowService類(lèi):具體實(shí)現(xiàn)各窗口的服務(wù),那么各窗口要完成什么服務(wù),在這個(gè)類(lèi)中要有體現(xiàn),這是要對(duì)面向?qū)ο蟪绦蛟O(shè)計(jì)的理解啊:兩個(gè)屬性,窗口的類(lèi)型;窗口的編號(hào);方法一個(gè)執(zhí)行過(guò)程start,執(zhí)行一個(gè)線程,根據(jù)窗口類(lèi)型,接受編號(hào),獲取服務(wù)
常量類(lèi)和枚舉:一個(gè)不大不小的工程,涉及了多少變量或者常量,為了方便查詢和修改,一個(gè)必要的常量類(lèi)是應(yīng)該設(shè)計(jì)的;比如說(shuō)服務(wù)時(shí)間,固定常量;
?
關(guān)于線程和Synchronized關(guān)鍵字:
復(fù)習(xí)到了線程,我對(duì)這方面的知識(shí)稍微有點(diǎn)模糊,所以復(fù)習(xí)了一下以前的知識(shí);
對(duì)于兩個(gè)獨(dú)立的調(diào)用線程的方法,方法未加修飾,當(dāng)在調(diào)用兩個(gè)線程的start方法時(shí),唯一的區(qū)別是這兩個(gè)方法的調(diào)用順序是不確定的,一旦調(diào)用方法就會(huì)完全執(zhí)行完,這是唯一的區(qū)別,并不會(huì)亂序:
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+"個(gè)饅頭");
????? }
?? }
}
class thread2extends Thread
{
@Override
?? public void run()
?? {
????? for(int i = 0; i < 100; i++)
????? {
??????? System.out.println("張三喝了"+i+"杯牛奶");
????? }
?????
?? }?
}
?
打印結(jié)果只會(huì)出現(xiàn)兩個(gè)輸出語(yǔ)句先后順序的不同
?
Synchronized關(guān)鍵字,同步的, synchronized關(guān)鍵字可以修飾方法,還有靜態(tài)代碼塊(static block)
Java中的每一個(gè)對(duì)象都有一把鎖(lock),只是平時(shí)狀態(tài)下,鎖是開(kāi)著的,當(dāng)被synchronized關(guān)鍵字所修飾時(shí),就將這把鎖激活,當(dāng)一個(gè)對(duì)象被多個(gè)線程所調(diào)用時(shí),由于線程的行進(jìn)度的不確定性,可能會(huì)引發(fā)方法內(nèi)很多問(wèn)題,而synchronized關(guān)鍵字就是只允許方法每一次只能允許一個(gè)線程訪問(wèn),只有當(dāng)線程訪問(wèn)結(jié)束,或者拋出異常時(shí),才允許下一個(gè)線程的介入:
?
?
synchronized修飾方法的對(duì)象會(huì)上鎖,如果一個(gè)類(lèi)的多個(gè)方法都被synchronized修飾時(shí),當(dāng)一個(gè)線程未訪問(wèn)完畢其中的一個(gè)synchronized方法時(shí),其他的線程是不能訪問(wèn)任何該對(duì)象的任何一個(gè)synchronized方法的,除非你生成了兩個(gè)對(duì)象!
下面我們來(lái)一步步的分析:
首先 當(dāng)一個(gè)類(lèi)中的兩個(gè)方法都沒(méi)有被synchronized修飾時(shí),這兩個(gè)方法的打印將是隨機(jī)進(jìn)行的:
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();
????? }
?? }
??
??
}
?
?
?
?
現(xiàn)在我要為上面的兩個(gè)方法加上synchronized關(guān)鍵字,使之變得有序,如果只加上一個(gè)的話,是沒(méi)有意義的,還是亂序,因?yàn)橥椒椒?#xff0c;可以和非同步方法同時(shí)運(yùn)行;
加上后將會(huì)輸出有序;先輸出hello 或者 world;
?
銀行業(yè)務(wù)調(diào)度系統(tǒng)的NumberManager類(lèi)用到了單列模式:那么如何驗(yàn)證和創(chuàng)建單列模式呢?
?
創(chuàng)建單列模式的步驟
1.?????構(gòu)造方法私有化,私有化構(gòu)造方法,則別的類(lèi)就不能創(chuàng)建他;
2.?????既然不能創(chuàng)建對(duì)象,那么也就是無(wú)法使用其中的非靜態(tài)方法了,只能使用靜態(tài)方法獲取這個(gè)唯一的實(shí)例;
3.?????所以應(yīng)該有一個(gè)靜態(tài)方法返回自己的對(duì)象,而這個(gè)對(duì)象應(yīng)該是成員變量,傳入靜態(tài)方法中,肯定是私有靜態(tài)的了
?
下面是一個(gè)完整的單列模式
package com.jianjian;
?
public class SingletonTest
{
?? // 構(gòu)造方法私有化
?? private SingletonTest()
?? {
?? };
?
?? // 生成私有實(shí)例對(duì)象
?? private static SingletonTest single =new SingletonTest();
?
?? // 構(gòu)造靜態(tài)方法,獲取私有實(shí)例對(duì)象
?? public static SingletonTestgetInstance()
?? {
????? returnsingle;
?? }
?
}
?
如何驗(yàn)證單例模式 以前說(shuō)過(guò),一個(gè)對(duì)象可以生成多個(gè)實(shí)例,比如每次使用new來(lái)生成相同對(duì)象的實(shí)例,雖然每個(gè)實(shí)例都是指向同一個(gè)對(duì)象,但是各個(gè)實(shí)例之間是獨(dú)立的,也就是生成了兩個(gè)對(duì)象,這就不是單例模式了;可以通過(guò)boolean類(lèi)型的輸出來(lái)驗(yàn)證 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 () ?????????????? { ?????????????????????????????? ?????????????? } } 輸出的結(jié)果是false;也就是生成的對(duì)象不是同一個(gè)對(duì)象; ? 使用private關(guān)鍵字和static來(lái)生成單例模式 面試的時(shí)候,寫(xiě)個(gè)單例模式出來(lái); public class SingletonTest {????????????? ?????????????? public static void main (String[] args) ?????????????? {???? ?????????????? Parent parent1 = Parent.method (); ?????????????? Parent parent2 = Parent.method (); ?????????????? System.out.println (parent1 == parent2);//驗(yàn)證是否為同一個(gè)對(duì)象 ?????????????? } ?????????????? } class Parent { ?????????????? static Parent parent = new Parent ();//知識(shí)點(diǎn);這里將實(shí)例定義為static 是為了使下面的靜態(tài)方法可以訪問(wèn); ?????????????? //靜態(tài)的方法只能訪問(wèn)靜態(tài)的變量 ?????????????? ?????????????? ?????????????? ?????????????? private? Parent() ?????????????? { ?????????????????????????????? ?????????????? } ?????????????? ?????????????? public static Parent method ( )//定義一個(gè)返回類(lèi)型為Parent的方法,將生成的Parent實(shí)例返回給主方法,這樣就避免了 ?????????????? //在主方法中生成對(duì)象;就完成了單例模式;通過(guò)驗(yàn)證可以知道二者生成的對(duì)象是同一個(gè)對(duì)象,指向同一個(gè)對(duì)象; ?????????????? { ?????????????????????????????? return parent; ?????????????? } } static可以不用生成對(duì)象,而直接使用類(lèi)的名字去調(diào)用類(lèi)中的方法;?
?
?
?
?
細(xì)節(jié)錯(cuò)誤,一個(gè)ArrayList對(duì)象儲(chǔ)存Integer類(lèi)型,
如果定義一個(gè)方法返回的是int 類(lèi)型
比如
publicint method ()
{
????? return list.remove(0)
}
你覺(jué)得這樣做對(duì)不對(duì),確實(shí)remove返回的是當(dāng)前的儲(chǔ)存值,如果集合為空的話,那又會(huì)怎么樣呢,集合為空則remove(0)返回的就是一個(gè)異常,你把異常轉(zhuǎn)換成了int,你說(shuō)成嗎。所以寫(xiě)成Integer,如果是空,就返回Integer = null,也是一個(gè)對(duì)象
?
?
?
?
服務(wù)窗口類(lèi)寫(xiě)完后,感覺(jué)要了自己半條命,我覺(jué)得其中涉及到太多的知識(shí)點(diǎn)了,首先是方法的重構(gòu)refector,記住,抽取方法的時(shí)候Extract Method ,會(huì)讓你選擇抽出方法后的位置,destination ,有可能是內(nèi)部類(lèi)中,所以選擇外部類(lèi)或者內(nèi)部類(lèi)吧;
三種類(lèi)型的窗口,應(yīng)該使用代碼重用,只有部分不同
?
?
?
?
?
?
調(diào)試程序的時(shí)候出現(xiàn)了異常,什么異常呢,下標(biāo)越界,為什么會(huì)下表越界呢;
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)
沒(méi)生成號(hào)之前你就去取號(hào)了,或者,取得比生的還快,于是你寫(xiě)了
public synchronized Integer fetchServiceNumber()
?? ?{
????? ?return?lineQueueNumber.remove(0);//拿走服務(wù)號(hào),一舉兩得
????? ?
?? ?}
??
要考慮到null的情況,應(yīng)該這樣寫(xiě)
public synchronized Integer fetchServiceNumber()
?? ?{
????? ?Integer Null = null;
????? ?if(lineQueueNumber.size() >0)
????? ?return?lineQueueNumber.remove(0);//拿走服務(wù)號(hào),一舉兩得
????? ?else
??????? ?returnNull;滿足返回的是一個(gè)對(duì)象就可以了
?
?? ?}
??
?
然后于是,你成功了!!!!!!!!!!!
?
?
?
?
但是呢,還是有一點(diǎn)不完美,打印輸出有點(diǎn)亂,這其實(shí)是涉及到了線程的選擇問(wèn)題;
被Synchronized關(guān)鍵字修飾的方法,每次只能被一個(gè)線程調(diào)用,(比如說(shuō)生成號(hào)碼,和取號(hào)碼,不能三個(gè)一起來(lái)取和生吧)
由于線程選擇的問(wèn)題,最好把輸出或者輸入寫(xiě)在索的后面,也就是帶關(guān)鍵字方法的后面,這樣就顯得輸出干凈一點(diǎn):
Integer clientNum =NumberMerchine.getMerchineInstance()
?????????? .getCommonClient().fetchServiceNumber();
????? System.out.println(windowNum +"號(hào)" +windowType + "正在等待服務(wù);");
?
?
為什么亂序:cup在執(zhí)行完生成號(hào)碼的線程后,本來(lái)該執(zhí)行打印的語(yǔ)句,但是它有可能發(fā)現(xiàn)其他的線程空置,去調(diào)用其他符合條件的線程了。所以才會(huì)亂序;
如果你想讓線程和輸出一起出來(lái)的話,就要把這兩個(gè)都鎖住,但是
有可能死鎖;
?
?
?
?
?
交通燈管理系統(tǒng)篇
?
匿名內(nèi)部中的方法要想訪問(wèn)外部類(lèi)的成員變量,則外部類(lèi)的成員變量必須是final的,如果名字相同的話,訪問(wèn)的形式是
OutClass.this.name
?
?
void execute(Runnablecommand) 另起一個(gè)線程來(lái)實(shí)現(xiàn)新增車(chē)輛功能使用新特性來(lái)世先線程調(diào)用車(chē)輛功能
??????? ?* 看一下這個(gè)方法接受的是一個(gè)Runnable接口對(duì)象,顯然是要用一個(gè)實(shí)現(xiàn)接口的匿名內(nèi)部類(lèi);
??????? ?* 必須重寫(xiě)其中的run方法,在run方法中實(shí)現(xiàn),增加車(chē)輛的操作
??????? ?*/
??????? // 把任務(wù)交給一個(gè)線程池,在線程池中找一個(gè)線程來(lái)執(zhí)行這個(gè)功能,
?
?
小技巧,對(duì)于枚舉構(gòu)造枚舉的情況,可以將枚舉中的枚舉變成字符串,在需要變回枚舉時(shí)使用方法
Enum.valueof(Stringenum)將會(huì)返回枚舉類(lèi)型
?
設(shè)計(jì)一個(gè)Lamp類(lèi)來(lái)表示一個(gè)交通燈,每個(gè)交通燈都維護(hù)一個(gè)狀態(tài),亮(綠),不亮(紅),每個(gè)交通燈要有變亮或者變黑的方法,并且能返回自己的亮黑狀態(tài)
總共有12條路線,所以,系統(tǒng)中總共要產(chǎn)生12個(gè)交通燈,右拐彎的路線本來(lái)不受燈的控制,但是為了讓程序采用統(tǒng)一的處理方式,故假設(shè)出四個(gè)右拐彎的燈,只是這些燈永遠(yuǎn)為常亮。
除了右拐彎方向的其他八條路線的燈,他們是兩兩成對(duì)的,可以歸為4組,所以,在編程處理時(shí),只要從這四組中各取一個(gè)等,對(duì)這四個(gè)等一次輪番變亮,與這四個(gè)方向燈方向?qū)?yīng)的燈則隨之變化,因此Lampl類(lèi)中要有一個(gè)變量來(lái)記住自己相反方向的燈,在一個(gè)Lamp對(duì)象的變亮和變黑方法中,路對(duì)應(yīng)方向的燈也變亮和變黑,每個(gè)燈黑時(shí),都伴隨這下一個(gè)燈的變亮,Lamp類(lèi)中還用一個(gè)變量來(lái)記住自己的下一個(gè)燈。
無(wú)論在程序的什么地方去獲得某個(gè)方向的燈時(shí),每次獲得的都是同一個(gè)實(shí)例對(duì)象,所以Lamp類(lèi)改用枚舉來(lái)做hi很方便的,永遠(yuǎn)都只代表12個(gè)方向的燈的實(shí)例對(duì)象。總結(jié)
以上是生活随笔為你收集整理的基础补充和加强——希望对后来的人有所帮助的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: InvocationHandler的in
- 下一篇: javaWeb Note1