您的位置:首页 > 编程语言 > Java开发

java多线程基础

2015-11-02 13:29 232 查看

多线程实现

实现多线程,有两种手段,一种是继续Thread类,另外一种是实现Runable接口。

class 类名 extends Thread{
方法1;
…
@override
public void run(){
// other code…
}
属性1;
…
}

class 类名 implements Runnable{
方法1;
…
@override
public void run(){
// other code…
}
属性1;
…
}


实现Runnable接口比继承Thread类所具有的优势:

1):适合多个相同的程序代码的线程去处理同一个资源

2):可以避免java中的单继承的限制

3):增加程序的健壮性,代码可以被多个线程共享,代码和数据独立。

线程的状态





新建状态(New):当线程对象创建后,即进入了新建状态。仅仅由java虚拟机分配内存,并初始化。如:Thread t = new MyThread();
就绪状态(Runnable):当调用线程对象的start()方法,线程即进入就绪状态。java虚拟机创建方法调用栈和程序计数器,此线程做好准备等待CPU调度执行。
运行状态(Running):当CPU开始调度处于就绪状态的线程时,执行run()方法进入到运行状态。
阻塞状态(Blocked):处于运行状态中的线程由于某种原因,暂时放弃对CPU的使用权,停止执行,此时进入阻塞状态,直到其进入到就绪状态,才会再次被CPU调用以进入到运行状态。
死亡状态(Dead):线程run()方法执行完了或者因异常退出了run()方法,该线程结束生命周期。当主线程结束时,其他线程不受任何影响。

根据阻塞产生的原因不同,阻塞状态又可以分为三种:

等待阻塞 – 运行状态中的线程执行wait()方法,使本线程进入到等待阻塞状态,JVM会把该线程放入等待池中;
同步阻塞 – 线程在获取synchronized同步锁失败(因为锁被其它线程所占用),它会进入同步阻塞状态;
其他阻塞 – 通过调用线程的sleep()或join()或发出了I/O请求时,线程会进入到阻塞状态。当sleep()状态超时、join()等待线程终止或者超时、或者I/O处理完毕时,线程重新转入就绪状态。


线程方法

yield()
: 正在执行的线程暂停,让出cpu资源给其他的线程,直接进入就绪状态。

sleep()
:当前线程睡眠后会进入阻塞状态,睡眠结束转入就绪状态 。

join():把指定的线程加入到当前线程,可以将两个交替执行的线程合并为顺序执行的线程。非静态

setPriority():设置优先级,优先级高的线程获取CPU资源的概率较大,java提供了10个优先级别,0~10。

Thread类提供的三个优先级静态常量:

MAX_PRIORITY  =10

MIN_PRIORITY =1

NORM_PRIORITY =5

在默认情况下,main线程具有普通优先级

setDaemon():用户可以用Thread的setDaemon(true)方法设置当前线程为守护线程。

Java中有两类线程:User Thread(用户线程)、Daemon Thread(守护线程) 。用户线程即运行在前台的线程,而守护线程是运行在后台的线程。守护线程作用是为其他前台线程的运行提供便利服务,而且仅在普通、非守护线程仍然运行时才需要,比如垃圾回收线程就是一个守护线程。当VM检测仅剩一个守护线程,而用户线程都已经退出运行时,VM就会退出,因为没有如果没有了被守护这,也就没有继续运行程序的必要了。如果有非守护线程仍然存活,VM就不会退出。守护线程的特征:如果所有前台线程都死亡,后台线程会自动死亡。
守护线程并非只有虚拟机内部提供,用户在编写程序时也可以自己设置守护线程。用户可以用Thread的setDaemon(true)方法设置当前线程为守护线程。 虽然守护线程可能非常有用,但必须小心确保其他所有非守护线程消亡时,不会由于它的终止而产生任何危害。因为你不可能知道在所有的用户线程退出运行前,守护线程是否已经完成了预期的服务任务。一旦所有的用户线程退出了,虚拟机也就退出运行了。因此,不要在守护线程中执行业务逻辑操作(比如对数据的读写等)。

