热门标签 | HotTags
当前位置:  开发笔记 > 运维 > 正文

java自定义线程模型处理方法分享

本文给大家总结分享了下个人关于java处理自定义线程模型的一些经验和处理方法,有需要的小伙伴可以参考下

看过我之前文章的园友可能知道我是做游戏开发,我的很多思路和出发点是按照游戏思路来处理的,所以和web的话可能会有冲突,不相符合。

来说说为啥我要自定义线程模型呢?

按照我做的mmorpg或者mmoarpg游戏划分,线程被划分为,主线程,全局同步线程,聊天线程,组队线程,地图线程,以及地图消息分发派送线程等;

一些列,都需要根据我的划分,以及数据流向做控制。

游戏服务器,主要要做的事情,肯定是接受玩家的 命令请求 -> 相应的操作 -> 返回结果;
在服务器端所有的消息都会注册到消息管理器里,然后消息在注册的时候会指定线程模型,
如果消息需要提交到玩家所在地图线程进行处理的话注册消息的时候就要把线程模型用(地图消息分发派送线程);

下面我们先来分析线程模型;

在看线程模型代码之前我先看看我的任务模型

package net.sz.engine.thread;

import java.io.Serializable;
import org.apache.log4j.Logger;
import net.sz.engine.structs.ObjectAttribute;
import net.sz.engine.structs.ObjectGlobal;

