并发编程

来源:互联网 发布:java用到哪些函数 编辑:程序博客网 时间:2024/05/18 14:11

并发性是指同一CUP轮流执行多个任务,而并行性是指多个CUP分别执行多个任务;

main方法里的内容就是主线程执行体;

java中用Thread来表示线程类,该子类就是线程;

当一个线程继承自Thread时也就意味着它不能再继承其他的类,java是单继承的;

使用继承接口Runnable时,只是表明该类具有跑线程的能力了,但不代表它就是线程了,可以用它的能力来创建线程,只要把它当作new Thread()的Target就可以了;

用继承Runnable的方法创建线程,因为一个具有跑线程能力的类可以用作很多线程的Target,所以它只需要创建一个实例就行了,用这个实例去创建多个线程,这种方法创建的多线程共享这个实例的属性资源;

如果是用继承自Thread的方法创建的线程类,在类内时,调用Thread的方法时用this.XXX就行了,因为本身就是一个线程类,而继承自Runnable则不行,因为本身不是一个线程类,只是一个可以当作Target的类而已;

线程的生命周期,新建、就绪、执行、死亡、阻塞,new Thread()只是进入新建,start()只是进入就绪,执行由调度决定,死亡由执行结束和异常决定,进入阻塞的原因:IO阻塞,sleep(),等待通知,等待同步锁,suspend挂起;

控制线程的工具一:join线程就是让使用线程的程序等待使用了join方法的线程执行完之后再继续执行;

控制线程的工具二:后台线程,用来为其他线程做服务的,它会在所有前台线程死亡后死亡,设置的方法是在调用对象的start()方法前,调用setDeamon(true)来设置为后台线程;

控制线程的工具三:sleep让线程进行睡眠,进入阻塞状态,传入的参数为毫秒,一秒等于一千毫秒,主线程暂停直接使用Thread.sleep();

控制线程的工具四:yield让步线程,让调用的对象直接进入就绪状态,然后按其他线程与自身线程的优先级选择优先级高的线程来获得执行机会;

yield与sleep相比,yield的执行不抛出异常,sleep会抛出异常,yield是让线程进入就绪,sleep是让线程进入阻塞,直到休眠时间结束才进入就绪,sleep让出执行机会后,不考虑优先级,yield考虑优先级;

控制线程工具五:setPriority和 getPriority,一共有10个级别,但是由于不同的操作系统级别设定不同,所以一般用MIN_PRIORITY,  MAX_PRIORITY,NORM_PRIORITY来代表1,6,10,启动后也可以设置优先级;


同步问题就是对于一个资源,同时有2个或以上的线程在访问它,修改它,造成资源发生错误;

解决同步问题的方法一:同步代码块,在线程里定义代码块,synchronized(同步监视器,一般是贡献资源);

解决同步问题的方法二:同步方法,在资源的内容中把会造成冲突的方法定义为同步方法,即加关键字synchronized,则在同一时刻,该类的对象只有一个可以使用这个方法;

解决同步问题的方法三:lock锁,先获得lock锁的对象(常用的锁ReentrantLock),然后在需要非共享的资源代码区前加上lock.lock()来加锁,最后用finally块来lock.unlock()来解锁;

这三种方法,一个比一个的精细范围,从线程到方法,从方法到方法内部;


线程的通信方式一:使用Object类带有的wait(),notify(),notifyAll()来实现合作,必须用监视器来调用这3个方法,对于同步方法,可以直接用,但是对于同步代码块,必须要用关键字后面的指定资源作为同步监视器来调用;

线程的通信方式二:使用lock类绑定的Condition类的实例来通信,有方法await(),signal(),signalAll(),用锁的对象.newCondition()来创建Condition的实例,然后用这个实例来使用3个方法,也就是用Condition来代替监视器了;


每个线程都有自己所属的线程组,在创建实例时,在Thread()里,可以指定线程组,线程名称,Target,用ThreadGroup来创建线程组,可以指定线程组的名称和父类线程组;可以对线程组里的线程做统一的操作,比如用线程组的实例.interrupt();

线程的运行过程可能会抛出异常,这种异常为未处理异常,此时需要用异常处理器来处理这些异常,而异常处理器是需要自己制作的,继承自Thread.UncaughtExceptionHandler接口,实现里面的UncaughtException方法去处理异常,这样就设置好了处理器,然后对于指定的线程,使用线程实例.set(Default)UncaughtExceptionHandler(创建好的处理器的实例)来设置线程的处理器,也可以在线程类里,用staticsetDefaultUncaughtExceptionHandler(异常类型)来为该线程类的所有线程实例设置默认异常处理器;

Runnable的加强版Callable,Callable允许声明抛出异常以及线程执行完的返回值,但是Callable的实例并不能作为Thread的Target,这样就意味着它不能创建线程,所以引入FutureTask来封装Callable的实例,这个FutureTask实例可以成为Target,封装的方法就是new FutureTask(Callable实例),再把FutureTask的实例作为Target传进去来创建Thread的实例;


线程池,就是一个已经存在许多空闲线程的集合,只需把Runnable或者Callable的实例放进去就会自动的执行任务;

线程池的类型有很多,有的可以带延迟,即在指定的时间后开始执行任务,不带延迟的线程池的实例是ExecutorService类型的,带延迟的线程池的实例是ScheduledExecutorService类型的;

ExecutorService类型的使用方法是实例.submit(任务),ScheduledExecutorService类型的使用方法是实例.schedule(任务,指定时间),或实例.scheduleAtFixedRate(任务,指定时间,指定重复间隔),或实例.scheduleWithFixedDelay(任务,指定时间,指定任务间隔时间);

比lock解决同步问题更小范围的方法是ThreadLocal<T>类,范围是变量,使用该关键字修饰的变量解决同步问题的方式是复制该变量的副本,使得每一个线程都有自己的这个变量,进而互不干扰,面向的是不同的方向,比如名称等必须各自有,各自不同的变量;

可以使用collection的方法把线程不安全的集合封装成线程安全的集合,用法是把创建的集合实例传到collection的方法里,返回一个线程安全集合,方法的名称多为synchroniazed+集合名称;

java也支持一些线程安全的集合,比如ConcurrentHashMap和ConcurrentLinkedQueue。

原创粉丝点击