Java之多线程
今天考虑有关线程的代码问题,如何写多线程,什么的东西应该定义为线程?等等等。。
随笔记下来,防止后面忘记了。。
什么是并发和并行?
并发是同一时间间隔内执行多个任务。
并行是同一时间执行多个任务。
多线程是并发,CPU多核是并行。
对线程分析:线程、资源
线程执行应用程序的不同功能,正是由于多线程是并发执行的,他们调用CPU的异步性,实现线程访问临界资源的异步性。
临界资源可以是实体(仓库、筷子、账户),也可以是打印机等硬件等不能同时(并发)进行多个操作(线程同步能解决同时进行多个操作引发的线程不安全问题)。
创建线程的两种方法?
创建线程的类可以继承Thread也可以是实现Runnable,都要重写run方法。run方法中的内容就是线程执行的代码。
开发过程中推荐使用实现Runnable接口,因为Java中的类是单继承,多实现的,为了保证类的扩展性,实现Runnable接口的方法还可以继承其他类和实现其他接口。
2017年5月28日 15:51:16
线程同步是一种方法,是指多线程异步方式访问同一数据或同一代码块,用同步锁(synchronized)锁住相同对象的代码,synchronized可以修饰方法也可以修饰块,修饰方法时默认锁对象是this,也就是当前类对象,修饰块时锁对象要自定义,可以是任意对象,但是要保证异步代码块的锁对象是同一个。也可以使用ReentrantLock进行同步,比synchronized更灵活。
线程安全是由于同时访问了同一数据或同一代码块出现的数据丢失等问题,可想而知,线程同步就是来解决线程安全问题的,只要保证了数据和代码块的异步,线程安全问题便可解决。
线程通信是由于多个线程之间相互制约出现的问题,线程同步中的等待(wait())和唤醒(notify())可以解决这个问题,当然wait()和notify()是Object类中的方法,既然是线程同步,必须有synchronized保证的线程同步,而wait()可以在适当的时刻休眠,释放该对象的锁,当前线程变成阻塞状态,等待其他线程先执行,当满足一定的条件,使用notify()方法可以唤醒休眠中锁对象相同的线程,从哪跌倒从哪爬起来,也就是休眠的地方,但是也需要注意notify()只是说明唤醒的线程变成就绪状态可以执行了,并不是一定立刻执行,具体什么时候执行还要看CPU调度。多线程中notify()是唤醒相同锁对象的任意一个休眠的线程,不能保证是唤醒哪一个,除非只有一个锁对象相同的休眠线程;如果想唤醒锁对象相同的全部休眠线程,应该用notifyAll()。
wait()和notify()为什么是Object类的方法?
synchronized的锁对象是任意的,而wait()和notify()以及notifyAll()是基于锁对象的,为了保证任意锁对象能调用wait()和notify()等方法,所以要把这几个方法定义在Object中。
Lock锁的用法
创建Lock锁对象,Lock是接口,所以要创建Lock子类的对象。
Lock lock = new ReentrantLock();
用Lock中的获取锁lock()和释放锁unlock()锁住的代码块是以Lock对象为锁对象,而且lock()和unlock()位置随意,所以用起来相对同步锁灵活,但是每用一个锁就要单独创建一个Lock对象(子类)相对麻烦。
Lock锁与Condition类结合也可以解决线程通信问题,且与synchronized具有相同的作用(休眠和唤醒),使用Condition类中的休眠await()和唤醒single()、singleAll()方法,而Condition的休眠和唤醒不是基于锁对象的,是基于Condition对象的,所以Lock和Condition结合可以很轻松的实现准确休眠和唤醒。
2017年5月31日 17:36:31
线程池
由于创建线程每次都要与操作系统进行交互,成本较高,所以引入线程池,一次性创建多个线程,用完回收可多次使用。
优点:线程中的线程结束之后不会立即死亡,而是回收到线程池变成空闲状态,等待调用下个对象。
创建线程池的方法
Executors类中的方法创建线程池
- static ExecutorService newCachedThreadPool()
- 创建一个可根据需要创建新线程的线程池,但是在以前构造的线程可用时将重用它们。
- static ExecutorService newFixedThreadPool(int nThreads)
- 创建一个可重用固定线程数的线程池,以共享的无界队列方式来运行这些线程。
- static ExecutorService newSingleThreadExecutor()
- 创建一个使用单个 worker 线程的 Executor,以无界队列方式来运行该线程。
注:根据源码,实际上创建线程返回的是ThreadPoolExecutor类对象,方法用ExecutorService接收。下面的submit方法实际上是ThreadPoolExecutor的父类、ExecutorService的子类AbstractExecutorService重写的方法。
ExecutorService接口中的方法接收并对线程池操作
- Future<?> submit(Runnable task)
- 提交一个 Runnable 任务用于执行,并返回一个表示该任务的 Future。
- void shutdown()
- 启动之后顺序关闭,执行以前提交的任务,但不接受新任务。
- List<Runnable> shutdownNow()
- 试图停止所有正在执行的活动任务,暂停处理正在等待的任务,并返回等待执行的任务列表。