关于Java多线程和并发运行的学习(三)


开学了,早上有课,下午就没课了。所以只有下午才能继续学习咯~~

昨天下午对简单的线程池了解了一下,Lock以及读写锁,缓存系统构成的大概原理,最后是Future和Callable(其实我还不知道最后两个到底有什么,不过作为知识点,我还是了解了一下。)

上一偏的知识点是关于线程中的线程范围数据共享,ThreadLocal以及多线程数据共享。


一,关于线程池

这里我们会使用到接口 ExecutorService,这个接口比较繁琐。不懂请查阅API文档。

<span style="font-size:14px;">package Multithreading;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;

public class ThreadPoolTest {
	public static void main(String[] args) {
		//ExecutorService threadPool = Executors.newFixedThreadPool(3);//创建3个线程(创建固定大小的线程池)
		//ExecutorService threadPool = Executors.newCachedThreadPool();//动态线程,需要多少给多少(创建缓存线程池)
		ExecutorService threadPool = Executors.newSingleThreadExecutor();//创建单一线程池,如果这个池子里的线程死了,它就再生成一个线程,总之要保持一个线程(如何实现线程低调后重新启动?)
		
		for (int i = 1; i <= 10; i++) {
			final int task = i;
			threadPool.execute(new Runnable() {
				public void run() {
					for (int j = 1; j <= 10; j++) {
						try {
							Thread.sleep(30);
						} catch (InterruptedException e) {
							// TODO Auto-generated catch block
							e.printStackTrace();
						}
						System.out.println(Thread.currentThread().getName() + " is looping of " + j + " for task of " + task);
					}
				}
			});
		}
		System.out.println("all of 10 tasks have committed!");
		threadPool.shutdown();//运行后关闭线程
		//threadPool.shutdownNow();//立即关闭线程
		
		Executors.newScheduledThreadPool(3).schedule(new Runnable() {
			public void run() {
				System.out.println("bombing!");
			}
		}, 10, TimeUnit.SECONDS);
	}
}</span>

二,关于Lock以及读写锁

Lock & Condition实现线程同步通信:
       Lock比传统线程模型中的synchronized方式更加面向对象,与生活中的锁类似,锁本身也应该是一个对象。两个线程执行的代码片段要实现同步互斥的效果,它们必须用同一个Lock对象。锁是上在代表 要操作的 资源的 类的 内部方法 中,而不是线程代码中!!这里贴出Lock与synchronized的对比代码,这里引用的是synchronized例子的代码,只是在同步锁的地方将synchronized替换为Lock。代码如下:

