diff --git a/src/main/clojure/clojure/java/jmx.clj b/src/main/clojure/clojure/java/jmx.clj
index 2cf77d5..4cedbfd 100644
--- a/src/main/clojure/clojure/java/jmx.clj
+++ b/src/main/clojure/clojure/java/jmx.clj
@@ -25,10 +25,16 @@
     -> (:Verbose :ObjectPendingFinalizationCount 
         :HeapMemoryUsage :NonHeapMemoryUsage)
 
-  What is the value of an attribute? 
+  What is the value of an attribute?
 
     (jmx/read \"java.lang:type=Memory\" :ObjectPendingFinalizationCount)
     -> 0
+    (jmx/read \"java.lang:type=Memory\" [:HeapMemoryUsage :NonHeapMemoryUsage])
+    ->
+    {:NonHeapMemoryUsage
+      {:used 16674024, :max 138412032, :init 24317952, :committed 24317952},
+     :HeapMemoryUsage
+      {:used 18619064, :max 85393408, :init 0, :committed 83230720}}
 
   Can't I just have *all* the attributes in a Clojure map?
 
@@ -184,13 +190,20 @@
   (.getMBeanInfo *connection* (as-object-name n)))
 
 (defn raw-read
-  "Read an mbean property. Returns low-level Java object model for
-   composites, tabulars, etc. Most callers should use read."
-  [n attr]
-  (.getAttribute *connection* (as-object-name n) (name attr)))
-
-(def read
-  "Read an mbean property."
+  "Read a list of mbean properties. Returns low-level Java object
+   models for composites, tabulars, etc. Most callers should use
+   read."
+  [n attrs]
+  (if (sequential? attrs)
+    (into {}
+          (map (fn [attr] [(keyword (.getName attr)) (.getValue attr)])
+               (.getAttributes *connection*
+                               (as-object-name n)
+                               (into-array (map name attrs)))))
+    (.getAttribute *connection* (as-object-name n) (name attrs))))
+
+(def ^{:doc "Read one or more mbean properties."}
+  read
   (comp objects->data raw-read))
 
 (defn- read-supported
diff --git a/src/test/clojure/clojure/java/test_jmx.clj b/src/test/clojure/clojure/java/test_jmx.clj
index b67c0fb..1a490bf 100644
--- a/src/test/clojure/clojure/java/test_jmx.clj
+++ b/src/test/clojure/clojure/java/test_jmx.clj
@@ -60,7 +60,12 @@
              (are [a b] (= a b)
                   false (jmx/raw-read mem :Verbose))
              (are [type attr] (instance? type attr)
-                  Number (jmx/raw-read mem :ObjectPendingFinalizationCount)))))
+                  Number (jmx/raw-read mem :ObjectPendingFinalizationCount)))
+    (testing "reading multiple attributes"
+             (are [a b] (= a b)
+                  false ((jmx/raw-read mem [:Verbose]) :Verbose))
+             (are [type attr] (instance? type attr)
+                  Number ((jmx/raw-read mem [:ObjectPendingFinalizationCount]) :ObjectPendingFinalizationCount)))))
 
 (deftest reading-attributes
   (testing "simple scalar attributes"
@@ -72,6 +77,17 @@
   (testing "tabular attributes"
            (is (map? (jmx/read "java.lang:type=Runtime" :SystemProperties)))))
 
+(deftest reading-multiple-attributes
+  (testing "simple scalar attributes"
+           (are [type attr] (instance? type attr)
+                Number ((jmx/read "java.lang:type=Memory" [:ObjectPendingFinalizationCount]) :ObjectPendingFinalizationCount)))
+  (testing "composite attributes"
+           (are [ks attr] (=set ks (keys attr))
+                [:used :max :init :committed]
+                ((jmx/read "java.lang:type=Memory" [:HeapMemoryUsage :NonHeapMemoryUsage]) :HeapMemoryUsage)))
+  (testing "tabular attributes"
+           (is (map? ((jmx/read "java.lang:type=Runtime" [:SystemProperties]) :SystemProperties)))))
+
 (deftest writing-attributes
   (let [mem "java.lang:type=Memory"]
     (jmx/write! mem :Verbose true)