/**
 * 任务模型
 *
 * 
* author 失足程序员
* mail 492794628@qq.com
* phone 13882122019
*/ public abstract class TaskEvent implements Serializable, Cloneable { private static final Logger log = Logger.getLogger(TaskEvent.class); private static final long serialVersiOnUID= 4196020659994845804L; //运行时数据 private transient final ObjectAttribute runOther = new ObjectAttribute; //任务创建的时间 protected long createTime; //任务的唯一id protected long taskId; //取消的任务 protected boolean cancel = false; public TaskEvent { this.runOther.put("submitTime", 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("submitTime"); } public ObjectAttribute getRunOther { return runOther; } public boolean isCancel { return cancel; } public void setCancel(boolean cancel) { this.cancel = cancel; } public abstract void run; @Override public Object clone throws CloneNotSupportedException { return super.clone; //To change body of generated methods, choose Tools | Templates. } }
package net.sz.engine.thread;

/**
 * 定时器执行器
 *
 * 
* author 失足程序员
* mail 492794628@qq.com
* phone 13882122019
*/ public abstract class TimerTaskEvent extends TaskEvent { private static final long serialVersiOnUID= -8331296295264699207L; /** * 开始执行的时间 */ protected long startTime; /** * 是否一开始执行一次 */ protected boolean startAction; /** * 结束时间 */ protected long endTime; /** * 执行次数 */ protected int actionCount; /** * 间隔执行时间 */ protected int intervalTime; /** * * @param startTime 指定开始时间 * @param isStartAction 是否一开始就执行一次 * @param endTime 指定结束时间 * @param actionCount 指定执行次数 * @param intervalTime 指定间隔时间 */ public TimerTaskEvent(long startTime, boolean isStartAction, long endTime, int actionCount, int intervalTime) { super; this.startTime = startTime; this.startAction = isStartAction; this.endTime = endTime; this.actiOnCount= actionCount; this.intervalTime = intervalTime; } /** * 指定任务的开始执行时间 * * @param startTime 指定开始时间 * @param isStartAction 是否一开始就执行一次 * @param actionCount 指定执行次数 * @param intervalTime 指定间隔时间 */ public TimerTaskEvent(long startTime, boolean isStartAction, int actionCount, int intervalTime) { this(startTime, isStartAction, 0, actionCount, intervalTime); } /** * 指定结束时间已结束时间为准,执行次数不一定够 * * @param isStartAction 是否一开始就执行一次 * @param endTime 指定结束时间 * @param actionCount 指定执行次数 * @param intervalTime 指定间隔时间 * */ public TimerTaskEvent(boolean isStartAction, long endTime, int actionCount, int intervalTime) { this(0, isStartAction, endTime, actionCount, intervalTime); } /** * 指定开始时间,和结束时间 * * @param startTime 指定开始时间 * @param endTime 指定结束时间 * @param intervalTime 指定间隔时间 */ public TimerTaskEvent(long startTime, long endTime, int intervalTime) { this(startTime, false, endTime, -1, intervalTime); } /** * 指定的执行次数和间隔时间 * * @param actionCount 指定执行次数 * @param intervalTime 指定间隔时间 */ public TimerTaskEvent(int actionCount, int intervalTime) { this(0, false, 0, actionCount, intervalTime); } /** * 提交后指定的时间无限制执行 * * @param intervalTime 指定间隔时间 */ public TimerTaskEvent(int intervalTime) { this(0, false, 0, -1, intervalTime); } 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 getEndTime { return endTime; } public void setEndTime(long endTime) { 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; } }

这里是任务模型和定时器任务模型;

package net.sz.engine.thread;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ConcurrentLinkedQueue;
import net.sz.engine.structs.ObjectGlobal;
import net.sz.engine.utils.MailUtil;
import net.sz.engine.utils.StringUtil;
import org.apache.log4j.Logger;
import org.jboss.jandex.Main;

/**
 * 线程模型
 * 
* author 失足程序员
* mail 492794628@qq.com
* phone 13882122019
*/ public class ThreadModel implements Runnable { private static final Logger log = Logger.getLogger(ThreadModel.class); private static long threadID = 0; protected static final Object SYN_OBJECT = new Object; protected long tid; protected String name; protected long lastSendMail = 0; protected final ArrayList threads = new ArrayList<>; /** * 任务列表 线程安全的任务列表 */ //protected final List taskQueue = new ArrayList<>; protected final ConcurrentLinkedQueue taskQueue = new ConcurrentLinkedQueue<>; /** * */ protected final List timerQueue = new ArrayList<>; // false标识删除线程 protected volatile boolean runing = true; public ThreadModel(ThreadGroup group) { this(group, "无名", 1); } public ThreadModel(String name) { this(ThreadPool.UnknownThreadGroup, name, 1); } public ThreadModel(ThreadGroup group, String name, int threadCount) { this(group, name, threadCount, null); } public ThreadModel(ThreadGroup group, String name, int threadCount, Runnable runnable) { synchronized (SYN_OBJECT) { threadID++; tid = threadID; } for (int i = 1; i <= threadCount; i++) { MyThread thread; if (runnable == null) { thread = new MyThread(tid, group, this, name + "-" + tid + "-" + i); } else { thread = new MyThread(tid, group, runnable, name + "-" + tid + "-" + i); } thread.start; threads.add(thread); } this.name = name; } /** * 线程名字 * * @return */ public String getName { return name; } /** * 获取线程的自定义id * * @return */ public long getId { return this.tid; } /** * 增加新的任务 每增加一个新任务,都要唤醒任务队列 * * @param runnable */ public void addTask(TaskEvent runnable) { taskQueue.add(runnable); synchronized (taskQueue) { /* 唤醒队列, 开始执行 */ taskQueue.notifyAll; } } /** * 向线程添加定时器任务 * * @param runnable */ public void addTimer(TimerTaskEvent runnable) { synchronized (timerQueue) { if (runing) { //一开始执行一次 if (runnable.startAction) { addTask(runnable); } timerQueue.add(runnable); } else { log.error("线程已经停止"); } } } // /** * 定时器线程执行器 */ public void timerRun { ArrayList taskModels; synchronized (timerQueue) { // 队列不为空的情况下 取出队列定时器任务 taskModels = new ArrayList<>(timerQueue); } if (!taskModels.isEmpty) { for (TimerTaskEvent timerEvent : taskModels) { int execCount = timerEvent.getRunOther.getintValue("Execcount"); long lastTime = timerEvent.getRunOther.getlongValue("LastExecTime"); long nowTime = System.currentTimeMillis; if (lastTime == 0) { timerEvent.getRunOther.put("LastExecTime", nowTime); } else if (timerEvent.isCancel) { //如果任务已经取消 synchronized (timerQueue) { timerQueue.remove(timerEvent); } log.debug("清理定时器任务:" + timerEvent.getClass.getName); } else if (nowTime > timerEvent.getStartTime // 是否满足开始时间 && (nowTime - timerEvent.getSubmitTime > timerEvent .getIntervalTime)// 提交以后是否满足了间隔时间 && (timerEvent.getEndTime <= 0 || nowTime = timerEvent .getIntervalTime)) // 判断上次执行到目前是否满足间隔时间 { // 提交执行定时器最先执行 this.addTask(timerEvent); // 记录 execCount++; timerEvent.getRunOther.put("Execcount", execCount); timerEvent.getRunOther.put("LastExecTime", nowTime); nowTime = System.currentTimeMillis; // 判断删除条件 if ((timerEvent.getEndTime > 0 && nowTime 0 && timerEvent.getActionCount <= execCount)) { synchronized (timerQueue) { timerQueue.remove(timerEvent); } log.debug("清理定时器任务:" + timerEvent.getClass.getName); } } } } } // // /** * * 查看线程堆栈 */ public void showStackTrace { StringBuilder buf = new StringBuilder; for (MyThread currentThread : threads) { long procc = System.currentTimeMillis - currentThread.getLastExecuteTime; if (procc > 5 * 1000 && procc <864000000L) {//小于10天//因为多线程操作时间可能不准确 buf.append("线程[") .append(currentThread.getName) .append("]可能已卡死 -> ") .append(procc / 1000f) .append("s\n ") .append("执行任务:") .append(currentThread.getLastCommand.getClass.getName); try { StackTraceElement elements = currentThread.getStackTrace; for (int i = 0; i 5 * 60 * 1000) { lastSendMail = System.currentTimeMillis; MailUtil.sendMail("线程执行已卡死 -> 游戏id-" + ObjectGlobal.GameID + " 平台-" + ObjectGlobal.Platform + " 服务器id-" + ObjectGlobal.ServerID, toString); } } } // @Override public void run { MyThread currentThread = (MyThread) Thread.currentThread; while (runing) { while (taskQueue.isEmpty && runing) { try { /* 任务队列为空,则等待有新任务加入从而被唤醒 */ synchronized (taskQueue) { taskQueue.wait(500); } } catch (InterruptedException ie) { log.error(ie); } } /* 取出任务执行 */ if (runing) { currentThread.lastCommand = null; currentThread.lastCommand = taskQueue.poll; } if (currentThread.lastCommand != null) { if (currentThread.lastCommand.isCancel) { //如果任务已经取消 continue; } /* 执行任务 */ // r.setSubmitTimeL; currentThread.lastExecuteTime = System.currentTimeMillis; try { currentThread.lastCommand.run; } catch (Exception e) { log.error("工人<“" + currentThread.getName + "”> 执行任务<" + currentThread.lastCommand.getClass.getName + "> 遇到错误: ", e); } long timeL1 = System.currentTimeMillis - currentThread.lastExecuteTime; if (timeL1 <= 20) { } else if (timeL1 <= 100L) { log.info("工人<“" + currentThread.getName + "”> 完成了任务:" + currentThread.lastCommand.toString + " 执行耗时:" + timeL1); } else if (timeL1 <= 200L) { log.info("工人<“" + currentThread.getName + "”> 长时间执行 完成任务:" + currentThread.lastCommand.toString + " “考虑”任务脚本逻辑 耗时:" + timeL1); } else { log.info("工人<“" + currentThread.getName + "”> 超长时间执行完成 任务:" + currentThread.lastCommand.toString + " “考虑是否应该删除”任务脚本 耗时:" + timeL1); } currentThread.lastExecuteTime = 0; } } log.error("线程结束, 工人<“" + Thread.currentThread.getName + "”>退出"); } /** * 自定义线程 */ public class MyThread extends Thread { /** * * @param tid 自定义线程id * @param group 分组 * @param run 执行方法 * @param name 线程名称 */ public MyThread(long tid, ThreadGroup group, Runnable run, String name) { super(group, run, name); this._id = tid; } //线程的自定义id public long _id; //正在执行的任务 public volatile TaskEvent lastCommand; //开始执行任务的时间 public volatile long lastExecuteTime = 0; public TaskEvent getLastCommand { return lastCommand; } public long getLastExecuteTime { return lastExecuteTime; } /** * 返回线程自定义id * * @return */ @Override public long getId { return _id; } } /** * 停止线程,设置线程的停止状态,并不会马上终止线程 */ public void stop { this.runing = false; } public boolean isRuning { return runing; } @Override public String toString { return "Thread{" + "tid=" + tid + ",Name=" + this.getName + '}'; } }

我从 ThreadModel 构造函数的

 public ThreadModel(ThreadGroup group, String name, int threadCount, Runnable runnable) {
  synchronized (SYN_OBJECT) {
threadID++;
tid = threadID;
  }
  for (int i = 1; i <= threadCount; i++) {
 MyThread thread;
if (runnable == null) {
thread = new MyThread(tid, group, this, name + "-" + tid + "-" + i);
} else {
thread = new MyThread(tid, group, runnable, name + "-" + tid + "-" + i);
 }
 thread.start;
 threads.add(thread);
  }
  this.name = name;
 }

可以看出,这里我运行声明一个或者多个 MyThread 线程类

为什么要这样考虑呢打个比方,如果是处理日志的写入数据这种,没有共享数据,没有线程临界区的处理流程,我可以考虑使用N个线程去处理这样的工作;不会产生脏数据;

如果是想组队请求,技能施法这种处理,我需要单队列处理,那么threadmodel里面肯定只有一个MyThread 这样不算阻塞模式串行执行(或队列执行)把共享数据和线程临界区的问题也解决了不再依赖锁;

字很丑,请见谅

上面图片看出,在每一个threadmodel 里面都会两个队列,一个timertaskevent,一个是taskevent,会存在一个全局的timer thread;

全局的 timer thread 的作用是用来定时去处理和发现 threadmodel里面timertaskevent需要执行了,就把他加入到taskevent队里里面;最终执行是taskevent队列

timertaskevent为什么要存储在对应的threadmodel里面呢,那是因为比如,我A线程(threadmodel实例)运行一段时间后需要关闭,释放资源了,那么我还要去其他地方查找对应的timertask并移除掉;

package net.sz.engine.thread;

import java.util.HashMap;
import java.util.Map;

/**
 *
 * 
* author 失足程序员
* mail 492794628@qq.com
* phone 13882122019
*/ class TimerThread extends Thread { private static final Object SYN_OBJECT = new Object; 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 hashMap = new HashMap<>(ThreadPool.getThreadMap); for (Map.Entry entrySet : hashMap.entrySet) { Long key = entrySet.getKey; ThreadModel value = entrySet.getValue; value.timerRun; } } } }

