1、,Beyond Technology,多线程编程,东软移动互联网事业部 第五研发部,实现方式状态变迁线程调度线程同步Concurrent工具包,概念理解,什么是程序?什么是进程?什么是线程?程序、进程、线程差别?,什么是程序?程序是可以连续执行,并能够完成一定任务的一条条指令的集合。 什么是进程?在某种程度上相互隔离的、独立运行的程序 什么是线程?在程序中并发执行多个指令流,每个指令流都称为一个线程,彼此间互相独立,应用场景,多个线程的执行是并发的,也就是在逻辑上“同时”,而不管是否是物理上的“同时”。如果系统只有一个CPU,那么真正的“同时”是不可能的,但是由于CPU的速度非常快,用户感觉不
2、到其中的区别,因此我们也不用关心它,只需要设想各个线程是同时执行即可。如果应用程序必须等待网络连接或数据库连接等数据吞吐速度相对较慢的资源时,多线程应用程序是非常有利的。基于Internet的应用程序有必要是多线程类型的,例如,当开发要支持大量客户机的服务器端应用程序时,可以将应用程序创建成多线程形式来响应客户端的连接请求,使每个连接用户独占一个客户端连接线程。这样,用户感觉服务器只为连接用户自己服务,从而缩短了服务器的客户端响应时间。,实现方式,继承Thread类实现Runnable接口,继承Thread类run()函数必须进行覆写,把要在多个线程中并行处理的代码放到这个函数中。不能直接调用
3、run()函数,而是通过调用start()函数来调用run()函数。实现Runnable接口Runnable接口只有一个run()函数。把一个实现了Runnable接口的对象作为参数产生一个Thread对象,再调用Thread对象的start()函数就可执行并行操作。问题:编码时如何选择?,状态变迁,线程调度,抢占式轮转式,线程同步,synchronized 方法通过在方法声明中加入 synchronized关键字来声明 synchronized 方法。如:public synchronized void accessVal(int newVal);synchronized 块 通过 sync
4、hronized关键字来声明synchronized 块。语法如下: synchronized(syncObject) /允许访问控制的代码注意:无论synchronized关键字加在方法上还是对象上,它取得的锁都是对象,而不是把一段代码或函数当作锁,Synchronized作用域,是某个对象实例内synchronized aMethod()可以防止多个线程同时访问这个对象的synchronized方法 .不同的对象实例的 synchronized方法是不相干扰的。 是某个类的范围synchronized static aStaticMethod防止多个线程同时访问这个类中的synchroni
5、zed static 方法。它可以对类的所有对象实例起作用。,synchronized(this),当两个并发线程访问同一个对象object中的这个synchronized(this)同步代码块时,一个时间内只能有一个线程得到执行。另一个线程必须等待当前线程执行完这个代码块以后才能执行该代码块。 当一个线程访问object的一个synchronized(this)同步代码块时,另一个线程仍然可以访问该object中的非synchronized(this)同步代码块。 当一个线程访问object的一个synchronized(this)同步代码块时,其他线程对object中所有其它synchro
6、nized(this)同步代码块的访问将被阻塞。,等待与通知,wait()是使持有对象锁的线程释放锁;wait(long)是使持有对象锁的线程释放锁时间为long(毫秒)后,再次获得锁,wait()和wait(0)等价;notify()是唤醒一个正在等待该对象锁的线程,如果等待的线程不止一个,那么被唤醒的线程由jvm确定;notifyAll是唤醒所有正在等待该对象锁的线程.,原子操作,原子操作不需要进行同步控制,原子操作,即不能被线程调试机制中断的操作,一旦操作开始,那么它一定可以在可能发生的”上下文切换”(切换到其他线程执行)执行完毕. 举例:非原子操作:Long和double (加上vol
7、atile ),自加和自减(使用同步),Java 5.0 concurrent工具包,这些工具虽然能在大多数情况下解决对共享资源的管理和线程间的调度,但存在以下几个问题 过于原始,拿来就能用的功能有限,即使是要实现简单的多线程功能也需要编写大量的代码。这些工具就像汇编语言一样难以学习和使用,比这更糟糕的是稍有不慎它们还可能被错误地使用,而且这样的错误很难被发现。如果使用不当,会使程序的运行效率大大降低。为了提高开发效率,简化编程,开发人员在做项目的时候往往需要写一些共享的工具来实现一些普遍适用的功能。但因为没有规范,相同的工具会被重复地开发,造成资源浪费。因为锁定的功能是通过Synchroni
8、zed来实现的,这是一种块结构,只能对代码中的一段代码进行锁定,而且锁定是单一的。,java.util.concurrent 包含了常用的多线程工具,是新的多线程工具的主体。 java.util.concurrent.atomic 包含了不用加锁情况下就能改变值的原子变量,比如说AtomicInteger提供了addAndGet()方法。Add和Get是两个不同的操作,为了保证别的线程不干扰,以往的做法是先锁定共享的变量,然后在锁定的范围内进行两步操作。但用AtomicInteger.addAndGet()就不用担心锁定的事了,其内部实现保证了这两步操作是在原子量级发生的,不会被别的线程干扰。
9、 java.util.concurrent.locks 包包含锁定的工具。,Callable 和 Future接口,Callable和Runnable有几点不同:Callable规定的方法是call(),而Runnable规定的方法是run(). Callable的任务执行后可返回值,而Runnable的任务是不能返回值的。 call()方法可抛出异常,而run()方法是不能抛出异常的。 运行Callable任务可拿到一个Future对象,通过Future对象可了解任务执行情况,可取消任务的执行,还可获取任务执行的结果。,调度和控制,Executor接口:作用:是用来执行Runnable任务的
10、execute(Runnable command):执行Ruannable类型的任务 ExecutorService接口:作用:继承了Executor的方法,并提供了执行Callable任务和中止任务执行的服务submit(task):可用来提交Callable或Runnable任务,并返回代表此任务的Future对象 invokeAll(collection of tasks):批处理任务集合,并返回一个代表这些任务的Future对象集合 shutdown():在完成已提交的任务后关闭服务,不再接受新任务 shutdownNow():停止所有正在执行的任务并关闭服务。 isTerminate
11、d():测试是否所有任务都执行完毕了。 isShutdown():测试是否该ExecutorService已被关闭 ScheduledExecutorService接口作用:继承了ExecutorService的方法,并ScheduledExecutorService提供了按时间安排执行任务的功能schedule(task, initDelay): 安排所提交的Callable或Runnable任务在initDelay指定的时间后执行。 scheduleAtFixedRate():安排所提交的Runnable任务按指定的间隔重复执行 scheduleWithFixedDelay():安排所提交
12、的Runnable任务在每次执行完后,等待delay所指定的时间后重复执行。,Executors类Java 5.0建议使用Executors的工具类来得到Executor接口的具体对象,需要注意的是Executors是一个类,不是Executor的复数形式。Executors提供了以下一些static的方法:callable(Runnable task): 将Runnable的任务转化成Callable的任务 newSingleThreadExecutor: 产生一个ExecutorService对象,这个对象只有一个线程可用来执行任务,若任务多于一个,任务将按先后顺序执行。 newCache
13、dThreadPool(): 产生一个ExecutorService对象,这个对象带有一个线程池,线程池的大小会根据需要调整,线程执行完任务后返回线程池,供执行下一次任务使用。 newFixedThreadPool(int poolSize):产生一个ExecutorService对象,这个对象带有一个大小为poolSize的线程池,若任务数量大于poolSize,任务会被放在一个queue里顺序执行。 newSingleThreadScheduledExecutor:产生一个ScheduledExecutorService对象,这个对象的线程池大小为1,若任务多于一个,任务将按先后顺序执行。
14、 newScheduledThreadPool(int poolSize): 产生一个ScheduledExecutorService对象,这个对象的线程池大小为poolSize,若任务数量大于poolSize,任务会在一个queue里等待执行,Lock接口ReentrantLock是Lock的具体类,Lock提供了以下一些方法:lock(): 请求锁定,如果锁已被别的线程锁定,调用此方法的线程被阻断进入等待状态。 tryLock():如果锁没被别的线程锁定,进入锁定状态,并返回true。若锁已被锁定,返回false,不进入等待状态。此方法还可带时间参数,如果锁在方法执行时已被锁定,线程将继续
15、等待规定的时间,若还不行才返回false。 unlock():取消锁定,需要注意的是Lock不会自动取消,编程时必须手动解锁。 (finally)Condition接口lock后需要在一定条件下才能做某些工作 await():使调用此方法的线程放弃锁定,进入睡眠直到被打断或被唤醒。 signal(): 唤醒一个等待的线程 signalAll():唤醒所有等待的线程,同步装置,Semaphore用来管理一个资源池的工具,Semaphore可以看成是个通行证,线程要想从资源池拿到资源必须先拿到通行证,Semaphore提供的通行证数量和资源池的大小一致。CountDownLatch 计数器awai
16、t():使调用此方法的线程阻断进入等待 countDown(): 倒计数,将计数值减1 getCount(): 得到当前的计数值CyclicBarrier类似于CountDownLatch也是个计数器,不同的是CyclicBarrier数的是调用了CyclicBarrier.await()进入等待的线程数,当线程数达到了CyclicBarrier初始时规定的数目时,所有进入等待状态的线程被唤醒并继续 Exchanger Exchanger让两个线程可以互换信息 exchange(Vx) 等待另一个线程到达此交换点(除非它被中断),然后将给定的对象传送给该线程,并接收该线程的对象。,Thank you,Neusoft Co., Ltd.,谢谢,