Java学习从入门到放弃

Java学习从入门到放弃

Java学习日记–线程(一)

1.1线程的介绍

在学习多线程前,先弄明白进程和线程的区别。

进程:是指运行中的应用程序,每一个进程都有自己独立的内存空间。一个应用程序可以同时启动多个进程。例如对于IE浏览器程序,每打开一个IE浏览器窗口,就启动了一个新的进程。同样,每次执行JDK的java.exe程序,就启动了一个独立的Java虚拟机进程,该进程的任务是解析并执行Java程序代码。

线程:是指进程中的一个执行流程,有时也称为执行情景。一个进程可以由多个线程组成,即在一个进程中可以同时运行多个不同的线程,它们分别执行不同的任务。当进程内的多个线程同时运行时,这种运行方式称为并发运行。许多服务器程序,如数据库服务器和Web服务器,都支持并发运行,这些服务器能同时响应来自不同客户的请求。

进程和线程主要的区别在于每个进程都需要操作系统为其分配独立的内存地址空间,而同一进程中的所有线程在同一块地址空间中工作,这些线程可以共享同一块内存和系统资源,比如共享一个对象或者共享已经打开的一个文件。

通俗的可以这样理解,人体可以同时进行呼吸、血液循环、思考问题等活动。用户既可以使用计算机听歌,也可以编写文档和发送邮件,而这些活动的完成可以同时进行。

一个程序运行后至少有一个进程,一个进程中可以包含多个线程,但至少有一个线程。

1.2单线程与多线程

往往在一个进程中,它包含的线程不只是一个,而是多个。因为多线程的效率往往要高于单线程。
用流程图表示,任务一和任务二是两个完全独立、互不相关的任务。
Java学习从入门到放弃_第1张图片
这个是单线程的环境下,完成任务一和任务二需要15s。

Java学习从入门到放弃_第2张图片
这个是多线程的环境下,完成任务一和任务二需要10s。

单线程的特点就是排队执行,也就是同步,就像在 cmd 中输入一条命令后,必须等待这条命令执行完才可以执行下一条命令一样。这就是单任务环境的缺点,即 CPU 利用率大幅降低。

而多线程的特点就是并发,许多子任务同时运行,举个栗子,QQ运行时就有很多的子任务在同时运行。像好友视频、下载文件、传输数据、发送表情等,这些不同的任务或者说功能都可以同时运行,其中每一项任务完全可以理解成是“线程”在工作,传文件、听音乐、发送图片表情等功能都有对应的线程在后台默默地运行。

但是也并不能说大量使用多线程效率就会提高。因为我们知道,CPU会在多个进程之间做着切换,如果我们开启的程序过多,CPU切换到每一个进程的时间也会变长,我们也会感觉机器运行变慢。所以合理的使用多线程可以提高效率,但是大量使用,并不能给我们带来效率上的提高。

1.3多线程的实例化

1.3.1继承Thread类

继承Thread类创建线程的步骤为:
(1)创建一个类继承Thread类,重写run()方法
(2)创建Thread类的子类的对象
(3)调用该对象的start()方法,该start()方法表示先开启新线程,来执行run中的逻辑

public class ThreadCreate {
    public static void main(String[] args){
        //继承Thread类
        MyThread thread=new MyThread();
        //一定要调用start来开启新线程,来执行run中的逻辑
        //如果直接调用run方法,不会开辟新的线程
        thread.start();
    }
}
class MyThread extends Thread{
    //需要重写run方法
    @Override
    public void run(){
        for (int i=0;i<10;i++){
            System.out.println("子线程的进程"+i);
        }
    }
}
1.3.2通过Runnable接口

实现Runnable接口创建线程的步骤为:
(1)创建一个类并实现Runnable接口
(2)重写run()方法
(3)创建实现Runnable接口的类的对象,将该对象当做Thread类的构造方法中的参数传进去
(4)使用Thread类的构造方法创建一个对象,并调用start()方法即可运行该线程

//通过Runnable接口
Runnable r1=() ->{
    for (int i=0;i<10;i++){
        System.out.println("线程二中的逻辑:"+i);
    }
};
Thread t1=new Thread(r1);
t1.start();
1.3.3通过Callable接口

实现Callable接口创建线程的步骤为:
(1)创建一个类并实现Callable接口
(2)重写call()方法,将所要完成的任务的代码写进call()方法中,需要注意的是call()方法有返回值,并且可以抛出异常
(3)如果想要获取运行该线程后的返回值,需要创建Future接口的实现类的对象,即FutureTask类的对象,调用该对象的get()方法可获取call()方法的返回值
(4)使用Thread类的有参构造器创建对象,将FutureTask类的对象当做参数传进去,然后调用start()方法开启并运行该线程。

import java.util.concurrent.Callable;
import java.util.concurrent.FutureTask;
public class ThreadCreate {
    public static void main(String[] args) throws Exception {
        Thread.currentThread().setName("主线程");
        System.out.println(Thread.currentThread().getName()+":"+"输出的结果");
        //创建FutureTask的对象
        FutureTask task = new FutureTask((Callable) new ThreadDemo3());
        //创建Thread类的对象
        Thread thread3 = new Thread(task);
        thread3.setName("线程三");
        //开启线程
        thread3.start();
        //获取call()方法的返回值,即线程运行结束后的返回值
        String result = task.get();
        System.out.println(result);
    }
}
class ThreadDemo3 implements Callable {
    @Override
    public String call() throws Exception {
        System.out.println(Thread.currentThread().getName()+":"+"输出的结果");
        return Thread.currentThread().getName()+":"+"返回的结果";
    }
}
1.3.4三种常见创建线程方式的比较

Thread继承:因为Java是单继承的,继承了Thread类就没办法继承其它类了,所以一般不会使用。Thread是重写run方法并且无返回值。

Runnable接口:比Thread类更加灵活,因为是接口,没有单继承的限制,所以使用的比Thread继承多。Runnable是重写run方法并且无返回值。

Callable接口:Callable是重写的call()方法并且有返回值并可以借助FutureTask类来判断线程是否已经执行完毕或者取消线程执行。

当线程不需要返回值时,就使用Runnable;需要返回值时,就使用Callable。

一般情况下不直接把线程体代码放到Thread类中,一般通过Thread类来启动线程。

你可能感兴趣的:(java,多线程,编程语言)