线程模型的管理器

package net.sz.engine.thread;

import java.util.HashMap;
import java.util.concurrent.ConcurrentHashMap;
import net.sz.engine.script.manager.ScriptManager;
import net.sz.engine.timer.GlobTimerEvent;
import net.sz.engine.timer.PrintlnServerMemoryTimerEvent;
import org.apache.log4j.Logger;

/**
 * 线程管理器
 *
 * 
* author 失足程序员
* mail 492794628@qq.com
* phone 13882122019
*/ public class ThreadPool { static private final Logger log = Logger.getLogger(ThreadPool.class); static public final long GloblThread; static private final TimerThread GloblTimerThread; static final long CheckThreadTimerThreadModel; static public final ThreadGroup GloblThreadGroup = new ThreadGroup("Global ThreadGroup"); static public final ThreadGroup UnknownThreadGroup = new ThreadGroup(GloblThreadGroup, "Unknown ThreadGroup"); static private final ConcurrentHashMap threadMap = new ConcurrentHashMap<>; public static void main(String[] args) { ThreadPool.addTimerTask(GloblThread, new TimerTaskEvent(1000) { @Override public void run { log.error("ssssss"); } }); } static { //创建全局线程 GloblThread = addThreadModel(GloblThreadGroup, "GloblThread"); //执行指定任务定时触发脚步 addTimerTask(GloblThread, new GlobTimerEvent(ScriptManager.getInstance.getBaseScriptEntry)); //查询服务器消耗定时模型 addTimerTask(GloblThread, new PrintlnServerMemoryTimerEvent); //创建定时器线程 GloblTimerThread = new TimerThread; GloblTimerThread.start; //检查线程卡死情况 CheckThreadTimerThreadModel = addThreadModel(GloblThreadGroup, "Check Thread Timer Event"); addTimerTask(CheckThreadTimerThreadModel, new CheckThreadTimerEvent); } /** * 删除指定id线程模型的时候回设置状态为停止状态 * * @param tid * @return */ static public ThreadModel remove(long tid) { ThreadModel remove = threadMap.remove(tid); if (remove != null) { remove.stop; } return remove; } /** * 获取线程池中所有线程 * * @return */ static public ConcurrentHashMap getThreadMap { return threadMap; } /** * 获取线程池的一个线程 * * @param threadId * @return */ static public ThreadModel getThreadModel(long threadId) { ThreadModel get = threadMap.get(threadId); if (get == null) { log.error("无法找到线程模型:" + threadId, new Exception("无法找到线程模型:" + threadId)); } return get; } /** * 向线程池注册一个线程 *
* 默认分组 UnknownThreadGroup * * @param name 线程名称 * @return */ static public long addThreadModel(String name) { return addThreadModel(UnknownThreadGroup, name); } /** * 向线程池注册一个线程 *
* 默认分组 UnknownThreadGroup * * @param name 线程名称 * @param threadcount 线程量 * @return */ static public long addThreadModel(String name, int threadcount) { return addThreadModel(UnknownThreadGroup, name, threadcount); } /** * 向线程池注册一个线程 * * @param group 线程分组信息 * @param name 线程名称 * @return */ static public long addThreadModel(ThreadGroup group, String name) { return addThreadModel(group, name, 1); } /** * 向线程池注册一个线程 * * @param group 线程分组信息 * @param name 线程名称 * @param threadcount 线程量 * @return */ static public long addThreadModel(ThreadGroup group, String name, int threadcount) { return addThreadModel(group, name, null, threadcount); } /** * 向线程池注册一个线程 * * @param group 线程分组信息 * @param name 线程名称 * @param runnable * @param threadcount 线程量 * @return */ static public long addThreadModel(ThreadGroup group, String name, Runnable runnable, int threadcount) { ThreadModel threadModel = new ThreadModel(group, name, threadcount, runnable); return addThreadModel(threadModel); } /** * 向线程池注册一个线程 * * @param threadModel */ static public long addThreadModel(ThreadModel threadModel) { threadMap.put(threadModel.getId, threadModel); return threadModel.getId; } /** * 添加任务 * * @param threadId * @param task * @return */ static public boolean addTask(long threadId, TaskEvent task) { ThreadModel threadModel = getThreadModel(threadId); if (threadModel != null) { threadModel.addTask(task); return true; } return false; } /** * 添加定时器任务 * * @param threadId * @param task * @return */ static public boolean addTimerTask(long threadId, TimerTaskEvent task) { ThreadModel threadModel = getThreadModel(threadId); if (threadModel != null) { threadModel.addTimer(task); return true; } return false; } /** * 添加任务,添加任务到当前线程 * * @param task * @return */ static public boolean addCurrentThreadTask(TaskEvent task) { Thread currentThread = Thread.currentThread; if (currentThread instanceof ThreadModel.MyThread) { long threadId = currentThread.getId; ThreadModel threadModel = getThreadModel(threadId); if (threadModel != null) { threadModel.addTask(task); return true; } } return false; } /** * 添加定时器任务,添加任务到当前线程 * * @param task * @return */ static public boolean addCurrentThreadTimerTask(TimerTaskEvent task) { Thread currentThread = Thread.currentThread; if (currentThread instanceof ThreadModel.MyThread) { long threadId = currentThread.getId; ThreadModel threadModel = getThreadModel(threadId); if (threadModel != null) { threadModel.addTimer(task); return true; } } return false; } }

接下来我们看看使用情况

上篇文章中线程介绍代码

public static void main(String[] args) throws InterruptedException {
  //线程并行情况,有多个线程执行多个任务/函数
  new Thread(new Run1).start;
  new Thread(new Run2).start;
 }

 //任务1
 static class Run1 implements Runnable {

  @Override
  public void run {
//执行任务1
 run1;
//执行任务3
 run3;
  }
 }
 //任务2

 static class Run2 implements Runnable {

  @Override
  public void run {
//执行任务3
 run3;
//执行任务1
 run1;
//执行任务2
 run2;
  }
 }

 //任务1
 public static void run1 {
  System.out.println("run1->" + System.currentTimeMillis);
 }

 //任务2
 public static void run2 {
  System.out.println("run2->" + System.currentTimeMillis);
 }

 //任务3
 public static void run3 {
  System.out.println("run3->" + System.currentTimeMillis);
 }

我把代码切换模式

 public static void main(String[] args) throws InterruptedException {
  //线程并行情况,有多个线程执行多个任务/函数
  long test1 = ThreadPool.addThreadModel("测试线程-1");
  long test2 = ThreadPool.addThreadModel("测试线程-2");
  //添加任务
  ThreadPool.addTask(test1, new Run1);
  ThreadPool.addTask(test2, new Run2);
  //添加定时器任务
  ThreadPool.addTimerTask(test1, new TimerRun1);
  ThreadPool.addTimerTask(test2, new TimerRun2);
 }
 //任务1
 static class Run1 extends TaskEvent {
  @Override
  public void run {
//执行任务1
 run1;
//执行任务3
 run3;
  }
 }
 //任务1
 static class TimerRun1 extends TimerTaskEvent {
  public TimerRun1 {
super(500);//500毫秒无限制执行
  }
  @Override
  public void run {
//执行任务1
 run1;
//执行任务3
 run3;
  }
 }
 //任务2
 static class Run2 extends TaskEvent {
  @Override
  public void run {
//执行任务3
 run3;
//执行任务1
 run1;
//执行任务2
 run2;
  }
 }
 //任务2
 static class TimerRun2 extends TimerTaskEvent {
  public TimerRun2 {
super(500);//500毫秒无限制执行
  }
  @Override
  public void run {
//执行任务3
 run3;
//执行任务1
 run1;
//执行任务2
 run2;
  }
 }
 //任务1
 public static void run1 {
  System.out.println("run1->" + System.currentTimeMillis);
 }
 //任务2
 public static void run2 {
  System.out.println("run2->" + System.currentTimeMillis);
 }
 //任务3
 public static void run3 {
  System.out.println("run3->" + System.currentTimeMillis);
 }

接下来我们看看执行效果

run1->1472120543013
run3->1472120543013
run3->1472120543017
run1->1472120543017
run2->1472120543017
run1->1472120543517
run3->1472120543517
run2->1472120543517
run1->1472120544018
run3->1472120544018
run2->1472120544018
run1->1472120544520
run3->1472120544520
run2->1472120544520
run1->1472120545021
run3->1472120545021
run2->1472120545021
run1->1472120545521
run3->1472120545521

一切正常;

这就是我的自定义线程模型;

到这里我的自定义线程模型就算介绍完成了;

那么优缺点在哪里呢?

优点是,数据流程控制很清晰,包括现在执行情况,以及线程卡死监控和任务 的定时器执行;

缺点,这个自定义线程模型依然不可能解决线程数据安全和临界区问题,在适当的时候依然需要靠锁或者其他形式来解决;

不足之处希望大神们指出,我好即时纠正。


推荐阅读
  • 如何提高PHP编程技能及推荐高级教程
    本文介绍了如何提高PHP编程技能的方法,推荐了一些高级教程。学习任何一种编程语言都需要长期的坚持和不懈的努力,本文提醒读者要有足够的耐心和时间投入。通过实践操作学习,可以更好地理解和掌握PHP语言的特异性,特别是单引号和双引号的用法。同时,本文也指出了只走马观花看整体而不深入学习的学习方式无法真正掌握这门语言,建议读者要从整体来考虑局部,培养大局观。最后,本文提醒读者完成一个像模像样的网站需要付出更多的努力和实践。 ... [详细]
  • 这是原文链接:sendingformdata许多情况下,我们使用表单发送数据到服务器。服务器处理数据并返回响应给用户。这看起来很简单,但是 ... [详细]
  • 本文详细介绍了云服务器API接口的概念和作用,以及如何使用API接口管理云上资源和开发应用程序。通过创建实例API、调整实例配置API、关闭实例API和退还实例API等功能,可以实现云服务器的创建、配置修改和销毁等操作。对于想要学习云服务器API接口的人来说,本文提供了详细的入门指南和使用方法。如果想进一步了解相关知识或阅读更多相关文章,请关注编程笔记行业资讯频道。 ... [详细]
  • svnWebUI:一款现代化的svn服务端管理软件
    svnWebUI是一款图形化管理服务端Subversion的配置工具,适用于非程序员使用。它解决了svn用户和权限配置繁琐且不便的问题,提供了现代化的web界面,让svn服务端管理变得轻松。演示地址:http://svn.nginxwebui.cn:6060。 ... [详细]
  • 本文介绍了Python高级网络编程及TCP/IP协议簇的OSI七层模型。首先简单介绍了七层模型的各层及其封装解封装过程。然后讨论了程序开发中涉及到的网络通信内容,主要包括TCP协议、UDP协议和IPV4协议。最后还介绍了socket编程、聊天socket实现、远程执行命令、上传文件、socketserver及其源码分析等相关内容。 ... [详细]
  • Android中高级面试必知必会,积累总结
    本文介绍了Android中高级面试的必知必会内容,并总结了相关经验。文章指出,如今的Android市场对开发人员的要求更高,需要更专业的人才。同时,文章还给出了针对Android岗位的职责和要求,并提供了简历突出的建议。 ... [详细]
  • Centos7.6安装Gitlab教程及注意事项
    本文介绍了在Centos7.6系统下安装Gitlab的详细教程,并提供了一些注意事项。教程包括查看系统版本、安装必要的软件包、配置防火墙等步骤。同时,还强调了使用阿里云服务器时的特殊配置需求,以及建议至少4GB的可用RAM来运行GitLab。 ... [详细]
  • 图解redis的持久化存储机制RDB和AOF的原理和优缺点
    本文通过图解的方式介绍了redis的持久化存储机制RDB和AOF的原理和优缺点。RDB是将redis内存中的数据保存为快照文件,恢复速度较快但不支持拉链式快照。AOF是将操作日志保存到磁盘,实时存储数据但恢复速度较慢。文章详细分析了两种机制的优缺点,帮助读者更好地理解redis的持久化存储策略。 ... [详细]
  • 本文介绍了2019年上半年内蒙古计算机软考考试的报名通知和考试时间。考试报名时间为3月1日至3月23日,考试时间为2019年5月25日。考试分为高级、中级和初级三个级别,涵盖了多个专业资格。报名采取网上报名和网上缴费的方式进行,报考人员可登录内蒙古人事考试信息网进行报名。详细内容请点击查看。 ... [详细]
  • 本文介绍了在Linux下安装和配置Kafka的方法,包括安装JDK、下载和解压Kafka、配置Kafka的参数,以及配置Kafka的日志目录、服务器IP和日志存放路径等。同时还提供了单机配置部署的方法和zookeeper地址和端口的配置。通过实操成功的案例,帮助读者快速完成Kafka的安装和配置。 ... [详细]
  • mac php错误日志配置方法及错误级别修改
    本文介绍了在mac环境下配置php错误日志的方法,包括修改php.ini文件和httpd.conf文件的操作步骤。同时还介绍了如何修改错误级别,以及相应的错误级别参考链接。 ... [详细]
  • 一句话解决高并发的核心原则
    本文介绍了解决高并发的核心原则,即将用户访问请求尽量往前推,避免访问CDN、静态服务器、动态服务器、数据库和存储,从而实现高性能、高并发、高可扩展的网站架构。同时提到了Google的成功案例,以及适用于千万级别PV站和亿级PV网站的架构层次。 ... [详细]
  • 从高级程序员到CTO的4次能力跃迁!如何选择适合的技术负责人?
    本文讲解了从高级程序员到CTO的4次能力跃迁,以及如何选择适合的技术负责人。在初创期、发展期、成熟期的每个阶段,创业公司需要不同级别的技术负责人来实现复杂功能、解决技术难题、提高交付效率和质量。高级程序员的职责是实现复杂功能、编写核心代码、处理线上bug、解决技术难题。而技术经理则需要提高交付效率和质量。 ... [详细]
  • GPT-3发布,动动手指就能自动生成代码的神器来了!
    近日,OpenAI发布了最新的NLP模型GPT-3,该模型在GitHub趋势榜上名列前茅。GPT-3使用的数据集容量达到45TB,参数个数高达1750亿,训练好的模型需要700G的硬盘空间来存储。一位开发者根据GPT-3模型上线了一个名为debuid的网站,用户只需用英语描述需求,前端代码就能自动生成。这个神奇的功能让许多程序员感到惊讶。去年,OpenAI在与世界冠军OG战队的表演赛中展示了他们的强化学习模型,在限定条件下以2:0完胜人类冠军。 ... [详细]
  • SpringBoot整合SpringSecurity+JWT实现单点登录
    SpringBoot整合SpringSecurity+JWT实现单点登录,Go语言社区,Golang程序员人脉社 ... [详细]
author-avatar
正在减肥的小小_519
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有