赞
踩
使用Java进行多线程编程的人,多少可能都知道执行main()方法的是一个名为main的线程
通过new Thread新建线程,若不指定线程名,默认线程名为Thread-xx
。其中xx
是从0开始的编号
public static void main(String[] args) {
for (int i = 0; i < 10; i++) {
new Thread(() -> System.out.println("I'm " + Thread.currentThread().getName())).start();
}
}
为何默认线程名为Thread-xx
,通过Thread类的对应构造函数就可以知道
public Thread(Runnable target) {
init(null, target, "Thread-" + nextThreadNum(), 0);
}
其实,早在很久之前,自己就有过一个疑问:一个Java程序除了我们所知的main线程、自定义线程,是否还包含其他线程?
据我目前掌握的知识,通过jstack命令可以实现
在学习并发编程时,发现有一个非常好用的ThreadMXBean
接口,就可以帮助我们了解Java程序究竟包含哪些线程
public static void main(String[] args) {
ThreadMXBean threadMXBean = ManagementFactory.getThreadMXBean();
ThreadInfo[] threadInfos = threadMXBean.dumpAllThreads(false, false);
for (ThreadInfo threadInfo : threadInfos) {
System.out.println("[" + threadInfo.getThreadId() + "]" + " " + threadInfo.getThreadName());
}
}
获取的线程信息如下,线程10和5,根据自己的经验,应该是idea在运行Java程序时自己添加的线程,以实现监听、响应程序中断
ThreadMXBean是接口,通过ManagementFactory获取到的肯定是ThreadMXBean接口的实现类的一个实例对象
这里使用了Java的多态,将实现类的对象赋值给接口引用
ThreadImpl
就是ThreadMXBean接口一个实现类
public static ThreadMXBean getThreadMXBean() {
return ManagementFactoryHelper.getThreadMXBean();
}
public static synchronized ThreadMXBean getThreadMXBean() {
if (threadMBean == null) {
threadMBean = new ThreadImpl(jvm);
}
return threadMBean;
}
其他使用场景
findDeadlockedThreads()
、findMonitorDeadlockedThreads()
( Java ThreadMXBean & 死锁检测 )getThreadCount()
、当前活动的守护线程数getDaemonThreadCount()
、活动线程数峰值getPeakThreadCount()
getThreadCpuTime(long id)
,获取用户态cpu时间:getThreadUserTime(long id)
通过ThreadMXBean获取Java程序中的线程信息时,资料中有这样的描述
下面使用JMX来查看一个普通的Java程序包含哪些线程
看完示例代码后,感觉这就是JMX?好像和自己了解的不太一样?
目前,很多大数据组件都可以通过JMX获取节点或者集群的运行情况,这些不同维度的数据一般被称作metric
以Presto为例
因此,自己对jmx的认知就是:一个可以动态获取程序运行状态的工具,对监控程序的运行非常有用
JMX的定义
JMX架构图
一些说明
创建管理学生的MBean接口:
public interface StudentMBean {
String getName();
void setName(String name);
int getAge();
void printStudentInfo();
String studentInfo();
}
实现MBean接口
class Student implements StudentMBean { private String name; private int age; public Student() { } public Student(String name, int age) { this.name = name; this.age = age; } @Override public String getName() { return name; } @Override public void setName(String name) { this.name = name; } @Override public int getAge() { return age; } @Override public void printStudentInfo() { System.out.println("Student: " + name + ", age: " + age); } @Override public String studentInfo() { return "Student: " + name + ", age: " + age; } }
向MBeanServer注册MBean
public class JConsoleAgent {
public static void main(String[] args) throws MalformedObjectNameException, NotCompliantMBeanException,
InstanceAlreadyExistsException, MBeanRegistrationException, InterruptedException {
// 创建MBeanServer
MBeanServer server = ManagementFactory.getPlatformMBeanServer();
// 创建ObjectName以唯一标识MBean,其中studentMBean为域名,boy为MBean的名称,可以自由定义
ObjectName student = new ObjectName("studentMBean:name=boy");
// 将student注册到MBeanServer中,注册时实现了ObjectName与MBean的绑定
server.registerMBean(new Student("jack", 24), student);
// 程序休眠一段时间,方便观察通过JConsole体验JMX
TimeUnit.MINUTES.sleep(30);
}
}
在命令行中输入jconsole
以启动JConsole工具,选择对应的代理创建连接
最终,studentBean的信息如下:
name属性为可读可写,可以直接修改name(通过刷新按钮,实现值的修改)
printStudentInfo()是一个无参、void方法,直接点击方法名即可运行该方法
printStudentInfo方法有输出,最终会在JConsoleAgent的运行界面打印输出信息
添加HtmlAdaptorServer的maven依赖
<!-- https://mvnrepository.com/artifact/com.sun.jdmk/jmxtools -->
<dependency>
<groupId>com.sun.jdmk</groupId>
<artifactId>jmxtools</artifactId>
<version>1.2.1</version>
</dependency>
保持MBean不变,创建HttpAdapterAgent,通过对JConsoleAgent进行一些改造,就可以通过网页与JMX进行交互
public class HttpAdapterAgent { public static void main(String[] args) throws MalformedObjectNameException, NotCompliantMBeanException, InstanceAlreadyExistsException, MBeanRegistrationException, InterruptedException { // 创建MBeanServer MBeanServer server = ManagementFactory.getPlatformMBeanServer(); // 创建ObjectName以唯一标识MBean,其中studentMBean为域名 // boy为MBean的名称,可以自由定义 ObjectName student = new ObjectName("studentMBean:name=boy"); // 将student注册到MBeanServer中,注册时实现了ObjectName与MBean的绑定 server.registerMBean(new Student("jack", 24), student); // 创建并注册HtmlAdaptorServer,从而可以通过网页管理MBean HtmlAdaptorServer adaptorServer = new HtmlAdaptorServer(); ObjectName adapter = new ObjectName("httpAdapter:name=web"); server.registerMBean(adaptorServer, adapter); // 启动HtmlAdaptorServer adaptorServer.start(); } }
访问本地的8082端口:http://localhost:8082/,将存在如下网页
选择进入studentMBean域、name为boy的MBean,信息展示更加清晰】
修改Agent使其支持远程访问
public class RemoteAgent { public static void main(String[] args) throws MalformedObjectNameException, NotCompliantMBeanException, InstanceAlreadyExistsException, MBeanRegistrationException, InterruptedException, IOException { // 创建MBeanServer MBeanServer server = ManagementFactory.getPlatformMBeanServer(); ObjectName student = new ObjectName("studentMBean:name=girl"); server.registerMBean(new Student("lucy", 24), student); // 为MBeanServer注册端口号和url LocateRegistry.createRegistry(8888); // 若需要支持JConsole链接,url的结尾必须为jmxrmi JMXServiceURL url = new JMXServiceURL("service:jmx:rmi:///jndi/rmi://localhost:8888/jmxrmi"); // 创建支持远程连接的服务器 JMXConnectorServer connectorServer = JMXConnectorServerFactory.newJMXConnectorServer(url, null, server); // 启动服务 connectorServer.start(); } }
此时,尚未定义客户端,可以先通过JConsole进行访问
studentMBean域、name为girl的MBean信息如下
public class Client { public static void main(String[] args) throws IOException, MalformedObjectNameException, AttributeNotFoundException, MBeanException, ReflectionException, InstanceNotFoundException, InvalidAttributeValueException { // 创建与server的连接 JMXServiceURL url = new JMXServiceURL("service:jmx:rmi:///jndi/rmi://localhost:8888/jmxrmi"); JMXConnector jmxConnector = JMXConnectorFactory.connect(url, null); MBeanServerConnection serverConnection = jmxConnector.getMBeanServerConnection(); // 定义想要访问的MBean,与MBeanServer中注册的一致 ObjectName student = new ObjectName("studentMBean:name=girl"); // 获取MBeanServer所有domain String[] domains = serverConnection.getDomains(); System.out.println("MBeanServer存在如下域名:"); for (String domain: domains) { System.out.println(domain); } System.out.println("================================="); // 直接修改或访问属性值,属性必须大写 serverConnection.setAttribute(student, new Attribute("Name", "grace")); System.out.println("Student: " + serverConnection.getAttribute(student, "Name")); // 直接调用MBean中的方法 String info = (String) serverConnection.invoke(student, "studentInfo", null, null); System.out.println(info); // 创建代理实现访问 StudentMBean proxy = MBeanServerInvocationHandler.newProxyInstance(serverConnection, student, StudentMBean.class, false); System.out.println("age: " + proxy.getAge()); } }
2.1
中基于JConsole的JMX示例,只能通过本地进程的方式进行访问
如果想要以最小的代价使其支持远程访问,可以在程序运行时添加以下jvm参数(idea的run Configurations)
-Dcom.sun.management.jmxremote
-Dcom.sun.management.jmxremote.port=8880
-Dcom.sun.management.jmxremote.authenticate=false
-Dcom.sun.management.jmxremote.ssl=false
-Djava.rmi.server.hostname=localhost
然后重新启动JConsoleAgent,通过localhost:8880
便可实现远程访问
注意: 任意一个Java程序,都包含默认的MBean,如下图所示
通过java命令启动程序的命令如下,注意:请使用类的完全限定名
java -Dcom.sun.management.jmxremote -Dcom.sun.management.jmxremote.port=8880 -Dcom.sun.management.jmxremote.authenticate=false -Dcom.sun.management.jmxremote.ssl=false -Djava.rmi.server.hostname=localhost vivo.internet.study.jmx.JConsoleAgent
当然,同样可以通过Client程序实现与JMX的交互:
service:jmx:rmi:///jndi/rmi://localhost:8880/jmxrmi
通过工作所接触的大数据组件,自己对JMX的理解非常表面:就是一个可以动态获取程序运行状态的工具,从而实现对Java程序的监控
通过这次的学习,发现JMX的强大远超想象:
JMX的架构:基础层、代理层、接入层,不同的接入方式:JConsole、网页、客户端
JMX的实现示例
一些注意事项:
参考文档:
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。