Aqueles que leram meus artigos anteriores podem saber que estou fazendo o desenvolvimento de jogos. Muitas das minhas idéias e pontos de partida são tratadas de acordo com as idéias de jogos, portanto, pode haver conflitos com a web e não está de acordo com ela.
Deixe -me dizer por que eu quero personalizar o modelo de thread?
De acordo com o jogo MMORPG ou MMORPG que eu fiz, os threads são divididos em threads principais, threads de sincronização global, thread de bate -papo, threads de equipe, threads de mapa e distribuição de mensagens e thread de entrega, etc.
Algumas colunas precisam ser controladas de acordo com minha divisão e fluxo de dados.
A principal coisa a fazer no servidor de jogo é aceitar a solicitação de comando do jogador -> operação correspondente -> retornar o resultado;
Todas as mensagens no lado do servidor serão registradas no gerenciador de mensagens e, em seguida, o modelo de thread será especificado quando a mensagem for registrada.
Se a mensagem precisar ser enviada ao thread do mapa do jogador para processamento, ao registrar a mensagem, você deverá usar o modelo do thread (distribuição de mensagens e thread de despacho);
Vamos analisar primeiro o modelo de thread;
Antes de olhar para o código do modelo de thread, primeiro olho para o meu modelo de tarefa
pacote net.sz.engine.thread; importar java.io.serializable; importar org.apache.log4j.logger; importar net.sz.engine.structs.objectttribute; importar net.sz.engine.structs.objectglobal;/** ** ** Modelo * * <br> * autor * autor <brmer> <brotmer> * 13882122019 <br> */Classe abstrata pública Taskevent implementa serializável, clonável {private estático logger final logger = logger.getLogger (taskevent.class); private estático final serialversionUid = 4196020659994845804L; // dados de tempo de execução Transiente privado Final ObjectAttribute runOther = new ObjectAtTribute; // tempo de criação de tarefas protegidas pelo tempo de createTime; // Tarefa ID exclusiva protegida TaskID long; // cancelado Boolean Protected Boolean cancel = false; public taskevent {this.runother.put ("submitime", System.currenttimemillis); CreateTime = System.CurrentTimemillis; cancel = false; TaskId = objectglobal.GeTuUID; } public Long GetCreateTime {return createTime; } public void setCreateTime (Long CreateTime) {this.CreateTime = CreateTime; } public long getSubmittime {return this.runother.getlongValue ("submitime"); } public ObjectAttribute getRunother {return runother; } public boolean iScancel {return cancel; } public void setCancel (cancelamento booleano) {this.cancel = cancel; } public abstrato void run; @Override Public Object Clone lança clonenotsupportedException {return super.clone; // Para alterar o corpo de métodos gerados, escolha Ferramentas | Modelos. }} pacote net.sz.engine.thread;/** * executor do timer * * <br> * Programador com falha do autor / *** Hora de iniciar a execução*/ Long StartTime protegido; / *** se deve executar uma vez no começo*/ Fartação booleana protegida; / *** Hora de término*/ EndTime de longo prazo protegido; / *** Número de execuções*/ Protected int ActionCount; / *** Tempo de execução do intervalo*/ intervalo de proteção protegido; / **** @param startTime Especifique o tempo de início* @param isStartAction Se deve executar uma vez no começo* @param final de fim especificar o tempo de término* @param ActionCount Especifique o número de execuções* @param intervalo {) especificar o tempo de intervalo*/ public timerTaskevent (starttime longtime, boolean isstartAttAndTime; this.startTime = startTime; this.startAction = isStartAction; this.endtime = endtime; this.actionCount = ActionCount; this.interValTime = intervaloTime; } /** * Specify the start execution time of the task* * @param startTime Specify the start time* @param isStartAction Whether it is executed once at the beginning* @param actionCount Specify the number of executions* @param intervalTime Specify the interval time*/ public TimerTaskEvent(long startTime, boolean isStartAction, int actionCount, int intervalTime) { this(startTime, isStartAction, 0, ActionCount, intervalo); } /** * The end time specified is the end time, and the number of executions is not necessarily enough* * @param isStartAction Whether to execute once at the beginning* @param endTime Specify the end time* @param actionCount Specify the number of executions* @param intervalTime Specify the interval time* */ public TimerTaskEvent(boolean isStartAction, long endTime, int actionCount, int intervalTime) { Este (0, isStartAction, Endtime, ActionCount, IntervaloTime); } / *** Especifique o horário de início e o término** @param StartTime Especifique o tempo de início* @param EndTime Especifique o tempo de término* @param intervaloTime Especifique o tempo de intervalo* / public TimerTaskevent (tempo de início longo, tempo longo, intimime int) {this (starttime, false, endtime, final, -1, intervalado); } / *** Tempos de execução especificados e tempo de intervalo** @param ActionCount Especifique o número de execuções* @param intervaloTime Especifique o tempo de intervalo* / public TimerTaskevent (int ActionCount, Int IntervalTime) {this (0, False, 0, ActionCount, intervaloTime); } / *** Execução ilimitada após envio* @param intervalTime Tempo de intervalo especificado* / public TimerTaskevent (Int intervalTime) {this (0, false, 0, -1, intervaloTime); } public long getStartTime {return startTime; } public void setStartTime (Long StartTime) {this.startTime = startTime; } public boolean isStartAction {return startAction; } public void setStartAction (boolean startAction) {this.startAction = startAction; } public Long GEETENDTIME {Return EndTime; } public void setendtime (longo final) {this.endtime = endtime; } public int getActionCount {return ActionCount; } public void setActionCount (int ActionCount) {this.actionCount = ActionCount; } public int getIntervaltime {return intervalTime; } public void setIntervaltime (int intervalTime) {this.intervaltime = intervalTime; }}Aqui estão o modelo de tarefas e o modelo de tarefa do timer;
pacote net.sz.engine.Thread; importar java.util.ArrayList; importar java.util.list; importar java.util.concurrent.concurrentLinkedQueue; import.szz.engine.structs.objectglobal; import.szine.engine.utils.mailil; org.apache.log4j.logger; importar org.jboss.jandex.main;/** * Modelo de thread * <br> * Programador fracassado do autor Logger.getLogger (threadmodel.class); private estático longo threadID = 0; Objeto final estático protegido syn_object = new Object; Tid longo protegido; nome da string protegida; Protegido LongSendMail = 0; Final ArrayList protegido <Mythread> threads = new ArrayList <>; /*** Lista de tarefas Lista de tarefas seguras de thread*/// Lista final protegida <TaskModel> TaskQueue = new ArrayList <>; Final ConcurrentLinkedQueue protegido <Taskevent> TaskQueue = novo ConcurrentLinkedQueue <>; / ***/ Lista final protegida <TemerTaSkevent> timerQueue = new ArrayList <>; // Falso Identidade Excluir thread protegido volátil booleano em execução = true; public ThreadModel (grupo ThreadGroup) {this (grupo, "sem nome", 1); } public threadModel (nome da string) {this (threadpool.unknownthreadgroup, nome, 1); } public ThreadModel (grupo ThreadGroup, nome da string, int threadCount) {this (grupo, nome, threadcount, nulo); } public threadModel (grupo ThreadGroup, nome da string, int threadCount, runnable runnable) {sincronizado (syn_object) {threadId ++; tid = threadId; } para (int i = 1; i <= threadcount; i ++) {threads mythread; if (runnable == null) {thread = new mythread (tid, grupo, this, nome + "-" + tid + "-" + i);} else {thread = new mythread (tid, grupo, runnable, nome + "" } thread.start; threads.add (thread); } this.name = nome; } / ** * Nome do thread * * @return * / public String getName {Nome de return; } / ** * Obtenha o ID personalizado do thread * * @return * / public Long GetId {return this.tid; } / ** * Adicione uma nova tarefa para cada nova tarefa, a fila de tarefas deve ser despertada * * @param runnable * / public void addtask (taskevent runnable) {taskQueue.add (runnable); Sincronizado (TaskQueue) {/ * Acorde a fila e comece a executar */ TaskQueue.NotifyAll; }}/** * Adicione uma tarefa do timer ao thread * * @param runnable */public void addtimer (timerTaskevent runnable) {synchronized (timerQueue) {if (runing) {// execute uma vez no início se (runnable.startaction) {addtask (runnable); } timerQueue.add (runnable);} else {log.error ("Thread parou"); }}}} // <Editor-Fold defaultState = "colapsed" desc = "thread do thread do time public void timerrun"> / *** thread do thread executor* / public void timerrun {ArrayList <TimerTaskevent> TaskModels; Sincronizado (TimerQueue) {// Se a fila não estiver vazia, retire o timer da fila TaskModels = new ArrayList <> (TimerQueue); } if (! taskModels.isEmpty) {for (timeTaskevent timerevent: taskModels) {int ExECCount = timerevent.getRunother.getTinValue ("ExecCount"); Long LastTime = timeRevent.getRunother.getLongValue ("LastExectime"); long nowtime = System.currenttimemillis; if (lastTime == 0) {timerevent.getRunother.put ("lastExectime", NowTime);} else if (timerevent.iscel) {// se a tarefa tiver sido cancelada sincronizada (timeerqueue) {tileue.roMovent); } log.debug ("Limpe a tarefa do timer:" + timerevent.getclass.getName);} else if (nowtime> timerevent.getStartTime // se o tempo de início é satisfeito && (agora o tempo <timeEvent.getSubMittime> timeEvent.getInterVimevent) // se o intervalo <getSubMission Aftermission e timerevent.getInTimevent) // se o intervalo <s = stickiEd após o signo (timeTime) (timeTime) (não é o tempo <gettime em = timeTein. timerevent.getEndtime) // Determine o tempo de término && (nowTime - lastTime> = timerevent.getIntervaltime)) // Determine se o tempo de intervalo foi satisfeito desde a última execução {// o timer de execução de envio é o primeiro a executar this.AddTask (timerent); ExecCount); TimeRevent.GetRuNother.put ("LastExectime", NowTime); NowTime = System.CurrentTimEmillis; // Determine a condição de exclusão se ((timeRevent.getActionTime> 0 && timeRevent <Terevent.GeTENDTime) || (timeEvent.GetAction> 0 && timeEvent <Terevent.getEnTime) || (timeEvent.GetCount> TimerQueue.Remove (TimeRevent); } log.debug ("Tarefa do timer de limpeza:" + timerevent.getclass.getName); }}}}}}} // </editor-fold> // <editor-Fold defaultState = "colapsed" desc = "ver thread Stack Public void showstacktrace">/****** View Thread Stack*/public void ShowStackTrace {StringBuilder BURF = new StringBilder; para (mythread currentThread: threads) {long procc = system.currenttimemillis - currentThread.getLastExecutime; if (Procc> 5 * 1000 && procc <86400000l) {// Thread de 10 dias // porque o tempo de operação Multithread pode ser inadequado buf.append ("" "" "). STUCK-> ") .APNEND (PROCC/1000F) .APNEND (" S/N ") .APNEND (" Execute Task: ") .Append (CurrentThread.getLastCommand.getClass.getName); Try {StackTraceElement Elements = currentThread.getStackTrace; (Int I = 0; .Apênd (elementos [i] .getclassName) .Append (".") .Append (Elements [i] .getMethDName) .Append ("("). Append (elementos [i] .getFilename) .Append (""; }} catch (Exceção e) {buf.append (e); } buf.append ("/n ++++++++++++++++++++++++++++++++++"); }} String tostring = buf.toString; if (! stringutil.isnullorEmpty (tostring)) {log.error (tostring); if (system.currenttimemillis-lastsendmail> 5 * 60 * 1000) {lastSendmail = System.CurrentTimEmillis; Mailutil.SendMail ("ThreadMail Stunt) Objectglobal.platform + "Id Server-" + Objectglobal.ServerID, ToString); }}} // </editor-fold> @Override public void run {mythread currentThread = (mythread) thread.currentThread; while (running) {while (taskQueue.isempty && Running) {try {/* A fila de tarefas está vazia, então uma nova tarefa está aguardando uma nova tarefa entrar e ser acordada*/sincronizada (TaskQueue) {TaskQueue.wait (500); }} catch (interruptedException ie) {log.error (ie); }}/* Obtenha a tarefa para executar*/if (running) {currentThread.LastCommand = null; currentThread.lastCommand = taskQueue.poll; } if (currentThread.lastCommand! = null) {if (currentThread.lastCommand.iscel) {// se a tarefa foi cancelada, continue; }/* Execute a tarefa* /// r.SetSubmittImel; currentThread.LASTEXECETIME = System.currenttimemillis; try {currentThread.LastCommand.run;} Catch (exceção e) {log.error ("trabalhador <" " + currentThread.GetName +" "> execute Foi encontrado um erro: ", e); } long timel1 = System.CurrentTimemillis - CurrentThread.LASTEXECETIME; if (timel1 <= 20) {} else if (timel1 <= 100l) {log.info ("trabalhador <" " + currentThread.getName +" "> concluiu a tarefa:" + currentThread.LastCommand.tostring + "Execution time:" + timel1);} elsengemand.toString + " + ""> Execução a longo prazo da conclusão da tarefa: " + currentThread.LastCommand.ToString +" "Considere" o script de tarefas lógica-consumindo: " + timel1);} else {log.info (" trabalhador <"" + currentthread.getname + ""> execute de tarefa " deve ser excluído "o script de tarefa demorado:" + timel1); } currentThread.LASTEXECETIMENTE = 0; }} log.error ("thread termina, trabalhador <" " + thread.currentThread.getName +" "exit"); } / *** Tópico personalizado* / public classe mythread estende o thread { / **** @param tid thread Id* @param grupo* @param run método de execução* @param nome thread name* / public mythread (tid longo, threadgroup Group, runnable run, string name) {(grupo, name); } // ID personalizado do thread public Long _id; // Tarefa de execução Taskevent volátil public Taskevent LastCommand; // Hora de começar a executar a tarefa Public Volatile LongExecuteTime ATime = 0; public taskevent getLastCommand {return lastCommand; } public Long GetLastexEctenTime {return lastExecuteTime; } / ** * Retorne thread Id personalizado * * @return * / @Override public Long GetId {return _id; }} / *** Pare o thread, defina o estado de parada do thread e não encerrará o thread imediatamente* / public void Stop {this.Runing = false; } public boolean isruning {return Running; } @Override public string tostring {return "thread {" + "tid =" + tid + ", name =" this.getName + '}'; }}Eu construí a partir de threadmodel
public ThreadModel (grupo ThreadGroup, Nome da String, Int ThreadCount, Runnable Runnable) {Synchronized (Syn_Object) {ThreadId ++; Tid = ThreadID; } para (int i = 1; i <= threadcount; i ++) {threads mythread; if (runnable == null) {thread = new mythread (tid, grupo, this, nome + "-" + tid + "-" + i);} else {thread = new mythread (tid, grupo, runnable, nome + "" } thread.start; threads.add (thread); } this.name = nome; }Como pode ser visto, aqui eu corro a aula de thread mythread declarada
Por que eu penso sobre isso? Por exemplo, se eu estiver processando dados de redação de logs, como dados compartilhados e nenhum fluxo de processamento de área crítica de thread, posso considerar o uso de n threads para processar esse trabalho; não produzirá dados sujos;
Se eu quiser se unir e solicitar habilidades de elenco, preciso processar uma única fila, então deve haver apenas um mythread no ThreadModel. Isso não conta como execução serial do modo de bloqueio (ou execução da fila) para resolver o problema de compartilhar dados e segmentar a área crítica, que não depende mais de bloqueios;
Eu sou muito feio, por favor me perdoe
Conforme mostrado na figura acima, haverá duas filas em cada modelo de ThreadM, um timetaskevent e o outro Taskvent, e haverá um tópico global do timer;
A função do thread do timer global é processar e descobrir que o timetaskevent no modelador de threads precisa ser executado, para que seja adicionado à equipe do Taskvent; A execução final é a fila do Taspvent
Por que os timetaskevent devem ser armazenados no modelo de threads correspondente? Isso ocorre porque, por exemplo, depois que meu thread A (Instância de Threadmodel) é executado por um período de tempo, preciso fechar e liberar recursos, então tenho que ir a outros lugares para encontrar o horário correspondente e removê -lo;
pacote net.sz.engine.thread; importar java.util.hashmap; importar java.util.map;/** * * <br> * Programador de queda do autor public timerThread {super (threadpool.globlthreadgroup, "Global Timer Thread"); } @Override public void run {while (true) {synchronized (syn_object) {try {syn_object.wait (2);} catch (interruptedException ex) {}} hashmap <long, threadmodel> hashmap = new hashmap <> (<> hashmap.entryset) {long key = entradas. value.timerrun; }}}}Gerenciador de modelos de thread
pacote net.sz.engine.thread; importar java.util.hashmap; importar java.util.concurrent.concurrenthashmap; importar net.sz.engine.script.manager.scriptManager; import.sz.engine.timer.globtimEvent; importemMermManager; org.apache.log4j.logger;/** * Thread Manager * * <br> * Programador com falha do autor Public final estático Long Globlthread; estático privado final TimerThreadGLOBLTIMERTHRADO; estático final longo de checkThreadTimerThreadModel; estático public final threadgroup GLOBLTHREADGROUP = new ThreadGroup ("Global ThreadGroup"); estático public final threadgroup UnknownThreadGroup = new ThreadGroup (GLOBLTHREADGUROP, "Unknown ThreadGroup"); estático privado final Concurrenthashmap <long, threadmodel> threadmap = new concorrentehashmap <>; public static void main (string [] args) {threadpool.addtimerTask (globlThread, new TimerTaskevent (1000) {@OverridePublic void run {Log.error ("ssssss");}}); } static {// Crie o thread global globlthread = addThreadModel (GLOBLTHREADGRUPL, "GLOBLTHREAD"); // Execute o tempo de tarefa especificado para acionar os passos AddtimerTask (GLOBLTHREAD, NEW GLOBTIMEVERENT (scriptManager.getInstance.getBaseScriptEntry)); // Consulta o modelo de tempo de consumo do servidor AddImerTask (GLOBLTHREAD, NOVA PRINTLNSERVERMEMORYTIMENTEVENT); // Crie o thread do timer GLOBLTIMERTHRADE = new TimerThread; GLOBLTIMERTHREAD.START; // Verifique o thread RECUCLO CHECKTHEADTIMERTHREADMODEL = AddThreadModel (GLOBLTHREADGUROP, "Verifique o Evento Threadtimer"); addTimerTask (checkThreadTimerThreadModel, new CheckThreadTimeRevent); } / ** * Ao excluir o modelo de encadeamento de ID especificado, defina o status no estado de parada * * @param tid * @return * / static public ThreadModel Remover (TID longo) {ThreadModel Remover = Threadmap.remove (Tid); if (remover! = null) {remover.stop; } retornar remover; } / ** * Obtenha todos os threads no pool de threads * * @return * / estático público concorrentehashmap <long, threadmodel> getThreadmap {return threadmap; } / ** * Obtenha um thread no thread pool * * @param threadId * @return * / static public ThreadModel getThreadModel (Long ThreadId) {ThreadModel get = threadmap.get (threadId); if (get == null) {log.error ("O modelo de thread não pode ser encontrado:" + ThreadID, nova exceção ("o modelo de thread não pode ser encontrado:" + ThreadID)); } retornar get; } / ** * Registre um thread no thread pool * <br> * Agrupamento padrão UnknownThreadGroup * * @Param Nome Thread Nome * @return * / public public Long addThreadModel (nome da string) {return addThreadModel (desconhecidoMreadGroup, nome); } / ** * Registre um thread no pool thread * <br> * Agrupamento padrão UnknownThreadGroup * * @Param Name ThreadName * @param threadCount ThreadCount * @return * / static public LongthThreadModel (nome da string, threadCount) {return addThReadModel (UNKNOWNEADGRUPRUProup, Nome, ThreadCount); } / *** Registre um thread com o pool de thread** @param Grupo Informações sobre agrupamento de threads* @param nome Nome do thread* @return* / estático public Long AddThreadModel (grupo ThreadGroup, nome da string) {return addThreadModel (grupo, nome, 1); } / *** Registre um tópico com o pool de threads** @param Grupo Informações sobre agrupamento de threads* @param Nome threadName* @param threadCount ThreadCount* @return* / estático public Long AddThReadModel (grupo ThreadGroup, Nome da String, Int ThreadCount) {Return addThreadModel (Grupo, Nome, Null, ThreadCount); } / *** Registre um thread com o pool de threads** @param grupo threadGroup Information* @param name threadname* @param runnable* @param threadcount threadCount* @return* / static public LongThReadModel (grupo threadMMMOUPTM). retornar addThreadModel (ThreadModel); } / ** * Registre um thread com o pool de thread * * @param threadmodel * / estático public Long AddThreadModel (ThreadModel ThreadModel) {Threadmap.put (ThreadModel.getId, ThreadModel); retornar threadmodel.getId; } / ** * Adicionar tarefa * * @param threadId * @param tarefa * @return * / estático public boolean addtask (threadID longo, tarefa taskevent) {threadModel threadModel = getThreadModel (ThreadID); if (threadmodel! = null) {threadmodel.addtask (tarefa); retorna true; } retornar false; } / ** * Adicionar tarefa do timer * * @param threadId * @param tarefa * @return * / estático public boolean addtimerTask (threadId longo, tarefa de timeTaskevent) {ThreadModel threadModel = getThreadModel (ThreadID); if (threadmodel! = null) {threadmodel.addtimer (tarefa); retorna true; } retornar false; } / ** * Adicionar tarefa, adicione a tarefa ao thread atual * * @param tarefa * @return * / estático public boolean addCurrentThreadTask (tarefa taskevent) {thread currentThread = thread.currentThread; if (CurrentThread instância de threadModel.mythread) {long threadId = currentThread.getId; threadModel ThreadModel = getThreadModel (ThreadId); if (threadModel! = null) {threadModel.addTask (Task); retorna true; }} retornar false; } / ** * Adicione uma tarefa do timer e adicione uma tarefa ao thread atual * * @param tarefa * @return * / estático boolean addCurrentThreadTimerTask (Tarefa TimerTavent) {ThreadThThread = Thread.CurrentThread; if (CurrentThread Instância de ThreadModel.mythread) {Long ThreadId = currentThread.getId; ThreadModel ThreadModel = getThreadModel (ThreadId); if (ThreadModel! = NULL) {ThreadModel.addtimer (Task); retorna true; }} retornar false; }}Em seguida, vamos dar uma olhada no uso
Código de introdução do tópico no artigo anterior
public static void main (string [] args) lança interruptedException {// thread paralelism, vários threads executam várias tarefas/funções new thread (new run1) .start; novo thread (novo run2) .start; } // Task1 Classe estática Run1 implementa Runnable {@Override public void run {// Execute a tarefa 1 run1; // Execute a tarefa 3 run3; }} // Task2 Classe estática Run2 implementa Runnable {@Override public void run {// Execute a tarefa 3 run3; // Execute a tarefa 1 run1; // execute a tarefa 2 run2; }} // Tarefa 1 public static void run1 {System.out.println ("run1->" + System.currenttimemillis); } // Tarefa 2 public static void run2 {System.out.println ("run2->" + System.CurrentTimEmillis); } // tarefa 3 public static void run3 {System.out.println ("run3->" + System.currenttimemillis); }Eu mudei o código para o modo
public static void main (string [] args) lança interruptedException {// threads são paralelos, vários threads executam várias tarefas/função long test1 = threadpool.addthreadmodel ("Teste Thread-1"); long test2 = threadpool.addthreadmodel ("thread-2 de teste 2"); // Adicione a tarefa ThreadPool.addTask (Test1, new Run1); Threadpool.addtask (test2, novo run2); // Adicione a tarefa do timer threadpool.addtimerTask (Test1, novo Timerrun1); Threadpool.addtimerTask (Test2, novo Timerrun2); } // Task1 Classe estática Run1 estende o Taskevent {@Override public void run {// Execute a tarefa 1 RUN1; // Execute a tarefa 3 run3; }} // Task1 Classe estática Timerrun1 estende TimerTaskevent {public timerrun1 {super (500); // 500ms Unlimited Execution} @Override public void Run {// Execute Task 1 Run1; // Execute Task 3 Run3; }} // Task2 Classe estática Run2 estende o Taskevent {@Override public void run {// Execute a tarefa 3 run3; // Execute a tarefa 1 run1; // execute a tarefa 2 run2; }} // Task2 Classe estática Timerrun2 estende TimerTaskevent {public timerrun2 {super (500); // 500ms Unlimited Execution} @Override public void Run {// Execute Task 3 Run3; // Execute 1 Run1; // Execute Task 2 Run2; }} // tarefa1 public static void run1 {System.out.println ("run1->" + System.currenttimemillis); } // Task2 public static void run2 {System.out.println ("run2->" + System.currenttimemillis); } // tarefa 3 public static void run3 {System.out.println ("run3->" + System.currenttimemillis); }Em seguida, vamos dar uma olhada no efeito de execução
RUN1-> 1472120543013RUN3-> 1472120543013RUN3-> 1472120543017RUN1-> 1472120543017RUN2-> 14721205 43017RUN1-> 1472120543517RUN3-> 1472120543517RUN2-> 1472120543517RUN1-> 1472120544018RUN3-> 1477 2120544018RUN2-> 1472120544018RUN1-> 1472120544520RUN3-> 1472120544520RUN2-> 1472120544520Run1 -> 1472120545021RUN3-> 1472120545021RUN2-> 1472120545021RUN1-> 1472120545521RUN3-> 147212054521
Tudo é normal;
Este é o meu modelo de encadeamento personalizado;
Neste ponto, meu modelo de encadeamento personalizado foi concluído;
Então, quais são as vantagens e desvantagens?
A vantagem é que o controle de fluxo de dados é muito claro, incluindo a situação atual de execução, bem como os threads prenderam o monitoramento e a execução do temporizador de tarefas;
Desvantagens, esse modelo de encadeamento personalizado ainda não pode resolver o problema da segurança dos dados do encadeamento e da área crítica, e ainda precisa ser resolvido por bloqueios ou outros formulários no momento apropriado;
Espero que os grandes deuses apontem as deficiências, para que eu possa corrigi -las imediatamente.