• Java——线程的创建,线程池


    线程

    多线程就是一个程序中有多个线程在同时执行。

    多线程下CPU的工作原理

    实际上,CPU(中央处理器)使用抢占式调度模式在多个线程间进行着高速的切换。对于CPU的一个核而言,某个时刻,只能执行一个线程,而CPU的在多个线程间切换速度相对我们的感觉要快,看上去就是在同一时刻运行。

    其实,多线程程序并不能提高程序的运行速度,但能够提高程序运行效率,CPU的使用率更高

    一、创建线程

    方法1:继承Thread类,重写run方法

    public class SubThread extends Thread{
    
        public SubThread(){
            super("x5456");     //通过构造方法修改线程名
        }
        
        public void run() {
            for(int i=0;i<100;i++){
                System.out.println(super.getName()+i);
            }
        }
    }

     调用:

    public static void main(String[] args) {
        //创建刚刚继承Thread类的子类的对象
        SubThread st = new SubThread();
        //通过setName方法,修改线程名
        st.setName("x54256");
        //调用对象的start方法,会自动执行我们重写的run方法
        st.start();
    
    
        for(int i=0;i<100;i++) {
            System.out.println(Thread.currentThread().getName()+i);     //获取当前线程的对象,调用getname()方法
        }
    }
    

    方法2:实现接口Runnable,重写run方法

    public class SubRunnable implements Runnable{
        public void run(){
            for(int i=0;i<100;i++){
                try {
                    // 调用Thread类的sleep方法,休眠50ms,由于父接口没有throws异常,so我们只能用try...catch
                    Thread.sleep(50);  
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(Thread.currentThread().getName()+"..."+i);
            }
        }
    }
    

    调用:

    public static void main(String[] args) {
        //创建实现Runnable接口的类的对象
        SubRunnable sr = new SubRunnable();
        //创建Thread类的对象
        Thread t = new Thread(sr);
        //启动线程
        t.start();
    
        for(int i=0;i<100;i++){
            System.out.println(Thread.currentThread().getName()+"..."+i);
        }
    }
    

    方法3:使用匿名内部类,实现多线程程序

    匿名内部类的前提:继承或者接口实现

    使用方法:

    new 父类或者接口(){
      重写抽象方法
    }

    public static void main(String[] args) {
    	//继承方式  XXX extends Thread{ public void run(){}}
    	new Thread(){
    		public void run(){
    			System.out.println("!!!");
    		}
    	}.start();
    	
    	//实现接口方式  XXX implements Runnable{ public void run(){}}
    	
    	Runnable r = new Runnable(){
    		public void run(){
    			System.out.println("###");
    		}
    	};
    	new Thread(r).start();
    	
    	//==================或=====================
    	new Thread(new Runnable(){
    		public void run(){
    			System.out.println("@@@");
    		}
    	}).start();
    	
    } 

    实现接口的好处:

    高内聚,低耦合:模块内能做的事就自己做,模块间的关系要尽量的小

    第二种方式实现Runnable接口避免了单继承的局限性,所以较为常用。实现Runnable接口的方式,更加的符合面向对象,线程分为两部分,一部分线程对象,一部分线程任务。继承Thread类,线程对象和线程任务耦合在一起。一旦创建Thread类的子类对象,既是线程对象,有又有线程任务。实现runnable接口,将线程任务单独分离出来封装成对象,类型就是Runnable接口类型。Runnable接口对线程对象和线程任务进行解耦。 

    多线程的内存图解:

    线程的一生: 

    二、线程池

    线程池,其实就是一个容纳多个线程的容器,其中的线程可以反复使用,省去了频繁创建线程对象的操作,无需反复创建线程而消耗过多资源。

    java中,如果每个请求到达就创建一个新线程,开销是相当大的。在实际使用中,创建和销毁线程花费的时间和消耗的系统资源都相当大,甚至可能要比在处理实际的用户请求的时间和资源要多的多。除了创建和销毁线程的开销之外,活动的线程也需要消耗系统资源。如果在一个jvm里创建太多的线程,可能会使系统由于过度消耗内存或“切换过度”而导致系统资源不足。为了防止资源不足,需要采取一些办法来限制任何给定时刻处理的请求数目,尽可能减少创建和销毁线程的次数,特别是一些资源耗费比较大的线程的创建和销毁,尽量利用已有对象来进行服务。

    线程池主要用来解决线程生命周期开销问题和资源不足问题。通过对多个任务重复使用线程,线程创建的开销就被分摊到了多个任务上了,而且由于在请求到达时线程已经存在,所以消除了线程创建所带来的延迟。这样,就可以立即为请求服务,使用应用程序响应更快。另外,通过适当的调整线程中的线程数目可以防止出现资源不足的情况。

    方法1:使用线程池方式--Runnable接口

    public static void main(String[] args) {
        //调用工厂类的静态方法,创建线程池对象(ExecutorService接口的实现类)
        //返回线程池对象,是返回的接口
        ExecutorService es = Executors.newFixedThreadPool(2);  //池内有2个线程
        //调用接口实现类对象es中的方法submit提交线程任务
        //将Runnable接口实现类对象,传递
        es.submit(new SubRunnable());
        es.submit(new SubRunnable());
        es.submit(new SubRunnable());
        es.submit(new SubRunnable());
    }
    

    实现的Runnable接口

    public class ThreadPoolRunnable implements Runnable {
    	public void run(){
    		System.out.println(Thread.currentThread().getName()+" 线程提交任务");
    	}
    }

    方法2:使用线程池方式Callable接口

    之前的实现方法,线程运行完没有返回值,而且不能抛异常。

    Callable接口:与Runnable接口功能相似,用来指定线程的任务。其中的call()方法,用来返回线程任务执行完毕后的结果,call方法可抛出异常。

    public static void main(String[] args) throws ExecutionException, InterruptedException {
        ExecutorService es = Executors.newFixedThreadPool(2);
        //提交线程任务的方法submit方法返回 Future接口的实现类
        Future<Integer> f = es.submit(new SubCallable());
        //获取返回值
        Integer i = f.get();
        System.out.println(i);
    }

    实现的Callable接口

    public class SubCallable implements Callable<Integer>{
        @Override
        public Integer call() {
            return 123;
        }
    }
    

    使用线程实现异步计算

    private int a;
    //通过构造方法传参
    public GetSumCallable(int a){
    	this.a=a;
    }
    
    public Integer call(){
    	int sum = 0 ;
    	for(int i = 1 ; i <=a ; i++){
    		sum = sum + i ;
    	}
    	return sum;
    }
    

    ThreadPoolDemo.java

    /*
     * 使用多线程技术,求和
     * 两个线程,1个线程计算1+100,另一个线程计算1+200的和
     * 多线程的异步计算
     */
    public class ThreadPoolDemo {
    	public static void main(String[] args)throws Exception {
    		ExecutorService es = Executors.newFixedThreadPool(2);
    		Future<Integer> f1 =es.submit(new GetSumCallable(100));
    		Future<Integer> f2 =es.submit(new GetSumCallable(200));
    		System.out.println(f1.get());
    		System.out.println(f2.get());
    		es.shutdown();
    	}
    }
    

    FutureTask的使用

    FutureTask继承了Callable和Future接口,所以它既能像callable一样被Thread执行,也能像Future那样获取结果。

        @Override
        public boolean testServiceUrl(ServiceUrlTestDTO url){
            // 调用地图服务
            Callable<Boolean> callable = new WebServiceUtil(url.getUrl(),url.getProxy(),url.getToken());
            FutureTask<Boolean> futureTask = new FutureTask<>(callable);
            Thread thread = new Thread(futureTask);
            thread.start();
            try {
                Boolean isOK = futureTask.get();
                System.out.println("服务【"+ url.getUrl() +"】测试:"+ isOK);
                return isOK;
            } catch (InterruptedException e) {
                e.printStackTrace();
            } catch (ExecutionException e) {
                e.printStackTrace();
            }
            return false;
        }
    

      

  • 相关阅读:
    linux下删除修改时间为某天之前的文件
    SPRING-MVC访问静态文件,如jpg,js,css
    [转]Kafka/Metaq设计思想学习笔记
    C++类型转换总结
    蓄水池抽样
    Cracking the coding interview--Q2.4
    Cracking the coding interview--Q2.3
    Cracking the coding interview--Q2.2
    魅蓝note2在ubuntu14.04下mtp模式无法自动mount的解决方法
    让reddit/r/programming炸锅的一个帖子,还是挺有意思的
  • 原文地址:https://www.cnblogs.com/x54256/p/8443794.html
Copyright © 2020-2023  润新知