本文共 6418 字,大约阅读时间需要 21 分钟。
/* * 多个线程同时更新计数器(模拟多线程中存在的问题) */public class Temp_1 { public static void main(String[] args) { // 连续模拟操作 10 次 for(int i = 0;i < 10;i++) { update_counter_demo(); } } // 构造工作者线程,模拟多线程同时更新计数器 public static void update_counter_demo(){ Counter counter = new Counter(0); int worker_thread_num = 10; // 10 个工作者线程 Thread[] workers = new Thread[worker_thread_num]; for(int j = 0;j < worker_thread_num;j++) { workers[j] = new Thread(new Runnable() { @Override public void run() { try { Thread.sleep(100); // 先等待 100 毫秒 } catch (InterruptedException e) { e.printStackTrace(); } for(int i = 0;i < 100;i++) { // 执行累加 100 次 counter.plusOne(); } } }); } // 启动所有工作者线程 for(int i = 0;i < worker_thread_num;i++) { workers[i].start(); } // 等待线程结束 for(int i = 0;i < worker_thread_num;i++) { try { workers[i].join(); } catch (InterruptedException e) { e.printStackTrace(); } } // 打印计数器数值 System.out.println(counter); } /* 简单计数器 */ private static class Counter { private int count; public Counter(int count) { this.count = count; } /* 计数器加 1 */ public void plusOne() { count++; } @Override public String toString() { return "[ count is " + count + " ]"; } }}
执行结果如下:
[ count is 838 ][ count is 932 ][ count is 995 ][ count is 969 ][ count is 827 ][ count is 979 ][ count is 1000 ] // 正确结果[ count is 1000 ] // 正确结果[ count is 820 ][ count is 998 ]
可以发现,连续模拟操作 10 次,其中 碰巧 有 2 次的运作结果是正确的!我们知道,错误结果是 未同步多个线程对共享变量的操作 导致的。
/* 简单计数器 */ private static class Counter { private int count; public Counter(int count) { this.count = count; } /* 计数器加 1,使用 synchronized 关键字修饰*/ synchronized public void plusOne() { count++; } @Override public String toString() { return "[ count is " + count + " ]"; } }
再次运行程序,执行结果如下:
[ count is 1000 ][ count is 1000 ][ count is 1000 ][ count is 1000 ][ count is 1000 ][ count is 1000 ][ count is 1000 ][ count is 1000 ][ count is 1000 ][ count is 1000 ]
可见运行结果正确,synchronized 关键字为我们提供保障。
/* * 多个线程同时更新计数器(使用自定义锁同步多线程操作) */public class Temp_1 { public static void main(String[] args) { // 连续模拟操作 10 次 for(int i = 0;i < 10;i++) { update_counter_demo(); } } // 构造工作者线程,模拟多线程同时更新计数器 public static void update_counter_demo(){ Counter counter = new Counter(0); int worker_thread_num = 10; Thread[] workers = new Thread[worker_thread_num]; for(int j = 0;j < worker_thread_num;j++) { workers[j] = new Thread(new Runnable() { @Override public void run() { try { Thread.sleep(100); // 先等待 100 毫秒 } catch (InterruptedException e) { e.printStackTrace(); } for(int i = 0;i < 100;i++) { counter.plusOne(); } } }); } // 启动所有工作者线程 for(int i = 0;i < worker_thread_num;i++) { workers[i].start(); } // 等待线程结束 for(int i = 0;i < worker_thread_num;i++) { try { workers[i].join(); } catch (InterruptedException e) { e.printStackTrace(); } } // 打印计数器数值 System.out.println(counter); } /* 简单计数器 */ private static class Counter { private int count; private MyLock lock; public Counter(int count) { this.count = count; this.lock = new MyLock(); } /* 计数器加 1 */ public void plusOne() { lock.lock(); // 获取锁:保护累加操作 try { count++; } finally { lock.unLock(); // 释放锁 } } @Override public String toString() { return "[ count is " + count + " ]"; } }}/* 简单锁实现(注意:这里并没有记录当前持有锁的线程对象, * 所以不能阻止未持有锁的线程释放锁,仅仅为了实现简单, * 而且如果使用方式正确,就不会有这种问题) */class MyLock{ private int count = 0; /* 获取锁 */ public void lock() { synchronized(this) { while(count != 0) { try { wait(); // 当前线程主动等待(期望被 notify() 或 interrupted()) } catch (InterruptedException e) { e.printStackTrace(); } } count++; } } /* 释放锁 */ public void unLock() { synchronized (this) { if(count > 0) { count--; notify(); // 通知(唤醒)在同一个对象监视器上等待的线程 } } }}
执行程序,运行结果正确。自定义锁 MyLock 中,lock() 方法执行加锁操作,它在一个 synchronized 代码块中检查锁的状态,如果锁的状态不为 0 则表示‘已加锁’,执行等待操作,否则通过更改锁的状态,从而获取锁。释放锁的操作也类似, 在 synchronized 提供的排它性保障下,通过更改锁的状态来表达‘加锁’与‘解锁’ 。当然,这个自定义锁的明显缺陷有:1 没有记录是那个线程拥有锁,从而不能阻止未执行加锁操作的线程释放锁。
转载地址:http://hclsi.baihongyu.com/