话说线程的四种创建方式

并行和并发的区别

并行:parallelism,物理上同时执行;多个处理器同时处理多条指令;(单线程永远无法达到并行状态)
并发:concurrency,逻辑上多个任务交织执行;多个进程指令交替执行,同一时刻只有一条指令执行。(宏观上给人一种错觉是多个进程同时执行)

进程与线程的区别

  1. 调度:线程作为调度的基本单位;进程是资源分配的基本单位。
  2. 并发性:不仅进程之间可以并发执行;同一个进程的多个线程之间也可并发执行。
  3. 拥有资源:进程是拥有资源的一个独立单位,线程不拥有系统资源,但可以访问隶属于进程的资源;
  4. 系统开销:在创建或撤消进程时,由于系统都要为之分配和回收资源,导致系统的开销明显大于创建或撤消线程时的开销。但是进程有独立的地址空间,一个进程崩溃后,在保护模式下不会对其它进程产生影响。
    线程只是一个进程中的不同执行路径。线程有自己的堆栈和局部变量,但线程之间没有单独的地址空间,一个进程死掉就等于所有的线程死掉,所以多进程的程序要比多线程的程序健壮,但在进程切换时,耗费资源较大,效率要差一些。
  5. 作用:多进程作用是提高CPU的使用率,而不是提高执行速度;
    多线程作用是提高应用程序的使用率,而不是提高执行速率。

结论:

  1. 线程是进程的一部分。
  2. CPU调度的是线程。
  3. 系统为进程分配资源,不对线程分配资源。

第一种方式继承Thread类

继承Thread类步骤

  1. 定义Thread类的子类,并重写该类的run()方法,该run()方法的方法体就代表线程需要完成的任务。run()即为线程执行体;
  2. 创建Thread子类的实例,即创建线程对象;
  3. 调用线程对象的start()方法来启动该线程。

常用方法

  • Thread currentThread():Thread类的静态方法,返回当前正在执行的线程对象。
  • void setName():Thread类的实例方法,重命名线程名称。
  • String getName():Thread类的实例方法,返回调用该方法的线程名称。
  • void start():启动线程。
  • run():start()方法调用后,执行Thread的run()体。
public class ExtendsThread extends Thread{

	@Override
    public void run() {
        System.out.println("开始运行线程的主方法run方法");
    }

    public static void main(String[] args) {
        ExtendsThread threadTest = new ExtendsThread();
        threadTest.start();// 必须调用start()方法才能启动线程
    }
}


第二种方式实现Runable接口

实现Runnable接口步骤

  1. 定义Runnable接口的实现类,并重写该接口的run()方法,该run()是线程执行体;
  2. 创建Runnable实现类的实例,并以此实例作为Thread的target来创建Thread对象,该Thread对象才是真正的线程对象;
  3. 调用线程对象的start()方法来启动该线程。

注意
实现Runable接口和继承Thread的方式区别:继承Thread创建的线程是创建的Thread子类即可代表线程对象;而实现Runable接口的创建的Runnable对象只能作为线程对象的target。

public class ImplRunable implements Runnable{
	@Override
	public void run() {
		System.out.println("开始运行线程的主方法run方法");
	}

	public static void main(String[] args) {
		ImplRunable implRunable = new ImplRunable();
		Thread t = new Thread(implRunable); 
		t.start();
	}
}

常用方法

  • public abstract void run();:Runnable接口中只包含一个抽象方法,Runnable接口是函数式接口,可使用Lambda表达式创建Runnable对象。

第三种方式实现Callable接口

使用Callable和Future步骤

  1. 创建Callable接口的实现类,并实现call()方法,该call()方法即为线程执行体,且该call()方法有返回值,再创建Callable实现类的实例;
  2. 使用FutureTask类包装Callable对象,该FutureTask对象封装了该Callable对象的call()方法的返回值;
  3. 使用FutureTask对象作为Thread对象的target创建并通过线程对象的start()方法启动线程;
  4. 调用FutureTask对象的get()方法获得子线程执行结束后的返回值。
public class ImplCallable implements Callable {
	@Override
	public String call(){
		System.out.println("开始运行线程的主方法run方法");
		return "运行结束":
	}

	public static void main(String[] args) {
		Callable<String> callable = new ImplCallable();
		FutureTask<String> futureTask = new FutureTask<>(callable);
		Thread t = new Thread(futureTask);
		t.start();
	}
} 

 

三种实现线程方式的区别

  1. 返回值:继承Thread类,run()方法没有返回值;实现Runnable接口和Callable接口方式基本相同,但Callable接口定义的call()方法具有返回值,可以声明抛出异常。
  2. 继承:继承Thread类,不能再继承其他类;线程类实现Runnable和Callable接口可以继承其他类。
  3. 访问当前线程:继承Thread类,需要访问当前线程,无须使用Thread.currentThread()方法,直接使用this即可获得当前线程;线程类实现Runnable和Callable接口,若访问当前线程,必须使用Thread.currentThread()方法。多个线程共享同一个target对象,适合多个相同线程来处理同一份资源的情况,从而将CPU、代码和数据分开,形成清晰的模型,较好地体现面向对象思想。

推荐使用线程类实现Runnable和Callable接口方式创建多线程。

Callable、Future和FutureTask具体有什么差异,可以看一下

https://www.cnblogs.com/dolphin0520/p/3949310.html

 

第四种方法使用线程池

public class ThreadPool{

	public static void main(String[] args) {
		new ThreadPool().newFixedThreadPool();
		new ThreadPool().newCachedThreadPool();
		new ThreadPool().newSingleThreadExecutor();
	}

	// 使用newFixedThreadPool创建线程池
	// 这种方法是创建固定线程数的线程池,如果使用线程的数量大于线程池里的线程数量,则需要等待其他线程释放,才可继续使用线程池里的线程
	public void newFixedThreadPool(){
		ExecutorService executorService = Executors.newFixedThreadPool(5);// 线程池里有5个线程
		for (int i = 0;i < 3;i++) {
			executorService.execute(new Runnable(){
				@Override
                public void run() {
                	System.out.println(Thread.currentThread().getName() + “线程开始执行”);
                    try {
                        Thread.sleep(2000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println(Thread.currentThread().getName() + “线程执行结束”);
                }
			});
		}
	}

	// 使用newCachedThreadPool创建线程池
	// 这种方法可不指定创建线程池的数量,会持续不断的产生线程,如果线程池里的线程60秒(默认时间)内没有被使用的话会自动销毁
	public void newCachedThreadPool(){
		ExecutorService executorService = Executors.newCachedThreadPool();
		for (int i = 0;i < 3;i++) {
			executorService.execute(new Runnable(){
				@Override
                public void run() {
                	System.out.println(Thread.currentThread().getName() + “线程开始执行”);
                    try {
                        Thread.sleep(2000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println(Thread.currentThread().getName() + “线程执行结束”);
                }
			});
		}
	}
	// 使用newSingleThreadExecutor创建线程池
	// 这种方法只有一个线程,必须等待此线程运行结束才可执行下一个任务
	public void newSingleThreadExecutor(){
		ExecutorService executorService = Executors.newSingleThreadExecutor();
		for (int i = 0;i < 3;i++) {
			executorService.execute(new Runnable(){
				@Override
                public void run() {
                	System.out.println(Thread.currentThread().getName() + “线程开始执行”);
                    try {
                        Thread.sleep(2000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println(Thread.currentThread().getName() + “线程执行结束”);
                }
			});
		}
	}
}


 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值