轻松掌握理解多线程

多线程实现方式

继承Thread类

一般不用这种方法实现,为什么后面会说

如何实现
  1. 自定义线程类继承Thread类
  1. 重写run方法,编写线程执行体
  1. 创建线程对象,调用start方法开启线程
public class Demo extends Thread{
	//重写run方法
    @Override
    public void run() {
        for (int i = 0; i < 10; i++) {
            System.out.println("线程"+i);
        }
    }
    public static void main(String[] args) {

        //创建一个线程对象
        Demo demo = new Demo();
        //调用start方法开启线程
        demo.start();


        for (int i = 0; i < 10; i++) {
            System.out.println("主线程"+i);
        }

    }

}


总结:线程开启后不一定立即执行,由CPU调度执行

实现Runnable接口

推荐使用,可以让一个对象同时被多个线程使用

如何实现
  1. 自定义线程类实现Runnable接口
  1. 重写run方法,编写线程执行体
  1. 创建实现类对象和代理类对象,通过代理类调用start方法开启线程
public class Demo implements Runnable{
	//重写run方法
    @Override
    public void run() {
        for (int i = 0; i < 10; i++) {
            System.out.println("线程"+i);
        }
    }
    public static void main(String[] args) {

        //创建接口实现类对象
        Demo demo = new Demo();
        
        //创建代理类线程对象,通过线程对象来开启线程--》代理
        Thread thread = new Thread(demo);
        thread.start();
        
        for (int i = 0; i < 10; i++) {
            System.out.println("主线程"+i);
        }

    }

}
	其中
	Thread thread = new Thread(demo);
	thread.start();
	也可以写成
	new Thread(demo).start();

在这里插入图片描述

实现Callable接口

如何实现
  1. 自定义线程类实现Callable接口,需要返回值类型
  1. 重写call方法,编写线程执行体,需要抛出异常
  1. 创建实现类对象和代理类对象,通过代理类调用start方法开启线程
  1. 创建执行服务:ExecutorService executorService = Executors.newFixedThreadPool(1);
  1. 提交执行:Future<Boolean> result = executorService.submit(demo);
  1. 获取结果:boolean r = result.get();
  1. 关闭服务:executorService.shutdownNow();
public class Demo implements Callable<Boolean> {
    @Override
    public Boolean call() {
        for (int i = 0; i < 100; i++) {
            System.out.println("线程"+i);
        }
        return true;
    }
    
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        Demo demo = new Demo();
		//创建执行服务
        ExecutorService executorService = Executors.newFixedThreadPool(1);
        //提交执行
        Future<Boolean> result = executorService.submit(demo);
        for (int i = 0; i < 10; i++) {
            System.out.println("主线程"+i);
        }
		//获取结果
        boolean r = result.get();
        //关闭服务
        executorService.shutdownNow();
    }
}

在这里插入图片描述

多线程理解

首先理解下面两句话

  • 线程对象通过继承Thread类或者直接通过Thread创建的
  • 线程要做的事(任务)通过重写Runnable接口的run方法定义的

如果只是创建了线程对象,却没有指定线程任务,那么这个线程是没有意义的,在程序上我们要实现线程对象和线程任务的绑定,就是让创建线程对象的那个类实现Runnable接口

我们看一下Thread类的源码
Thread源码
因为Thread类实现了Runnable接口,所以Thread类具备了执行线程任务的能力
但是线程不一定要执行任务,主要体现在Thread的构造器
Thread无参构造
Thread有参构造
有参构造这里传入的var1,就是我们上面说的任务

	// 这就是没有任务的一个线程,无参构造
	new Thread().start();
	// 创建线程时,传入任务,有参构造
	new Thread(()->{
		for(int i=0; i<10; i++){
			System.out.println(i);
		}
	}).start();

Thread的run()方法理解

线程对象的线程任务是在run()方法里写的,前面说到线程是否执行由CPU调度决定,当线程开始执行任务时,调用Thread的run()方法。
Thread的run()方法
这里的target就是我们传进来的任务

Thread类定义了一个成员变量private Runnable target;,用来接收任务

