zoukankan      html  css  js  c++  java
  • java多线程的3种实现方式

    多线程相关的问题

    1.什么是进程?

    ​ 正在执行的程序

    2.什么是线程?

    ​ 进程的子单位,一个能够完成独立功能的执行路径

    3.为什么需要开启多线程?

    • 当执行某些耗时操作的任务的时候需要开启多线程,防止线程阻塞
    • 能够让两个任务看起来像是在同时执行
    • 提高CPU的使用率,进而提高进程和内存的使用率

    4.为什么开启多线程会同时执行?

    ​ 因为CPU切换执行的速度太快了,肉眼无法差距

    5.开启多线程是不是越多越好,提高了效率还是降低了效率?

    ​ 不是,线程越多,效率越慢,但是太少,浪费CPU资源,所以,合理利用CPU

    6.并发和并行的区别

    • 并发 --> 在同一个时间段下同时执行多个线程,看起来像同时执行
    • 并行 --> 在同一个时间刻度下(不能够在分割的时间单位)执行多个线程,本质就上就是同时执行

    CPU在某一个最小的时间刻度单位下,执行的是一个进程的一个线程的一个不可再分割的原子性语句

    线程开启方式

    Java提供了三种创建线程方法:

    ​ 1.继承Thread类的方式

    ​ 2.实现Runnable的方式

    ​ 3.通过 Callable 和 Future 创建线程。

    方式一:继承Thread类

    ​ 1.自定义类MyThread继承Thread类。3
    ​ 2.MyThread类里面重写run()方法。
    ​ 3.创建线程对象。
    ​ 4.启动线程。
    注意:
    ​ 1、启动线程使用的是start()方法而不是run()方法
    ​ 2、线程能不能多次启动

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

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

    代码演示使用方式一开启线程

    // 方式二:实现Runnable接口
    class CalculateRunnable implements Runnable {
    	private int m;
    	private int n;
    	public CalculateRunnable(int m, int n) {
    		super();
    		this.m = m;
    		this.n = n;
    	}
    	public CalculateRunnable() {
    		super();
    	}
    	@Override
    	public void run() {
    		int sum = 0;
    		for (int i = m; i <= n; i++) {
    			sum += i;
    			System.out.println("CalculateRunnable子线程: " + i);
    		}
    		System.out.println(m + "~" + n + "的和: " + sum);
    	}
    } 
    

    方式二:实现Runnable接口

    ​ 1.自定义类MyRunnable实现Runnable接口
    ​ 2.重写run()方法
    ​ 3.创建MyRunnable类的对象
    ​ 4.创建Thread类的对象,并把步骤3创建的对象作为构造参数传递
    ​ 5.启动线程

    实现接口方式的好处
    可以避免由于Java单继承带来的局限性。
    适合多个相同程序的代码去处理同一个资源的情况,把线程同程序的代码,数据有效分离,较好的体现了面向对象的设计思想。

    代码演示方式二开启线程

    // 方式一:继承Thread类
    class CopyThread extends Thread {
    	private File srcFile;
    	private File descFile;
    	
    	public CopyThread() {
    		super();
    	}
    
    	public CopyThread(File srcFile, File descFile) {
    		super();
    		this.srcFile = srcFile;
    		this.descFile = descFile;
    	}
    
    	@Override
    	public void run() {
    		// main方法怎么写,这里就怎么写
    		copy(srcFile, descFile);
    	}
    
    	public void copy(File srcFile, File descFile) {
    		try (BufferedInputStream bis = new BufferedInputStream(new FileInputStream(srcFile));
    				BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(descFile))){
    			byte[] bys = new byte[1024];
    			int len = 0;
    			while ((len = bis.read(bys)) != -1) {
    				bos.write(bys, 0, len);
    				bos.flush();
    			}
    		} catch (FileNotFoundException e) {
    			e.printStackTrace();
    		} catch (IOException e) {
    			e.printStackTrace();
    		}
    	}
    }
    
    主方法调用
    	public static void main(String[] args) {
    		CopyThread ct = new CopyThread(new File("Test.java"), new File("Test.txt"));
    		CalculateRunnable cr = new CalculateRunnable(1, 200);
    		Thread t = new Thread(cr);
    		ct.start();
    		t.start();
    		for (int i = 0; i < 100; i++) {
    			System.out.println("main线程: " + i);
    		}
    	}
    

    方式三: Callable 和 Future 创建线程。

    • 创建 Callable 接口的实现类,并实现 call() 方法,该 call() 方法将作为线程执行体,并且有返回值。
    • 创建 Callable 实现类的实例,使用 FutureTask 类来包装 Callable 对象,该 FutureTask 对象封装了该 Callable 对象的 call() 方法的返回值。
    • 使用 FutureTask 对象作为 Thread 对象的 target 创建并启动新线程。
    • 调用 FutureTask 对象的 get() 方法来获得子线程执行结束后的返回值。

    继承Thread和实现Runnable的方式的特点:

    ​ 1.没有返回结果

    ​ 2.没有异常

    Callable和Runnable的区别:

    ​ 1.Runnable无返回值,没有异常抛出的

    ​ 2.Callable可以在启动线程中获取返回值, 以及接受子线程的异常

    线程间的数据传递:线程通信

    ​ A线程中开启了B线程

    ​ A --> B 通过构造方法

    ​ B --> A 通过Callable方式

    public class CallableDemo{
    	public static void main(String[] args) {
    		FutureTask<Integer> task = new FutureTask<>(new CalculateCallable(0,100));
    		Thread t = new Thread(task);
    		t.start();
    		
    		try {
    			Integer i = task.get();
    			System.out.println("计算结果: " + i);
    		} catch (InterruptedException | ExecutionException e) {
    			e.printStackTrace();
    		} catch (Exception e) {
    			e.printStackTrace();
    		}
    		
    		System.out.println("程序结束");
    	}
    }
    
    class CalculateCallable implements Callable<Integer> {
    	
    	private int m;
    	private int n;
        
    	public CalculateCallable() {
    		super();
    	}
        
    	public CalculateCallable(int m, int n) {
    		super();
    		this.m = m;
    		this.n = n;
    	}
    
    	@Override
    	public Integer call() throws Exception {
    		int sum = 0;
    		for (int i = m; i <= n; i++) {
    			System.out.println("子线程: " + i);
    			sum += i;
    			throw new NullPointerException("空指针异常!");
    		}
    		return sum;
    	}
    }
    
  • 相关阅读:
    C++11 vector使用emplace_back代替push_back
    Centos6.4 编译安装 nginx php
    Centos 编译安装nodejs&express框架
    zookeeper 入门(二)
    zookeeper 入门(一)
    Paxos算法1-算法形成理论[转载]
    yum只下载软件不安装的两种方法
    Centos 6.4 安装dnsmasq
    Centos 6.4 安装erlang&rabbitmq
    Centos 6.4 安装Python 2.7 python-pip
  • 原文地址:https://www.cnblogs.com/zhiwenxi/p/11407857.html
Copyright © 2011-2022 走看看