线程知识和线程池

来源:互联网 发布:轻淘客和淘宝联盟区别 编辑:程序博客网 时间:2024/05/17 22:50

一、线程
1.通过继承Therad类的子类,并重写该类的run()方法。该run()方法的方法体就代表了线程需要完成的任务,因此把run()方法称为线程执行体。创建Therad子类的实例,即创建了线程对象。调用线程对象的start()方法来启动线程。
public class FirstTherad extends Thread{private int i;//重写run()方法,run()方法的方法体就是线程执行体。public void run(){for(;i<100;i++){//当线程类继承Therad类时,直接使用this即可获取当前线程//Therad对象的getName()返回当前线程的名字//因此可以直接调用getName()方法返回当前线程的名字System.out.println(getName()+"  "+i);//这里的getName,使用了this.}}public static void main(String[] args){for(int i=0;i<100;i++){//调用Therad的currentTherad()方法获取当前线程System.out.println(currendTherad().getName()+" "+i);}}}
二、实现Runnable接口创建类
1.定义Runnable接口的实现类,并重写该接口的run()方法,该run()方法的方法体同样是该线程的线程执行体。
2.创建Runnable实现类的的实例,并以此实例作为Therad的target来创建Therad对象,该Therad的对象才是真正的线程对象。
public class FirstTherad implements Runnable{private int i;//重写run()方法,run()方法的方法体就是线程执行体。public void run(){for(;i<100;i++){//当线程类实现Runnable接口时//如果想获取当前,只能用Therad.currentTherad()方法,而不能用this。System.out.println(getName()+"  "+i);}}public static void main(String[] args){for(int i=0;i<100;i++){//调用Therad的currentTherad()方法获取当前线程System.out.println(currendTherad().getName()+" "+i);if(i==20){SecondTherad st=new SecondTherad();//通过new Therad(target,name)方法创建新线程new Therad(st,"新线程1").start();}}}}
三、线程的生命周期
新建(new)、就绪(Runnable)、运行(Running)、阻塞(Blocked)和死亡(Dead)
2.1新建和运行状态。当程序使用new关键字创建了一个线程之后,该线程就处于新建状态,当线程对象调用了start()方法之后,该线程处于就绪状态。注意启动线程使用start()方法,而不是run()方法。如果直接调用线程对象的run()方法,则run()方法立即就会被执行,而且在run()方法返回之前其他线程无法执行。如果直接调用线程对象的run()方法,则run()方法不能直接通过getName()方法来获得当前执行线程的名字,而是需要使用Therad.currentTherad()方法获得当前线程,在调用线程对象的getName()方法来获得线程的名字。
现在大多数系统采用抢占式调度策略。只有当一个线程调用了sleep()或yield()方法后才会放弃所占用的资源,但是会出现阻塞的状态。同时已经死亡了的线程不能再次调用。
控制线程 当某个线程调用了join()方法,调用线程将被阻塞,直到join()方法加入到join线程执行完成为止。也就是先执行这个线程的方法。


四、线程同步
线程同步。为啥会出现同步?因为线程的调度不确定性,可能导致程序不能完全执行,为了执行总个程序就要要到同步,使程序不出现错误。run()方法体本身并不具有同步安全性。为了解决这个问题,使用同步代码块synchronized(obj)){........}//同步代码块,意思就是线程开始执行同步代码块之前,必须获得对同步监视器的锁定。obj就是同步监视器。虽然java程序允许件事任何对象作为同步监视器,但是通常推荐使用可能被并发访问的共享资源充当同步监视器。同步方法 。使用synchronized关键字来修饰某个方法,则该方法称为同步方法。对于synchronized修饰的实例方法(非静态方法)而言,无需显示指定的同步监视器,同步方法的同步监视器是this,也就是调用该方法的对象。同步锁(Lock)比synchronized更强大ReentrantReadWriteLock为读写操作提供了三种锁模式:Writing、readingOptimistic、Reading。同时也叫封锁资源。
class X{//定义锁对象private final ReentrantReadWriteLock lock =new ReentrantReadWriteLock();//....//定义需要保证线程安全的方法public void m(){//加锁lock.lock();try{//需要保证线程安全的代码//...method body}finally{lock.unlock();//通常建议finally快来确保必要时释放锁。}}}
五、线程池

1.延迟线程池
实际开发中有时需要让指定的任务在特定的时延之后执行,使用这个线程池可以满足要求。同意,线程池可以通过调用Exectuors类的静态方法来创建。
public static ScheduledExcutorService newScheduledThreadPool(int corePoolSize):此方法创建一个线程池,可以以一定的延时执行指定的任务,参数corePoolSize用于支出线程池中的线程数量。
2.使用自定义线程池
前面的线程池虽然好使用,但是能够自定义的参数很少,如果有特殊的需要就很难满足需求。这时可以考虑使用TheradPoolExecutor类来实现自定义的线程池,其内置了一个线程队列(由BlockingQueue),在有新任务到点时,如何处理满足如下设置:
1.如果当前线程池中的数量比规定标准值少,则倾向于创建新线程。
2.如果当前线程池中的数量比规定标准值少多,则倾向于把新的任务请求放到队列中。如果队列已满,并且线程数量没有超过最大值,则创建新线程。
3.如果当前线程池中的线程数量达到最大值,而队列已满,则请求被拒绝。
4.如果空闲线程超过设定的存活时间,则空闲线程对象销毁。
TheradPoolExecutor类实现了ExecutorService接口,其对象也具有ExecutorService接口中所有功能。
public TheradPoolExecutor(int corePoolSize,int maximumPoolsize,long keepAliveTime,TimeUnitunit,BlockingQueue<Rnnable>workQueue):
corePoolSize参数给出线程池的尺寸的标准值,maximumPoolsize参数给出线程池的最大尺寸,keepAliveTime参数指定空闲线程的存活时间,unit参数指定时间单位,workQueue
参数指定的是工作等待队列。查找api。


3.有返回值的线程调用
实际开发中有时这样的需要,希望调用的线程不但能执行指定的任务,最好能够像方法那样调用有返回值,这就需要使用Callable与Future接口。
1.Callable接口位于util.concurrent包中。实现Callable接口需要实现call方法。call签名:
public Object call()throws Exception在实现call方法时可将任何类型的数据作为返回值,不论返回值是什么类型的,系统都会当作Object类型来处理。读者可以通过泛型技术为call方法指定为特定的返回类型。
2.Future接口
public Future submit(Callable task)  
此方法的入口参数为Callable接口类型的引用,其指向实现了Callable接口类的对象。
此方法的返回值Future接口类型引用,其指向封装了任务状态与结果信息的对象。//方法查找API。
import java.util.concurrent.*;class MyTask implements Callable{//任务名称private String tname;public MyTask(String tname){this.tname=tname;}//存放任务代码的方法public void call(){System.out.println("\n=========任务"+tname+"任务执行成功========");//返回任务的返回值return "恭喜你,任务成功执行,我是返回消息";}}//主类public class Sample{public static void main(String[] args)throws Exception{//创建尺寸为2的固定线程池ExcetorService theradPool=Exceutors.newSingleTheradExceutor();//创建线程池的submit方法执行任务Future future=theradPool.submit(new MyTask("TaskA"))try{//调用阻塞方法get获取任务的返回值System.out.println(future.get());}catch(Exception ex){//捕获并处理可能抛出的异常ex.printStackTrace();}//关闭线程池,结束程序。theradPool.shutdown();}}
六、BlockingQueue 阻塞队列
包括:ArrayBlockingQueue以数组实现,LinkedBlockingQueue以链表实现,PriorityBlockingQueue以优先级阻塞队列实现.
阻塞队列是指队列的尺寸不能超过指定的大小。如果队列已经满了,则试图向队列中添加元素的线程被阻塞等待,直到队列中有空位置为止。查 看API看方法。
BlockingQueue bq=new ArrayBlockingQueue(10);创建阻塞队列,尺寸为10

import java.util.*;//自定义元素类class MyElement implements Comparable{//表示优先级的成员int priority;//表示元素内容的成员String content;//有参构造函数public MyElement(int priority,String content){this.priority=priority;this.content=content;}//实现确定自然顺序的compareTo方法public int compareTo(Object o){//按优先级从小到大排序return this.priority-((MyElement)o).priority;}//重写toString方法public String toString(){return "[优先级:"+priority+",内容“"+content+"”]";}}//自定义比较器class Mycompartor implements Comparator{//实现确定顺序的compare方法public int compare(Object o1,Object o2){//按优先级从大到小排序return ((MyElement)o2).priority-((MyElement)o1).priority;}}//主类public static void Sample{//访问指定队列所有元素的方法public static void scanQueue(Queue queue){Object o=queue.poll();while(o!=null){System.out.print(o);o=queue.poll();System.out.println();}}//主方法public static void main(String[] args){//创建SortedSet集合SortedSet ss=new TreeSet();//向集合中添加10个元素for(int i=0;i<10;i++){ss.add(new MyElement((int)Math.round(Math.roundom()*100),"第"+i+"个"));}//将SortedSet集合中的元素添加到优先级队列中         Queue pq1=new PriorityQueue(ss); //对第一个优先级队列访问 System.out.println("==================对第一个优先级队列访问========================"); scanQueue(pq1); //创建优先级队列队形,并指定比较器 Queue pq2=new priorityQueue(10,new Mycompartor()); //向优先级队列中添加10个元素for(int i=0;i<10;i++){pq2.offer(new MyElement((int)Math.round(Math.roundom()*100),"第"+i+"个"));}//对第二个优先级队列访问 System.out.println("==================对第一个优先级队列访问========================"); scanQueue(pq2);}}//可以看出,在没有优先级队列指定比较器的 情况下,元素按自然顺序输出,反之按比较器指定顺序输出。








0 0