package Multithreading;

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class LockTest {
	public static void main(String[] args) {
		new LockTest().init();
	}

	// 要想创建内部类的实体对象,就必须有一个外部类的实体对象
	private void init() {
		final Outputer outputer = new Outputer();
		new Thread(new Runnable() {
			public void run() {
				while (true) {
					try {
						Thread.sleep(10);
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
					outputer.output("zhangxiaoxiang");
				}
			}
		}).start();

		new Thread(new Runnable() {
			@Override
			public void run() {
				while (true) {
					try {
						Thread.sleep(10);
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
					outputer.output("lihongming");
				}
			}
		}).start();
	}

	static class Outputer {
		Lock lock = new ReentrantLock();// 制造一把锁
		public void output(String name) {
			int len = name.length();
			lock.lock();//锁上
			try {
				for (int i = 0; i < len; i++) {
					System.out.print(name.charAt(i));
				}
				System.out.println();
			} finally {
				lock.unlock();//开锁
			}
		}

		public synchronized void output2(String name) {
			int len = name.length();
			for (int i = 0; i < len; i++) {
				System.out.print(name.charAt(i));
			}
			System.out.println();
		}

		// static方法的线程锁
		public static synchronized void output3(String name) {
			int len = name.length();
			for (int i = 0; i < len; i++) {
				System.out.print(name.charAt(i));
			}
			System.out.println();
		}
	}
}

在读写锁中,分读锁readLock和写锁writeLock,读读不用上锁,读写和写写需要上锁。

package Multithreading;

import java.util.Random;
import java.util.concurrent.locks.ReentrantReadWriteLock;

public class ReadWriteLockTest {
	public static void main(String[] args) {
		final Queue q = new Queue();
		for (int i = 0; i < 3; i++) {//创建3个线程
			new Thread() {
				public void run() {
					while (true) {
						q.get();//读线程
					}
				}
			}.start();

			new Thread() {
				public void run() {
					while (true) {
						q.put(new Random().nextInt(10000));//写线程
					}
				}
			}.start();
		}
	}
}

class Queue {
	private Object data = null;// 共享数据,只能有一个线程能写该数据,但可以有多个线程读该数据
	private ReentrantReadWriteLock rwl = new ReentrantReadWriteLock();//创建读写锁

	public void get() {
		rwl.readLock().lock();//上读锁
		System.out.println(Thread.currentThread().getName()
				+ " be ready to read data ! ");
		try {
			Thread.sleep((long) (Math.random() * 1000));
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		System.out.println(Thread.currentThread().getName()
				+ " have read data :" + data);
		rwl.readLock().unlock();//解读锁
	}

	public void put(Object data) {
		rwl.writeLock().lock();
		System.out.println(Thread.currentThread().getName()
				+ " be ready to write data !");
		try {
			Thread.sleep((long) (Math.random() * 1000));
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		this.data = data;
		System.out.println(Thread.currentThread().getName()
				+ " have write data :" + data);
		rwl.writeLock().unlock();
	}
}

三,关于系统缓存

这里可以查询API手册里的ReentrantReadWriteLock,

里面有一个例子,如何利用重入来执行升级缓存后的锁降级:


<span style="font-size:14px;">class CachedData {
   Object data;
   volatile boolean cacheValid;
   ReentrantReadWriteLock rwl = new ReentrantReadWriteLock();

   void processCachedData() {
     rwl.readLock().lock();
     if (!cacheValid) {
        // Must release read lock before acquiring write lock
        rwl.readLock().unlock();
        rwl.writeLock().lock();
        // Recheck state because another thread might have acquired
        //   write lock and changed state before we did.
        if (!cacheValid) {
          data = ...
          cacheValid = true;
        }
        // Downgrade by acquiring read lock before releasing write lock
        rwl.readLock().lock();
        rwl.writeLock().unlock(); // Unlock write, still hold read
     }

     use(data);
     rwl.readLock().unlock();
   }
 }
</span>

在使用某些种类的 Collection 时,可以使用 ReentrantReadWriteLock 来提高并发性。通常,在预期 collection 很大,读取者线程访问它的次数多于写入者线程,并且 entail 操作的开销高于同步开销时,这很值得一试。例如,以下是一个使用 TreeMap 的类,预期它很大,并且能被同时访问。

class RWDictionary {
    private final Map<String, Data> m = new TreeMap<String, Data>();
    private final ReentrantReadWriteLock rwl = new ReentrantReadWriteLock();
    private final Lock r = rwl.readLock();
    private final Lock w = rwl.writeLock();

    public Data get(String key) {
        r.lock();
        try { return m.get(key); }
        finally { r.unlock(); }
    }
    public String[] allKeys() {
        r.lock();
        try { return m.keySet().toArray(); }
        finally { r.unlock(); }
    }
    public Data put(String key, Data value) {
        w.lock();
        try { return m.put(key, value); }
        finally { w.unlock(); }
    }
    public void clear() {
        w.lock();
        try { m.clear(); }
        finally { w.unlock(); }
    }
 }

实现注意事项:此锁最多支持 65535 个递归写入锁和 65535 个读取锁。试图超出这些限制将导致锁方法抛出错误

那么实现系统缓存,代码如下:

package Multithreading;

import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;

/**
 * 写一个缓存系统
 */
public class CacheDemo {
	private Map<String, Object> cache = new HashMap<String, Object>();

	public static void main(String[] args) {

	}

	private ReadWriteLock rwl = new ReentrantReadWriteLock();

	public Object getData(String key) {
		rwl.readLock().lock();//读锁
		Object value = null;
		try {
			value = cache.get(key);
			if (value == null) {// 读到的结果如果为空,则需要写入
				rwl.readLock().unlock();// 先解开读锁
				/*
				 * 此时这里会再次进入多个线程
				 */
				rwl.writeLock().lock();// 再锁上写锁,只允许一个线程进,其他线程堵在锁外
				try {
					if (value == null) {//如果检查得到进入的线程为空,则从数据库写入数据;如果不为空,则不用再次写入数据,因为缓存value中含有其对应的值
						value = "aaaa";// 实际是去queryDB();缓存进cache中
					}
				} finally {
					rwl.writeLock().unlock();// 解开写锁
				}
				rwl.readLock().lock();// 恢复读锁
			}
		} finally {
			rwl.readLock().unlock();// 此条线程读完后,解开读锁
		}
		return value;// 返回此条线程得到的数据
	}
}




  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值