1.概述
在学习线程之前,我们先来了解一下三个概念:程序,进程,线程
程序:为完成某种特定任务,用编程语言所编写的一段静态代码
进程:正在运行中的程序,是操作系统中进行资源分配的最小单位
线程:进程内部的最小执行单元,是操作系统进行任务调度的最小单元
三者的关系为:进程里属于程序,线程隶属于进程;且操作系统在一个时间点只能执行一个任务,特点为快,切换执行多个任务
2.线程与进程之间的关系
首先先了解一下主线程,主线程负责而启动程序,例如java中的main方法就是启动Java主线程,对于线程而言,拿QQ作为例子,一个聊天窗口就是一个线程
2.1一个进程中可以包含多个线程,而一个线程只能隶属于一个进程,且线程不能脱离进程独立运行,例如关闭QQ之后,聊天窗口则无法发消息。
2.2每一个进程至少需要一个线程(主线程),在主线程中开始执行程序,main方法就在程序中执行。
2.3一个进程中的所有线程共享同一个进程中的共存资源
2.4在一个单线程中,程序的执行顺序为从上至下
3.创建线程
目前两种方式:
3.1继承Thread类
先介绍一下Thread类,用于创建线程与操作线程的一个类。
在创建一个线程时,必须需要重写Thread类中的run()方法,具体操作写在run方法内部;
启动程序时先创建一个Thread类对象,通过Thread对象调用start()方法,切记不是调用run()方法,调用run()方法只是单纯的方法调用,在调用start()方法后,并不是立即启动线程,此时线程需等待操作系统进行任务调度,然后才会启动线程。
代码实现如下:
public class ThreadDome extends Thread {
//通过继承Thread类创建线程时,必须重写Thread类中的run方法
//需要在线程中执行的任务,必须写在run方法中
@Override
public void run() {
for (int i = 0; i <10 ; i++) {
if(i%10==0){
Thread.yield();//线程让步使线程从运行状态进入就绪状态
}
try {
Thread.sleep(1000);
//sleep()方法使线程从一个运行的状态进入阻塞,在sleep休眠时间到达后再次进入就绪状态,参数单位为毫秒
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(i);
}
}
}
//测试类
public class Text {
public static void main(String[] args) throws InterruptedException {
//要开启一个线程,需通过先创建个线程类对象,再通过调用start方法
ThreadDome threadDome=new ThreadDome();
threadDome.start();
//切记不能调用run方法,调用run方法,只是普通的方法调用,而非开启线程
}
}
3.2实现Runnable接口
Runnable接口是一个功能接口,注意实现Runnable接口的类并不是线程类,而继承Thread类的是线程类,实现Runnable接口的类只能称之为实现Runnable接口的实现类。
通过实现Runnable接口的方式创建线程时必须实现接口中的run()方法,将具体要实现的任务代码写入run()方法中。
启动线程时,需先创建一个Runnable实现类的对象,在创建一个Thread对象,将实现类对象作为参数传入Thread对象中,再调用start()方法,等待操作系统进行调度
代码实现:
public class RunnableDome implements Runnable {
@Override
public void run() {
System.out.println(Thread.currentThread().getName());
}
}
public class Text {
public static void main(String[] args) {
RunnableDome runnableDome=new RunnableDome();
//创建实现类的对象
Thread thread=new Thread(runnableDome);
//将实现类的对象作为参数传入Thread对象
thread.start();
}
}
4.线程的分类
4.1用户线程
用户线程即我们平时需要执行的任务创建的线程,比如main方法所执行的线程
4.2守护线程
任意一个守护线程为所有JVM中用户线程的保姆
因任意一个守护线程都是在所有守护线程执行完毕即死亡之后,等到最后一个用户线程执行完毕,守护线程同JVM一同退出。
注意:设置守护线程必须在启动线程之前,否则就会抛出异常,设置守护线程的代码为
Thread.currentThread().setDaemon(true);
//设置正在执行的线程为守护线程
代码实现:
package 守护Thread;
public class ThreadDome1 extends Thread {
@Override
public void run() {
for (int i = 0; i <10 ; i++) {
System.out.println("用户"+i);
}
}
}
package 守护Thread;
public class ThreadDome2 extends Thread {
int i=0;
@Override
public void run() {
while (true){
System.out.println("守护"+i++);
}
}
}
package 守护Thread;
public class Text {
public static void main(String[] args) {
ThreadDome1 threadDome1=new ThreadDome1();
threadDome1.start();
ThreadDome2 threadDome2=new ThreadDome2();
threadDome2.setDaemon(true);
//设置线程为守护线程,在所有用户线程结束后死亡
threadDome2.start();
}
}
5.多线程
多线程即允许一个程序中创建多个线程并行执行任务
优点:
提高程序的响应
提高CPU的利用率
改善程序结构,将复杂任务分配多个线程分别完成
缺点:
线程越多占用内存越多
多线程需要CPU时间跟踪线程
此上两个问题都可以通过提升硬件性能改善
第三个缺点:线程对贡献资源的访问会互相影响,必须去解决竟用共享资源的问题
此问题的解决我们下章解决