题目:
Linux环境下的Java主线程中自定义启动两个子线程,两个子线程分别负责执行各自的定时任务,第一个线程负责的任务定时像mysql数据中写任务,写数据的task是由shell启动java写的task,第二个线程负责的任务由python写的,由java线程负责调用(这次我的python任务只是向特定目录下追加写一个文件)。
现将解题思路,技术细节分享如下:
首先是宏观的工作空间结构图
driver目录下存放的是主线程的Driver类
starttask1.sh与starttask2.sh分别是两个任务的启动脚本
tasks目录下存放两个task的源文件以及mysql的连接驱动mysql-connector.jar(已重命名)
tmp是用来存放python写入的随机数文件
先解决向数据库中写文件的问题(WriteMysqlTask.java负责)
先上WriteMysqlTask.java的源码:
import java.sql.*;
public class WriteMysqlTask {
public static void main(String[] args){
try{
Class.forName("com.mysql.jdbc.Driver");
System.out.println("加载驱动成功");
}catch(ClassNotFoundException e1){
System.out.println("加载驱动失败");
e1.printStackTrace();
}
String url="jdbc:mysql://localhost:3306/data";
Connection conn;
try {
conn = DriverManager.getConnection(url, "root", "123456");
Statement stmt = conn.createStatement();
System.out.println("将向数据库中插入[name=xiaoxing,age=18]的数据...");
String insert_data = "insert into peoinfo values('xiaoming',18) ";
stmt.execute(insert_data);
System.out.println("已插入数据成功,请在data数据库peoinfo表中查验");
stmt.close();
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
接下来,是很重要的一步解决该文件的编译运行问题。
Class.forName()采用的是运行时动态加载,里面的内容编译时不进行检查,因此,编译大多不会出现问题,就直接用
javac WriteMysqlTask.java进行编译
效果图如下:
运行的时候需要注意一下:
java -classpath ‘./:mysql-connector.jar’ WriteMysqlTask
尤其是-classpath参数的填写不要填错,填错将直接导致Class.forName(“com.mysql.jdbc.Driver”);执行出错
以下是效果图:
紧接着,介绍一下该task的启动sh,即:startwriteMysql.sh
备注:编译执行完后rm -f *.class删除所有任务字节码文件
以下,是该shell脚本的执行效果图:
python相关的task任务源程序如下所示(writeRandom.py):
以追加的方式向tmp目录下的pwrite.txt写如11位随机数
两个启动task的sh分别为starttask1.sh和starttask2.sh:
starttask1.sh的内容:
starttask2.sh的内容:
这里,我觉得有必要解释一下cd ../tasks
我们先来看一下目录:
cd ../tasks命令的意思是:../返回上级目录,再进入tasks目录。因为Driver.java驱动是在driver目录下执行的,所以当用java Driver启动starttask1.sh与starttask2.sh时,尽管 starttask*.sh文件本身不在driver目录中,但当前执行目录确实是在driver目录中。因此,需返回上级目录后方可进入tasks目录执行相应的任务。
接下来,重点介绍一下Driver.java
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.util.Timer;
import java.util.TimerTask;
public class Driver {
static void execshell(int taskid) {
try {
String shpath = "../starttask" + taskid + ".sh";
Process ps;
System.out.println("shpath:"+shpath);
ps = Runtime.getRuntime().exec(shpath);
ps.waitFor();
BufferedReader br = new BufferedReader(new InputStreamReader(ps.getInputStream()));
StringBuffer sb = new StringBuffer();
String line;
while ((line = br.readLine()) != null) {
sb.append(line).append("\n");
}
String result = sb.toString();
System.out.println(result);
} catch (Exception e) {
e.printStackTrace();
}
}
static class WritemysqlTask extends TimerTask {
static int count=1;
@Override
public void run() {
count++;
double oldtime=System.currentTimeMillis();
System.out.println("WritemysqlTask任务第"+count+"次启动时间为:"
+ oldtime);
execshell(1);
double pretime=System.currentTimeMillis();
System.out.println("WritemysqlTask任务:结束时间为:"+pretime);
double all=pretime-oldtime;
System.out.println("WritemysqlTask任务:此次任务共计耗时:"+all);
}
}
static class ExecutepyTask extends TimerTask {
static int count=1;
@Override
public void run() {
count++;
double oldtime=System.currentTimeMillis();
System.out.println("ExecutepyTask任务第"+count+"次启动时间为:"
+ oldtime);
execshell(2);
double pretime=System.currentTimeMillis();
System.out.println("ExecutepyTask任务:结束时间为:"+pretime);
double all=pretime-oldtime;
System.out.println("ExecutepyTask任务:此次任务共计耗时:"+all);
}
}
static class RunnableThread1 implements Runnable {
@Override
public void run() {
Timer timer = new Timer();
timer.schedule(new WritemysqlTask(), 1000, 1000);
}
}
static class RunnableThread2 implements Runnable {
@Override
public void run() {
Timer timer = new Timer();
timer.schedule(new ExecutepyTask(), 1000, 1000);
}
}
public static void main(String[] args) {
System.out.println("main即将启动两个线程");
double old = System.currentTimeMillis();
new Thread(new RunnableThread1()).start();
new Thread(new RunnableThread2()).start();
double pre = System.currentTimeMillis();
String consume = pre - old + "";
System.out.println("main已启动两个线程,耗时:" + consume);
}
}
说明:因为是已命令行java XX启动执行java程序的,为了方便编译运行,特将类统统写成静态内部类方便行事。
String shpath = “../starttask” + taskid + “.sh”;
根据传递进来的taskid值分别拼装出../starttask1.sh和../starttask2.sh
即返回上一级目录执行sh脚本,本级目录是在driver中。
Runtime.getRuntime().exec(shpath);
通过Runtime的静态方法getRuntime()的到运行时,之后.exec(XX)执行相应的shell命令。
Timer timer = new Timer();
timer.schedule(new XXX(), 1000, 1000);
调度器从Task启动的的1秒后每隔1秒调度一次任务。
我们接下来看一下startDriver.sh
最后,跑一个全部流程:
清空peoinfo表中的所有数据
./startDriver.sh启动流程:
执行13次调度以后按Ctrl+c停止执行:
查看数据库中的数据:
查看tmp目录下文件的数据:
至此,全部跑通。