通过合理的时间调度,避开共享资源的存取冲突。另外,在并行任务设计上可以通过适当的策略,保证任务与任务之间不存在共享资源,设计一个规则来保护一个客户的计算工作和数据访问只会被一个线程或一台机器完成,而不是把一个客户的计算工作分配给多个线程去完成
其中Running表示运行状态,Runable表示就绪状态(万事俱备,只欠CPU),Blocked表示阻塞状态,阻塞状态又有多种情况,可能是因为调用wait()方法进入等待池,也可能执行同步方法或同步代码块进入等锁池,或者是调用了sleep()方法或join()方法等待休眠或其他线程结束,或是因为发生了I/O中断。
在面向对象编程中,创建和销毁对象是很费时间的,因为创建一个对象要获取内存资源或者其他更多资源。在Java中更是如此,虚拟机将试图跟踪每一个对象,以便能够在对象销毁后进行垃圾回收。所以提高服务程序效率的一个手段就是尽可能减少创建和销毁对象的次数,特别是一些很耗资源的对象创建和销毁,这就是“池化资源”技术产生的原因。 线程池:事先创建若干个可执行的线程放入一个池(容器)中,需要的时候从池中获取线程不用自行创建,使用完毕不需要销毁线程而是放回池中,从而减少创建和销毁线程对象的开销。 Java5+中的EXecutor接口定义一个执行线程的工具。他的子类型即线程池接口是ExecutorService。要配置一个线程池是比较复杂的,尤其是对于线程池原理不是很清楚的情况下,Executors里面提供了一些静态工厂方法,生成一些常用的线程池: 1.newSingleThreadExecutor:创建一个单线程的线程池。这个线程池只有一个线程在工作,也就是相当于单线程串行执行所有的任务。如果这个唯一的线程因为异常结束,那么就会有一个新的线程来代替执行它。此线程池保证所有任务的执行顺序按照任务的提交顺序执行 2.newFixedThreadPool:创建固定大小的线程池。如果线程池的大小超过了处理任务所需要的线程,那么就会回收部分空闲线程(60秒不执行任务),当任务数增加时,此线程池又可以智能的添加新线程来处理任务。此线程池不会对线程池大小做限制,线程池大小完全依赖与JVM能够创建的最大线程大小 3.newScheduledThreadPool:创建一个大小无限的线程池。此线程池支持定时以及周期性执行任务的需求 4.newSingleThreadExecutor:创建一个单线程的线程池。此线程池支持定时以及周期性执行任务的需求
如果系统中存在临界资源(资源数量少于竞争资源的线程数量的资源),例如:正在写的数据以后可能被另一个线程读到,或者正在读的数据可能已经被另一个线程写过了,那么这些数据就必须进行同步存取(数据库操作中的排他锁)。应用程序在对象上调用了一个需要花费很长时间来执行的方法,并且不希望让程序等待方法的返回时,就应该使用异步编程,在很多情况下采用异步途径往往更有效率。事实上,同步就是这阻塞式操作,异步就是非阻塞式操作
1.wait():使一个线程处于等待(阻塞)状态,并且释放所持有的对象的锁; 2.sleep():使一个正在运行的线程处于睡眠状态,是一个静态方法,调用此方法要处理InterruptedException异常; 3.notify():唤醒一个处于等待状态的线程,并不能确切的唤醒某一个等待状态的线程,而是由JVM确定唤醒哪个线程,而且与优先级无关; 4.notifyAll():唤醒所有处于等待状态的线程,该方法并不是将对象的锁给所有线程,而是让它们竞争,只有获得锁的线程才能进入就绪状态; 通过Lock接口提供了显式的锁机制(explicit lock),增强了灵活性以及对线程的协调。Lock接口中定义了加锁(Lock())和解锁(unlock())的方法,同时还提供了newCondition()方法来产生用于线程之间通信的Condition对象;此外,Java5还提供了信号量机制(semaphore),信号量可以用来限制对某个共享资源进行访问的线程的数量。在对资源进行访问之前,线程必须得到信号量的许可(调用Semaphore对象的acquire()方法);在完成对资源的访问后,线程必须向信号量归还许可(调用Semaphore对象的release()方法)。
不能。其他线程只能访问该对象的非同步方法,同步方法则不能进入。因为非静态方法上的synchronized修饰符要求执行方法时要获得对象的锁,如果已经进入了A方法说明对象的锁已经被取走了,那么试图进入B方法的线程就只能在等锁池中等待对象的锁。
1.sleep()方法给其他线程运行机会时不考虑线程的优先级,因此会给低优先级的线程以运行的机会;yield()方法只会给相同优先级或更高优先级的线程以运行的机会; 2.线程执行sleep()方法后转入阻塞(blocked)状态,而执行yield()方法后转入的是就绪(ready)状态; 3.sleep()方法声明抛出异常InterruptedException,而yield()方法没有声明任何异常; 4.sleep()方法比yield()方法(跟操作系统CPU调度相关)具有更好的可移植性。
有两种实现方法,分别是继承Thread类与实现Runnable接口。 用synchronized关键字修饰同步方法。 反对使用stop(),因为他不安全。他会解除由线程获取的所有锁定,而却如果对象处于一种不连贯状态,那么其他线程能在那种状态下检查和修改他们。结果很难查出真正的问题所在。suspend()方法容易发生死锁,调用suspend()方法的时候,目标线程会停下来,但却仍然持有在这之前获得的锁定。此时其他任何线程都不能访问锁定的资源,除非被”挂起“的线程恢复运行。对任何线程来说,如果他们想恢复目标线程,同时又试图使用任何一个锁定的资源,就会造成死锁。所以不应该使用suspend(),而应该在自己的Thread类中置入一个标志,指出线程应该活动还是挂起。若标志指出线程应该挂起,便用wait()命其进入等待状态。若标志指出线程应当恢复,则用一个notify()重新启动线程。
多线程有两种实现方法,分别是继承Thread类与实现Runnable接口。 同步的实现有两种:synchronized,wait和notify。
启动一个线程是调用start()方法,使线程所代表的虚拟机处于可运行状态,这意味着它可以由JVM调度并执行。这并不意味着线程就会立即执行,run()方法可以穿必须退出的标志停止一个线程
sleep是线程类(Thread)的方法,导致此线程暂停执行指定时间,把执行机会给其他线程,但是监控状态依然保持,到时后会自动恢复。调用sleep不会释放对象锁。 wait时Object类的方法,对此对象调用wait方法导致本线程放弃对象锁,进入等待此对象的等锁池,只有针对此对象发出notify()方法或notifyAll()方法后本线程才能进入对象的等锁池准备获得对象锁进入运行状态。
监视器和锁在Java虚拟机中是一块使用的。监视器监视一块同步代码块,确保一次只有一个线程执行同步代码块。每一个监视器都和一个对象引用相关联。线程再获取锁之前不允许执行同步代码
同步方法默认用this或者当前类class对象作为锁; 同步代码块可以选择一声明来加锁,比如同步方法要跟细颗粒度,我们可以选择只同步会发生同步问题的部分代码而不是整个方法
1.新建(new):新创建了一个线程对象。 2.可运行(runnable):线程对象创建后,其他线程(比如main线程)调用了该对象的start()方法。该状态的线程位于可运行线程池中,等待被线程调度选中,获取CPU使用权 3.运行(running):可运行状态(runnable)的线程获得了CPU时间片(timeslice),执行程序代码。 4.阻塞(block):阻塞状态是指线程因为某种原因放弃了cpu的使用权,也即让出了cpu timeslice暂时停止运行。直到进入可运行状态(runnable),才有机会再次获得cpu timesclice转到运行态(running)。阻塞的情况分为三种: A.等待阻塞:运行(running)的线程执行o.wait()方法,JVM会把该线程放入等待队列(waiting queue)中。 B.同步阻塞:运行(running)的线程再获取对象的同步锁时,若该同步锁被别的线程占用,则JVM会把该线程放入到等锁池中。 C.其他阻塞:运行(running)的线程执行Thread.sleep(long ms)或t.join()方法,或者发出了I/O请求时,JVM会把该线程置为阻塞状态。当sleep()状态超时,join()等待线程终止或者超时或者I/O处理完毕时,线程重新转入可运行状态(Runnable)。 5.死亡(dead):线程run()、main()方法执行结束,或者因为异常推出了run()方法,则该线程结束生命周期。死亡的线程不可再次复活