zoukankan      html  css  js  c++  java
  • Java学习线程

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

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

    单线程程序:若有多个任务,只能依次执行。当上一个任务执行完毕后,才能执行下一个任务。

    多线程程序:若有多个任务可以同时执行。

    package com.oracle.demo01;
    
    public class Demo01 {
    	/*普通main方法的执行就叫单线程
    	 * 单线程:只有一条路,后面的代码必须等着前面的代码执行完才能执行
    	 * main的名字就叫主线程 "main"
    	 * 
    	 * 在dos窗口,执行java.demo01
    	 * 启动JVM,运行demo01中的main方法
    	 * JVM运行main方法的时候,去找操作系统,开线程,开一条路径
    	 * 对于CPU来说,就有了一条执行路径
    	 * 
    	 * */
         public static void main(String[] args) {
    		function();
    		System.out.println(Math.abs(-9));
    	}
         public static void function(){
        	 for(int i=0;i<10000;i++){
        		 System.out.println(i);
        	 }
         }
    }
    

    Thread类

    构造方法:

    常用方法:

    Runnable接口

    常用方法:

    Thread类构造方法:

    自定义线程的两种方式

    1、将类声明为Thread类的子类,该子类应重写Thread类的run方法。

          创建对象,开启线程。run方法相当于其他线程的main方法。

    2、声明一个实现Runnable接口的类,该类实现run方法。

       然后创建Runnable的子类对象,传入到某个线程的构造方法中开启线程

    第一种方式:

    1、 定义一个类继承Thread。

    2 、重写run方法。

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

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

    package com.oracle.demo01;
    
    public class MyThread extends Thread {
    	// 构造方法
    	public MyThread() {
    	}
    
    	public MyThread(String name) {
    		super(name);
    	}
    
    	@Override
    	public void run() {
    		Thread t = Thread.currentThread();
    		String name = t.getName();// 获取当前正在执行的线程的名称
    		for (int i = 0; i < 5; i++) {
    			System.out.println(name + "正在运行" + i);
    			try {
    				t.sleep(100);
    			} catch (InterruptedException e) {
    				// TODO Auto-generated catch block
    				e.printStackTrace();
    			}
    		}
    	}
    }
    package com.oracle.demo01;
    
    public class Demo02 {
    	public static void main(String[] args) {
    		//创建子类对象  就是创建线程对象
    		MyThread mt = new MyThread();
    	    //启动线程(隐含运行run方法)
    		mt.start();
    		
    		MyThread mt1 = new MyThread("小猪佩奇");
    		//mt1.setName("小猪佩奇");  //设置自定义线程名称,但是主线程main不能改名称
    		mt1.start();
    	    for(int i=0;i<5;i++){
    		    System.out.println("main方法"+i);
    	    }
    	}
    }
    

    线程对象调用 run方法和调用start方法区别

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

    继承Thread类的原理

    Thread t1 = new Thread();

    t1.start();//这样做没有错,但是该start调用的是Thread类中的run方法,而这个run方法没有做什么事情,更重要的是这个run方法中并没有定义我们需要让线程执行的代码。

    创建线程的目的:

    是为了建立程序单独的执行路径,让多部分代码实现同时执行。也就是说线程创建并执行需要给定线程要执行的任务。

    对于之前所讲的主线程,它的任务定义在main函数中。自定义线程需要执行的任务都定义在run方法中。

    Threadrun方法中的任务并不是我们所需要的,只有重写这个run方法。既然Thread类已经定义了线程任务的编写位置(run方法),那么只要在编写位置(run方法)中定义任务代码即可。所以进行了重写run方法动作。

    第二种方式:

    1、定义类实现Runnable接口。

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

    3、创建Thread类的对象

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

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

    package com.oracle.demo01;
    
    public class RunnableDemo implements Runnable{
    
    	@Override
    	public void run() {
    		for(int i=0;i<5;i++){
    			System.out.println("线程正在执行"+i);
    		}	
    	}
    }
    
    package com.oracle.demo01;
    
    public class Test {
    	public static void main(String[] args) {
            //创建Runnable的子类对象
    	RunnableDemo rd=new RunnableDemo();
    	//创建Thread类对象,并将Runnable的子类对象作为参数传递给Thread类的构造方法
             Thread t=new Thread(rd);
             t.start();
             for(int i=0;i<5;i++){
                 System.out.println("主方法正在执行"+i);
             }
    	}
    }
    

    实现Runnable的原理 

           实现Runnable接口避免了继承Thread类的单继承局限性。覆盖Runnable接口中的run方法,将线程任务代码定义到run方法中。

        创建Thread类的对象只有创建Thread类的对象才可以创建线程。线程任务已被封装到Runnable接口的run方法中,而这个run方法所属于Runnable接口的子类对象,所以将这个子类对象作为参数传递给Thread的构造函数,这样,线程对象创建时就可以明确要运行的线程的任务。

    实现Runnable的好处

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

    获取线程名称

    Thread.currentThread()获取当前线程对象

    Thread.currentThread().getName();获取当前线程对象的名称

    线程的匿名内部类

    package com.oracle.demo01;
    /*
     * 匿名内部类格式:
     * new 父类或接口{
     *    重写后的抽象方法
     * }.方法
     * new Thread(){
     *    public void run(){
     *    
     *    }
     * }.start();
     * */
    public class Demo03 {
        public static void main(String[] args) {
    		new Thread(){
    			public void run(){
    				System.out.println(super.getName()+"正在运行");
    			}
    		}.start();
    		
    		new Thread(new Runnable(){
    			public void run(){
    				System.out.println("Runnable正在运行");
    			}
    		}).start();
    	}
    }
    

    线程的生命周期

    线程最初被创建是新建状态,通过调用start()方法到达运行状态Runnable,当run()方法结束后,线程就到了死亡状态terminated
    如果在调用start()方法时,CPU没有时间或者遇到同步锁,则线程达到受阻塞状态。当CPU有时间了,线程就会从受阻塞状态到达运行状态
    最后到达死亡状态。
    若在运行状态,别的线程抢走了CPU资源,则线程会处于受阻塞状态;若是调用了sleep()方法,线程就会处于休眠状态,当CPU有空了,线程会重新处于运行状态;
    若是调用了wait()方法,则线程处于等待状态,当调用notify()方法时或者CPU有时间了,线程会从等待状态转为运行状态。
    当线程处于休眠状态或者等待状态时,若是CPU没有时间,则线程都会处于受阻塞状态。
    注:在受阻塞状态:线程具有cpu执行的资格,cpu有空就执行
    在休眠状态或者等待状态,线程放弃CPU的执行资格,CPU有空了线程也不执行。

    线程池

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

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

    使用线程池的方式—Runnable接口

    线程池都是通过线程池工厂创建,再调用线程池中的方法获取线程,再通过线程去执行任务方法。

    Executors:线程池创建工厂类

     public static ExecutorService newFixedThreadPool(int nThreads):返回线程池对象

     ExecutorService:线程池类

     Future<?> submit(Runnable task):获取线程池中的某一个线程对象,并执行

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

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

    1、创建线程池对象

    2、创建Runnable接口子类对象

    3、提交Runnable接口子类对象

    4、关闭线程池

    package com.oracle.demo02;
    
    public class RunnableImp implements Runnable{
    
    	@Override
    	public void run() {
    		// TODO Auto-generated method stub
    		for(int i=0;i<5;i++){
    		   System.out.println(Thread.currentThread().getName()+"线程"+i);
    		}
    	}
    
    }
    package com.oracle.demo02;
    
    import java.util.concurrent.ExecutorService;
    import java.util.concurrent.Executors;
    
    public class Test {
    	public static void main(String[] args) {
    		//创建一个线程池对象,通过线程池工厂类调用newFixedThreadPool()
    		//创建了盛有两个线程的线程池对象
    		ExecutorService es = Executors.newFixedThreadPool(5);
    		//获取线程池中某一线程对象,并执行
    		es.submit(new RunnableImp());
    		es.submit(new RunnableImp());
    		es.submit(new RunnableImp());
                    //关闭线程池
    		es.shutdown();
    	}
    }
    

    使用线程池方式-Callable接口

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

     ExecutorService:线程池类

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

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

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

    1、创建线程池对象

    2、创建Callable接口子类对象

    3、提交Callable接口子类对象

    4、关闭线程池

    package com.oracle.demo02;
    
    import java.util.concurrent.Callable;
    
    public class CallableImp implements Callable<String>{
    
    	@Override
    	public String call() throws Exception {
    		// TODO Auto-generated method stub
    		return "asd";
    	}
    
    }
    package com.oracle.demo02;
    
    import java.util.concurrent.ExecutionException;
    import java.util.concurrent.ExecutorService;
    import java.util.concurrent.Executors;
    import java.util.concurrent.Future;
    
    public class Test2 {
    	public static void main(String[] args) throws InterruptedException, ExecutionException {
    		// 创建线程池对象
    		ExecutorService es = Executors.newFixedThreadPool(2);
    		//获取线程池中的某一个线程对象,并执行线程中的call()方法
    		//Future接口:用来记录线程任务执行完毕后产生的结果。线程池创建与使用
    		Future<String> f = es.submit(new CallableImp());
                    System.out.println(f.get());
    	}
    }
    

    练习:计算1-100和1-200的总和

    package com.oracle.demo02;
    
    import java.util.concurrent.Callable;
    
    public class CallableDemo implements Callable<Integer> {
    	private int x;
    
    	public CallableDemo(int x) {
    		this.x = x;
    	}
    
    	public Integer call() throws Exception {
    		int sum = 0;
    		for (int i = 1; i <= x; i++) {
    			sum = sum + i;
    		}
    		return sum;
    	}
    
    }
    package com.oracle.demo02;
    
    import java.util.concurrent.ExecutionException;
    import java.util.concurrent.ExecutorService;
    import java.util.concurrent.Executors;
    import java.util.concurrent.Future;
    
    public class Test3 {
    	public static void main(String[] args) throws InterruptedException, ExecutionException {
    		// 创建线程池对象
    		ExecutorService es = Executors.newFixedThreadPool(2);
    		Future<Integer> f = es.submit(new CallableDemo(100));
    		Future<Integer> f2 = es.submit(new CallableDemo(200));
    		System.out.println(f.get());
    		System.out.println(f2.get());
    		es.shutdown();
    	}
    }
    

      

  • 相关阅读:
    git did not exit cleanly (exit code 128)
    thinkphp导出csv文件,用表格输出excel
    php函数substr_replace中文乱码的替代解决方法
    javascript中的后退和刷新
    技术上有困惑,来看看“一万个小时”吧
    什么是 .live()
    为什么php+apache本地站点访问超级慢
    thinkphp学习笔记10—看不懂的路由规则
    4星|《大英博物馆中国简史》:中国古代代表性文物图解
    贾似道简单几招让凶暴的临安府太学生变温顺:4星|《宫崎市定人物论》
  • 原文地址:https://www.cnblogs.com/Java-125/p/8931479.html
Copyright © 2011-2022 走看看