创建对象的时候传入的任务var1赋值给target成员变量,线程执行的时候,操作target
看一下源码
在这里插入图片描述

执行init方法把var1传进去,给var2,注意这里var1已经变成var2了
init

调用自己的重载方法,继续把var2往里面传
init重载
在这里插入图片描述

为什么不直接调用run()方法,而是调用start()方法呢?

上面我们说到,线程对象调用start()方法后,最终会调用run()方法,那么为什么不直接调用run()方法呢?

实际上,如果直接调用run()方法,那么就变成主线程在直接调用run()方法,也就是说还是主线程在执行方法,并不是多线程执行了,而调用start()才会真正启动线程

至于为什么调用start()才会真正启动线程,核心是start()方法里的start0()

start源码
start0
这个start0()是一个本地方法,由JVM调用

为什么不推荐使用继承Thread的方式实现多线程?

第一,因为OOP只能单继承,有局限性,这是一个原因

第二,通过例子说明

首先,我们使用线程对象,肯定要执行任务,继承Thread类就可以定义自己的任务

public class Demo extends Thread{
	//重写run方法
    @Override
    public void run() {
        //要执行的任务
    }
}

这样的方式直接把线程任务和线程对象绑定了,将来想让这个线程对象去做别的线程任务,还得修改代码,耦合度太高。

如果是通过实现接口来定义自己的任务呢

public class Demo1 implements Runnable{
	//重写run方法
    @Override
    public void run() {
        //吃饭
    }
}
public class Demo2 implements Runnable{
	//重写run方法
    @Override
    public void run() {
        //打豆豆
    }
}
public static void main(String[] args) {
	Runnable r1 = new Demo1();
	Runnable r2 = new Demo2();
	//这时thread执行的是Demo1的任务:吃饭
	Thread thread = new Thread(r1);
	thread.start();
	//想让thread执行别的任务,不需要去改thread代码,直接传入另一个任务即可
	//这时thread执行的是Demo2的任务:打豆豆
	thread = new Thread(r2);
	thread.start();
}

那这种方法有没有缺点呢?

当然有,当任务很多时,要创建很多个类,很麻烦,怎么优化?

  1. 使用匿名内部类,减少类的定义
public static void main(String[] args) {
	Thread thread = new Thread(new Runnable() {
		@Override
		public void run() {
			//要执行的任务
		}
	});
	thread.start();
}
  1. 再优化,关注方法实现,用lambda
public static void main(String[] args) {
	Thread thread = new Thread(()->{
		//要执行的任务,方法实现
	});
	thread.start();
}

sleep()方法让哪个线程休眠?

看下面这段代码

public static void main(String[] args) {
	Thread thread1 = new Thread(()->{
		for(int i=0; i<10; i++){
			System.out.println("我是thread1");
		}
	});
	thread1.start();
	try{
		thread1.sleep(1000);
	}catch (InterruptedException e) {
		e.printStackTrace();
	}
	Thread thread2 = new Thread(()->{
		for(int i=0; i<10; i++){
			System.out.println("我是thread2");
		}
	});
	thread2.start();
}

第一个问题:这段代码是先输出我是thread1还是我是thread2呢?

答案:先输出我是thread1

第二个问题:sleep()方法是不是让thread1休眠了?

答案:没有,sleep()方法让main主线程休眠了1s

这是怎么回事呢?

事实上,并不是哪个线程调用sleep()方法,它就会休眠,而是看sleep()写在哪里,与调用者无关!

那怎么样让thread1休眠呢?把sleep()写在它的任务执行体里就行

public static void main(String[] args) {
	Thread thread1 = new Thread(()->{
		try{
			Thread.sleep(1000);
		}catch (InterruptedException e) {
			e.printStackTrace();
		}
		for(int i=0; i<10; i++){
			System.out.println("我是thread1");
		}
	});
	thread1.start();
	
	Thread thread2 = new Thread(()->{
		for(int i=0; i<10; i++){
			System.out.println("我是thread2");
		}
	});
	thread2.start();
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值