wait与sleep区别:

sleep静态方法,wait非静态 。
sleep方法没有释放锁,而wait方法释放了锁,使得其他线程可以使用同步控制块或者方法。
 wait,notify和notifyAll只能在同步控制方法或者同步控制块里面使用,而sleep可以在任何地方使用
sleep必须捕获异常,而wait,notify和notifyAll不需要捕获异常 

线程同步

1.同步代码块:

synchronized (obj) {
//...
}

2.同步方法:
public synchronized void method(){
// ....
}
3.同步锁:
class C {
//锁对象
private final ReentrantLock lock = newReentrantLock();
......
//保证线程安全方法
public void method() {
//上锁
lock.lock();
try {
//保证线程安全操作代码
} catch() {

} finally {
lock.unlock();//释放锁
}
}
}


三种方法都是执行 “加锁->访问->释放锁”操作,但是同步代码块和同步方法使用隐式的同步监视器,LOCK则是显示调用方法。

线程池

并发的线程数量很多,频繁创建和销毁线程就会消耗资源、降低系统的效率。

Java核心线程池ThreadPoolExecutor类:

public ThreadPoolExecutor(
int corePoolSize, //核心池大小
int maximumPoolSize, //线程池最大线程数
long keepAliveTime, //表示线程没有任务执行时最多保持多久时间会终止
TimeUnitunit, //参数keepAliveTime的时间单位
BlockingQueue<Runnable>workQueue, //阻塞队列
ThreadFactorythreadFactory, //线程工厂,主要用来创建线程
RejectedExecutionHandlerhandler);//表示当拒绝处理任务时的策略


1.默认情况下,在创建了线程池后,线程池中的线程数为0,当有任务来之后,就会创建一个线程去执行任务,当线程池中的线程数目达到corePoolSize后,就会把到达的任务放到缓存队列当中;

2.任务缓存队列已满,会尝试创建新的线程去执行任务,当线程池中的线程数目达maximumPoolSize,则会采取任务拒绝策略进行处理;

3.如果线程池中的线程数量大于 corePoolSize时,如果某线程空闲时间超过keepAliveTime,线程将被终止。

但是如果调用了allowCoreThreadTimeOut(boolean)方法,在线程池中的线程数不大于corePoolSize时,keepAliveTime参数也会起作用,直到线程池中的线程数为0;

参数:

缓存队列workQueue的类型为BlockingQueue<Runnable>,通常可以取下面三种类型:

  ArrayBlockingQueue:基于数组的先进先出队列,此队列创建时必须指定大小;
  LinkedBlockingQueue:基于链表的先进先出队列,如果创建时没有指定此队列大小,则默认为Integer.MAX_VALUE;
  synchronousQueue:这个队列比较特殊,它不会保存提交的任务,而是将直接新建一个线程来执行新来的任务。

当线程池的任务缓存队列已满并且线程池中的线程数目达到maximumPoolSize,如果还有任务到来就会采取任务拒绝策略,通常有以下四种策略:

ThreadPoolExecutor.AbortPolicy:丢弃任务并抛出RejectedExecutionException异常。
ThreadPoolExecutor.DiscardPolicy:也是丢弃任务,但是不抛出异常。
ThreadPoolExecutor.DiscardOldestPolicy:丢弃队列最前面的任务,然后重新尝试执行任务(重复此过程)
ThreadPoolExecutor.CallerRunsPolicy:由调用线程处理该任务

线程池初始化:

prestartCoreThread():初始化一个核心线程;

prestartAllCoreThreads():初始化所有核心线程

线程池关闭:
 ThreadPoolExecutor提供了两个方法,用于线程池的关闭,分别是shutdown()和shutdownNow(),其中:

 shutdown():不会立即终止线程池,而是要等所有任务缓存队列中的任务都执行完后才终止,但再也不会接受新的任务

shutdownNow():立即终止线程池,并尝试打断正在执行的任务,并且清空任务缓存队列,返回尚未执行的任务
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: