线程

多线程:

  进程:

    进程指正在运行的程序,当一个程序进入内存运行,即变成一个进程,进程是处于运行过程中的程序,并且具有一定独立功能。

  线程:

    线程是进程中的一个执行单元,负责当前进程中程序的执行,一个进程中至少有一个线程。一个进程中是可以有多个线程的,这个应用程序也可以称之为多线程程序。

  总结:一个程序运行后至少有一个进程,一个进程中可以包含多个线程

      单线程:

          一个进程只有一个线程。

      多线程:

          一个进程有多个线程。

  程序运行原理解析:

       分时调度

      所有线程轮流使用 CPU 的使用权,平均分配每个线程占用 CPU 的时间。

                      抢占式调度

         优先让优先级高的线程使用 CPU,如果线程的优先级相同,那么会随机选择一个(线程随机性),Java使用的为抢占式调度。

 

       

      线程在内存里的体现,有几条线程,有几个栈(在java里)

      主线程就是主方法。

Thread类:

    

 

     

 

     

 

     创建线程的步骤:

        第一种方式:

          1 定义一个类继承Thread。

          2 重写run方法。

          3 创建子类对象,就是创建线程对象。

          4 调用start方法,开启线程并让线程执行,同时还会告诉jvm去调用run方法。

     代码体现:

           

public class MyThread extends Thread{
//重写run方法描述线程任务(先定义一个类继承Thread类)
public void run() {
    //int y=1/0;
    //Exception in thread "Thread-0" java.lang.ArithmeticException: / by zero
    for(int i=0;i<100;i++){
        System.out.println(Thread.currentThread().getName()+":"+i);
    }
}
}
public class Demo02 {

    public static void main(String[] args) {
        
        //线程在内存里的体现,有几条线程,有几个栈(在java里)
        //创建线程对象
        MyThread my=new MyThread();
        //开启线程
        my.start();
        //描述主线程的任务
        //currentThread返回(哪个程序正在执行)的Thread对象,()
        for(int i=0;i<100;i++){
            System.out.println(Thread.currentThread().getName()+":"+i);
        }
    }

}

      获取线程名称:

            

 

            总结:线程对象调用run方法不开启线程。仅是对象调用方法。线程对象调用start开启线程,并让jvm调用run方法在开启的线程中执行。

Runnable接口:

      第二种方式:

          

 

           

 

           

 

           

        创建线程的步骤。

        1、定义类实现Runnable接口。

        2、覆盖接口中的run方法。。

        3、创建Thread类的对象

        4、将Runnable接口的子类对象作为参数传递给Thread类的构造函数。

        5、调用Thread类的start方法开启线程。

        代码:

            

public class MyRunnable implements Runnable{
//第二种方式创建多线程
    //先实现Runnable接口
    public void run() {
        //描述线程任务
        for(int i=0;i<100;i++){
            System.out.println(Thread.currentThread().getName()+":"+i);
        }
    }
    //避免了单继承的缺陷,实现了更多的可能性
    //没有将线程任务与线程对象绑在一起
}
public class Demo03 {

    public static void main(String[] args) {
        // 创建线程任务对象
        MyRunnable my = new MyRunnable();
        // 创建线程对象,并将线程任务交给线程对象
        Thread t = new Thread(my);
        // 开始线程,
        t.start();
        // 描述主线程任务
        for (int i = 0; i < 100; i++) {
            // 先获得当前任务对象,调用方法获得主线程的名字
            System.out.println(Thread.currentThread().getName() + ":" + i);
        }
    }

}

       好处:

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

匿名内部类的使用:

   使用匿名内部类的方式实现Thread类。     

public class Demo04 {
public static void main(String[] args) {
    //匿名内部类
        //new  父类/接口(){}
        //方法重写
        //1:继承Thread类的方式
        Thread t=new Thread(){
            public void run() {
                for(int i=0;i<100;i++){
                    System.out.println(Thread.currentThread().getName()+":"+i);
                }
            }
            
        };
        //开启线程
        t.start();
}
    
}

    使用匿名内部类的方式实现Runnable接口。

public class Lianxi01 {

    public static void main(String[] args) {
    //创建线程任务对象
        //创建匿名内部类对象
        Runnable r=new Runnable(){
            
            public void run() {
                //重写run方法
                for(int i=0;i<100;i++){
                    //获取线程名
                    System.out.println(Thread.currentThread().getName()+":"+i);
                }
            }
        };
        //创建线程对象,并将线程任务交给线程对象
        Thread t=new Thread(r);
        //开始线程
        t.start();
        //描述主线程任务
        for(int i=0;i<100;i++){
            System.out.println(Thread.currentThread().getName()+":"+i);
        }
    }

}

 线程池:

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

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

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

    原理:

      

  可以将线程对象存到ArrayList集合中,泛型是<Thread>,通过add方法存到集合中,再通过remove方法取出来,在用add方法加进去。

使用线程池方式1:

    Runnable接口:

          Executors:线程池创建工厂类。public static ExecutorService newFixedThreadPool(int   nThreads):返回线程池对象,int 表示的几条线程。

          ExecutorService:线程池类  Future<泛型>    submit(Runnable task):获取线程池的某一个对象,并执行。

          Future接口:用来记录线程任务执行完毕后产生的结果。

   使用线程池中线程对象的步骤:

               创建线程池对象

                                                   创建Runnable接口子类对象

                                                   提交Runnable接口子类对象

                                                   关闭线程池

   代码:

public class MyRunnable implements Runnable{
//第二种方式创建多线程
    //先实现Runnable接口
    public void run() {
        //描述线程任务
        for(int i=0;i<100;i++){
            System.out.println(Thread.currentThread().getName()+":"+i);
        }
    }
    //避免了单继承的缺陷,实现了更多的可能性
    //没有将线程任务与线程对象绑在一起
}
public class Demo01 {

    public static void main(String[] args) {
        //1:获取线程池对象
        ExecutorService es=Executors.newFixedThreadPool(2);
        //创建线程任务对象
        MyRunnable r=new MyRunnable();
        //由线程池随机选一条空闲线程取执行线程任务
        es.submit(r);
        es.submit(r);
        es.submit(r);
        //销毁线程池(不推荐使用)
        es.shutdown();
    }

}

第二种方式:

    Callable接口:

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

                      ExecutorService:线程池类

                      <T> Future<T> submit(Callable<T> task):获取线程池中的某一个线程对象,并执行线程中的call()方法

      Future接口:用来记录线程任务执行完毕后产生的结果。线程池创建与使用

        使用线程池中线程对象的步骤:

        创建线程池对象

        创建Callable接口子类对象

        提交Callable接口子类对象

        关闭线程池

    代码:

      

public class MyCallable implements Callable<String>{

public String call() throws Exception {
    
    return "abc";
}

}
public class Demo02 {

    public static void main(String[] args) throws InterruptedException, ExecutionException {
        //获取线程池对象,
        ExecutorService es=Executors.newFixedThreadPool(5);
        //创建线程任务
        MyCallable r=new MyCallable();
        //提交线程任务
        Future<String> f=es.submit(r);
        //获取返回值
        String s=f.get();
        System.out.println(s);
        //关闭线程池
        es.shutdown();
    }

}

线程练习:

    

public class GetSum implements Callable<Integer>{
private int n;
public GetSum(){
    
}
public GetSum(int n){
    this.n=n;
}
public Integer call() throws Exception {
    int sum=0;
    for(int i=1;i<=n;i++){
        sum+=i;
    }
    return sum;
}
}
public class Demo03 {

    public static void main(String[] args) throws InterruptedException, ExecutionException {
        //分别用两条线程计算1——n的和,一条计算1-100的和,一条计算1-200的和
        //获取线程池对象
        ExecutorService es=Executors.newFixedThreadPool(2);
        //创建线程池任务
        GetSum g1=new GetSum(100);
        GetSum g2=new GetSum(200);
        //提交任务
        Future<Integer> f1=es.submit(g1);
        Future<Integer> f2=es.submit(g2);
        //获取返回值
        Integer sum1=f1.get();
        Integer sum2=f2.get();
        System.out.println(sum1);
        System.out.println(sum2);
        //销毁线程池
        es.shutdown();
    }

}

 

posted @ 2020-11-04 10:56  马雪峰1  阅读(97)  评论(0编辑  收藏  举报