为什么我会把volatile与zookeeper放在一起,原因是这两个有个共同点,就是可见性。
我写了两个测试代码,先给出volatile的测试代码
import java.util.concurrent.ArrayBlockingQueue; import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.TimeUnit; /** * volatile修饰的变量的可见性测试 * * @author zhaowen * */ public class AtomicTest { // 全部线程可见变量 public static volatile int ccount = 0; // 计数器 public static int add = 0; // 线程安全示例 class AddSafe implements Runnable { @Override public void run() { for (;;) { // 相当于可见性的锁 if (ccount == 0) { try { // lock ccount = 1; ++add; System.out.println(add); System.out.println(add); System.out.println(add); // exit the for loop return; } finally { // unlock ccount = 0; } } } } } // 线程不安全示例 class AddUnsafe implements Runnable { @Override public void run() { ++add; System.out.println(add); System.out.println(add); System.out.println(add); } } public void test() { ThreadPoolExecutor executor = new ThreadPoolExecutor(8, 16, 30L, TimeUnit.SECONDS, new ArrayBlockingQueue<Runnable>(200), new ThreadPoolExecutor.CallerRunsPolicy()); for (int i = 0; i < 100; i++) { executor.execute(new AddSafe()); // executor.execute(new AddUnsafe()); } } public static void main(String[] args) { AtomicTest t = new AtomicTest(); t.test(); } }
这是zookepper的测试代码:
import java.util.concurrent.ArrayBlockingQueue; import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.TimeUnit; import org.I0Itec.zkclient.IZkDataListener; import org.I0Itec.zkclient.ZkClient; import org.apache.zookeeper.CreateMode; /** * zookeeper数据的可见性测试 * * @author zhaowen * */ public class Subscribe { // 结点名 private static final String PATH = "/clusters"; class Sub implements Runnable { @Override public void run() { // 创建连接 ZkClient zkClient = new ZkClient("127.0.0.1:2181"); // 初始化 if (!zkClient.exists(PATH)) { zkClient.create(PATH, "false", CreateMode.PERSISTENT); } // 增加监听 zkClient.subscribeDataChanges(PATH, new IZkDataListener() { @Override public void handleDataChange(String datapath, Object data) throws Exception { // 处理结点数据变化事件 System.out.println(System.currentTimeMillis() + " : [" + this.hashCode() + "] change : " + String.valueOf(data)); } @Override public void handleDataDeleted(String datapath) throws Exception { // 处理结点删除事件 System.out.println(System.currentTimeMillis() + " : [" + this.hashCode() + "]" + " node deleted"); } }); } } public void test() { ThreadPoolExecutor executor = new ThreadPoolExecutor(8, 16, 30L, TimeUnit.SECONDS, new ArrayBlockingQueue<Runnable>(200), new ThreadPoolExecutor.CallerRunsPolicy()); for (int i = 0; i < 10; i++) { executor.execute(new Sub()); } } public static void main(String[] args) throws Exception { // 创建连接 ZkClient zkClient = new ZkClient("127.0.0.1:2181"); // 初始化 if (!zkClient.exists(PATH)) { zkClient.create(PATH, "false", CreateMode.PERSISTENT); } // 启动监听 Subscribe subtest = new Subscribe(); subtest.test(); // 延时2秒 Thread.sleep(2000); // 测试结点数据的变化事件 zkClient.writeData(PATH, "true"); // 延时2秒 Thread.sleep(2000); // 测试结点数据的删除事件 zkClient.delete(PATH); } }
volatile修饰的变量在多线程环境下的作用是全局的可见性,即当一个线程修改后,其他线程对此变量是立马可见的,这一点我发现和在分布式集群环境下zookeeper所存储的变量的特性相同,当集群中的某个结点修改zookeeper中的变量后,所有监听的结点都能立马感知,会收到具体的变更事件,这就是我为什么要把volatile和zookeeper放在一起的原因,希望能对理解volatile和zookeeper有些帮助。
volatile修饰的变量与原子性是没有关系的,我最初接触volatitle是在写嵌入式C程序时,对于摄像头采集的数据用volatile修饰,意即当硬件使数据变化时,代码也能立马感知处理。
从Demo代码可以拓展到分布式锁的应用,具体怎么用应该很明了了。