多线程的创建
文章目录
前言
多线程在我们实际的编程应用中经常会用到,在日常软件的使用中也很常见,比如:你可以边使用QQ聊天、边听音乐,也可以边写程序边给朋友打视频电话等等。这些功能都依赖于多线程的实现,如果没有多线程的话系统处理的效率将会大打折扣,当然在单核处理器下也不可能做到以上的行为。今天就来简单地聊一下我认为的多线程和其中的一些应用。
一、线程和进程是什么?
在学习多线程的内容之前,我们必须对线程和进程有一个大概的了解。
进程:顾名思义,简单来讲就是,当一个软件运行了,它就被看做是一个进程(”行进中的程序")
线程:指的是,当一个软件运行后,他可以同时干很多事情,那么这时每件事情就是一个线程。
例如:QQ可以聊天的同时传文件,两者同时进行。 那么QQ就是一个进程,它拥有聊天和传文件这两个线程。
PS:当然实际中QQ绝对不止这两个线程,因为软件在后台还会运行很多对用户不透明的工作
二、单核处理器是否支持多线程?
如果电脑是单核的,那么此时支不支持多线程呢? 答案是支持!
此时多个线程会抢占CPU的执行权,执行到就执行,执行完就释放资源,如果还要执行则继续抢。
因为这个抢占和执行的速度很快,所以在我们看来虽然是单核处理器,但是进程是同时进行的。
(这里详细的讲解可以去翻阅进程的五/七状态模型,此处不做展开,有基础概念即可)
三、创建多线程
我们现在已经了解了进程和线程的概念和区别,下面就进入主题:多线程
首先来了解几种常见的创建多线程的方法
1.通过继承Thread类创建
代码如下(示例):
public class ThreadDemo extends Thread{
/**
* 每一个方法都有一个无参数的构造方法
* 如果是继承则会调用父类的构造方法
*/
//默认的无参构造方法
public ThreadDemo(){
super();
}
@Override
public void run(){
for (int i = 0; i < 5; i++) {
System.out.println(Thread.currentThread().getName()+"*****"+i);
}
}
/**
* 目前这个代码是三个线程同时执行 谁先执行谁后执行还不一定
* 跟之前不一样 以前是只有一个main线程(主线程) 从上到下执行
* @param args
*/
public static void main(String[] args) {
System.out.println("我是main方法,我是入口");//main方法是主线程
ThreadDemo thread1 = new ThreadDemo();//线程1
ThreadDemo thread2 = new ThreadDemo();//线程2
thread1.start();//线程进入就绪态 准备抢cpu 抢到就进入运行态
thread1.start();
System.out.println("main方法结束");
}
}
运行结果:
1、我们可以通过继承Thread使用它的默认无参构造方法,new一个新的ThreadDemo实例化对象thread1,这个对象就是一个线程。
2、thread1中有start()方法,在调用该方法后,线程就进入就绪态,准备抢占CPU,当抢占到后则变为运行态。处于运行态后会执行重写过的run()方法。
3、可以使用Thread.currentThread().getName()方法获得当前线程的Name。
4、可以看到main函数中的语句并不是顺序输出的,而是乱序,当线程抢占到CPU资源后才进行输出。
2.通过实现Runnable接口创建
代码如下(示例):
public class MyRunnable implements Runnable{
//函数式接口
@Override
public void run() {
for (int i = 0; i < 5; i++) {
System.out.println(Thread.currentThread().getName()+"*****"+i);
}
}
public static void main(String[] args) {
System.out.println("main 开始");
//第一种
MyRunnable runnable = new MyRunnable();
Thread thread1 = new Thread(runnable,"窗口1");
Thread thread2 = new Thread(runnable,"窗口2");
thread1.start();
thread2.start();
//第二种
Runnable runnable1 = new Runnable() {
@Override
public void run() {
System.out.println("线程内容是:窗口3");
}
};
Thread thread3 = new Thread(runnable1,"窗口3");
/**
* 以上代码也可以使用lambda表达式编写 但是不利于代码的重复利用 创建多个线程的话 run中的方法需要重复好几次
*/
//第三种
Thread thread4 = new Thread(() -> System.out.println("线程内容是:窗口4"), "窗口4");
thread3.start();
thread4.start();
System.out.println("main 结束...");
}
}
1、Runnable是一个接口,所以我们要实现它
2、我们可以使用Thread,传入一个Runnable和一个String传线程的名字,new一个Thread对象,那么这个对象就是一个线程(第一种)
3、同样的,我们也可以在main函数中实现Runnable接口(第二种)
4、第三种方法是对第二种方法的简写,使用了lambda表达式,但这样写也有缺陷,因为使用一次及被回收,如果还需要的话还要再写一遍。
3.通过实现Callable接口创建
public class CallableDemo implements Callable<String> {
@Override
public String call() throws Exception {
for (int i = 0; i < 5; i++) {
System.out.println(Thread.currentThread().getName()+"+++"+i);
}
return "hello";
}
public static void main(String[] args) throws Exception{
System.out.println("主线程");
//未来的任务 可以将一些耗时的操作交给他完成
FutureTask<String> futureTask = new FutureTask<String>(new CallableDemo());
new Thread(futureTask).start();
//FutureTask间接实现了Runnable的实现类
//想获得子线程的返回值 需要通过get方法 只有子线程执行完 才能拿到结果
System.out.println(futureTask.get());
System.out.println("主线程结束");
}
}
1、一般用于子线程需要跑一个任务 这个任务工作量很大 并且需要返回值 可以考虑使用Callable。
2、Callable和Runnable一样都是接口,都需要实现。
3、Callable通常和FutureTask搭配使用。
4、Callable接口有返回值,而Runnable接口没有。
四、线程名设置
public class MyThread extends Thread{
/**
* 每一个方法都有一个无参数的构造方法
* 如果是继承则会调用父类的构造方法
*/
//默认无参
public MyThread(){
super();
}
//有参构造
public MyThread(String name){
super(name);
}
@Override
public void run(){
}
public static void main(String[] args) {
//给线程起名字
//第一种 直接调用setName方法修改
MyThread thread1 = new MyThread();//线程1
thread1.setName("线程1");
//第二种 创建线程的时候通过构造方法指定
MyThread thread2 = new MyThread("线程2");//线程2
//demo01.run();//不要直接run 否则不是多线程
}
}
1、每一个方法都有一个无参数的构造方法,如果是继承则会调用父类的构造方法。继承中,子类的构造方法必须先调用父类的构造方法,父类的构造有有参和无参。
2、默认每个线程都有自己的默认的名字 Thread-xxxx
小结
三种创建方式对比:
继承Thread 是方案一, 实现Runnable 接口是方案二
1、优先使用 方案二
2、方案一是继承,java是单继承的,有局限性,而对于接口,一个类可以实现多个接口。
3、关于多线程的操作,都是Thread类,Thread类是专门管理多线程的,比如启动,设置名字等。而Runnable只是编写业务代码,将两者分开更加合理(代码的解耦)【高内聚低耦合】
4、使用Callable接口和Runnable接口相比
1)有返回值
2)抛异常
3)需要放入FutureTask中才能使用
PS(一些题外话)
创建线程的方法一共有四种,此处先列出3种,最后一种是从线程池中获取,下次再加上 嘿嘿
初次写文章,如有错误敬请指教,希望和大家多多交流学到更多的知识!