在美眉图片下载demo中,我们可以看到多个线程在公用一些变量,这个时候难免会发生冲突。冲突并不可怕,可怕的是当多线程的情况下,你没法控制冲突。按照我的理解在java中实现同步的方式分为三种,分别是:同步代码块机制,锁机制,信号量机制。
一、同步代码块
在java的多线程并发开发过程中,我们最常用的方式就是使用同步代码关键字(synchronized)。这种方式的使用不是特别复杂,需要注意的只是你需要明确到底同步的是那个对象,只有当同步的对象一致的情况下,才能够控制互斥的操作。一般情况下,我们会同步this或者是当前class对象。同步this对当前实例有效,同步class对当前所有class的对象有效。下面这个demo的功能是,启动十个线程,最终结果是每个线程都将共享的变量加上1.
private static final java.util.Random random = new java.util.Random(System.currentTimeMillis()); public static void main(String[] args) { Runnable runnable = new Runnable() { private int count = 0; // 资源对象 @Override public void run() { try { int oldCount = count; Thread.sleep(random.nextInt(1000) + 10); // 处理 count = oldCount + 1; System.out.println(Thread.currentThread().getName() + ", 原有资源:" + oldCount + ", 现在预期资源:" + (oldCount + 1) + ",现在实际资源:" + count); } catch (InterruptedException e) { } } }; for (int i = 0; i < 10; i++) { new Thread(runnable).start(); } }
我们可以发现结果如下图所示,明显可以看出在是个线程访问一个变量的情况下,导致最终的结果不对。
加同步锁的代码和上述代码差不多,区别只是在获取资源和修改资源的时候进行同步块处理。
int oldCount = 0; synchronized (this) { oldCount = count; Thread.sleep(random.nextInt(1000) + 10); // 处理 count = oldCount + 1; }
二、锁机制
在Java中的锁机制是通过java.util.concurrent.locks.Lock来实现的,这个接口主要有三个实现类,分别是ReentrantLock,ReentrantReadWriteLock.ReadLock, ReentrantReadWriteLock.WriteLock。锁机制和同步代码块相比,我们可以明显的发现使用lock机制会降低同步粒度,提高性能。特别是在一些情况下,使用lock是一种非常不错的选择,比如说在读远远高于写的状况下,使用读写锁那是一种非常不错的选择。下面直接来一个cachedemo
1 static class CacheDemo { 2 private static final int maxSize = 100000; // 最大存储量 3 private static CacheDemo demo; 4 private Map<String, String> cache = new LinkedHashMap<String, String>() { 5 private static final long serialVersionUID = -7259602073057254864L; 6 7 protected boolean removeEldestEntry(Map.Entry<String, String> eldest) { 8 return maxSize > this.size(); // 超过就移除 9 }; 10 }; 11 private ReentrantReadWriteLock rrel = new ReentrantReadWriteLock(); 12 private Lock writeLock = rrel.writeLock(); // 写锁 13 private Lock readLock = rrel.readLock(); // 读锁 14 15 /** 16 * 获取cache对象 17 * 18 * @return 19 */ 20 public static CacheDemo instance() { 21 if (demo == null) { 22 synchronized (CacheDemo.class) { 23 if (demo == null) { 24 demo = new CacheDemo(); 25 } 26 } 27 } 28 return demo; 29 } 30 31 /** 32 * 添加 33 * 34 * @param key 35 * @param value 36 */ 37 public void put(String key, String value) { 38 this.writeLock.lock(); // 加锁 39 try { 40 this.cache.put(key, value); 41 } finally { 42 // 防止在操作过程中出现异常,使用try-finally保证解锁一定执行。 43 this.writeLock.unlock(); // 解锁 44 } 45 } 46 47 /** 48 * 获取这个对象 49 * 50 * @param key 51 * @return 52 */ 53 public String get(String key) { 54 this.readLock.lock(); // 加锁 55 try { 56 return this.cache.get(key); 57 } finally { 58 this.readLock.unlock(); // 解锁 59 } 60 } 61 62 /** 63 * 移除key 64 * 65 * @param key 66 */ 67 public void remove(String key) { 68 this.writeLock.lock(); 69 try { 70 this.cache.remove(key); 71 } finally { 72 this.writeLock.unlock(); 73 } 74 } 75 76 /** 77 * 清空 78 */ 79 public void clean() { 80 this.writeLock.lock(); 81 try { 82 this.cache.clear(); 83 } finally { 84 this.writeLock.unlock(); 85 } 86 } 87 }