进程与线程
一、并发与并行
-
并发∶指两个或多个事件在
同一个时间段
内发生。 -
并行 : 指两个或多个事件在
同一时刻
发生(同时发生)。
二、进程、线程与守护线程
进程∶是指一个内存中运行的应用程序,每个进程都有一个独立的内存空间,一个应用程序可以同时运行多个进程;进程也是程序的一次执行过程,是系统运行程序的基本单位﹔系统运行一个程序即是一个进程从创建、运行到消亡的过程。
线程︰线程是进程中的一个执行单元,负责当前进程中程序的执行,一个进程中至少有一个线程。一个进程中是可以有多个线程的,这个应用程序也可以称之为多线程程序。
简而言之 : 一个程序运行后至少有一个进程,一个进程中可以包含多个线程。我们可以在电脑底部任务栏,右键----->打开任务管理器,可以查看当前任务的进程︰进程
守护(daemon)线程
- 线程分为用户线程和守护线程
- 虚拟机必须确保用户线程执行完毕
- 虚拟机不用等待守护线程执行完毕。
- 如,后台记录操作日志,监控内存,垃圾回收等待.
三、创建线程类
1、概要与简单使用
Java使用 java.lang.Thread 类代表 线程,所有的线程对象都必须是Thread类或其子类的实例。每个线程的作用是完成一定的任务,实际上就是执行一段程序流即一段顺序执行的代码。Java使用线程执行体来代表这段程序流。Java中通过继承Thread类来创建并启动多线程,步骤如下∶
1.定义Thread类的子类,并重写该类的run()方法,该run()方法的方法体就代表了线程需要完成的任务,因此把 run()方法称为线程执行体。
2.创建Thread子类的实例,即创建了线程对象
3.调用线程对象的start( )方法来启动该线程
/**
* java.Lang. Thread类:是描述线程的类,我们想要实现多线程程序,就必须继承Thread类
* 实现步骤:
* 1.创建一个Thread类的子类
* 2.在Thread类的子类中重写Thread类中的run方法,设置线程任务(开启线程要做什么?)
* 3.创建Thread类的子类对象
* 4.调用Thread类中的方法start方法,开启新的线程,执行run方法l
* void start()使该线程开始执行;Java虚拟机调用该线程的run方法。
* 结果是两个线程并发地运行;当前线程(main线程)和另一个线程(创建的新线程,执行其run方法)。
* 多次启动一个线程是非法的。特别是当线程已经结束执行后,不能再重新启动。
*/
public class ThreadTest {
public static void main(String[] args) {
MyThread myThread = new MyThread();
myThread.start();
//非 Thread 类[子类]对象 获取线程名称: 用 Thread类 的 静态方法 currentThread()
//获取 Thread对象,再使用getName()获取进程名
System.out.println("线程名称:"+Thread.currentThread().getName());
for (int i = 0; i < 20; i++) {
System.out.println("main --> "+i);
}
}
}
public class MyThread extends Thread {
@Override
public void run() {
// Thread 类[子类]对象 获取线程名称,使用成员方法 getName()
System.out.println("线程名称为:"+getName());
for (int i = 0; i < 20; i++) {
System.out.println("run --> "+i);
}
}
}
//输出结果(部分) -->随机打印
线程名称:main
线程名称为:Thread-1
线程名称为:Thread-0
main --> 0
run --> 0
main --> 1
run --> 1
run --> 2
......
main --> 17
main --> 18
main --> 19
图解解释:
内存图解:
2、构造方法与常用方法
在上一天内容中我们已经可以完成最基本的线程开启,那么在我们完成操作过程中用到了java.lang.Thread类,API中该类中定义了有关线程的一些方法,具体如下∶
构造方法
public Thread( )//分配一个新的线程对象。
public Thread( String name)//分配一个指定名字的新的线程对象。
public Thread(Runnable target)//分配一个带有指定目标新的线程对象。
public Thread(Runnable target,string name)//分配一个带有指定目标新的线程对象并指定名字。
常用方法
public String getName()//获取当前线程名称。
public void start()//导致此线程开始执行; Java虚拟机调用此线程的run方法。
public void run()//此线程要执行的任务在此处定义代码。
public static void sleep(long millis)//使当前正在执行的线程以指定的毫秒数暂停(暂时停止执行)
public static Thread currentThread()//返回对当前正在执行的线程对象的引用。
翻阅API后得知创建线程的方式总共有两种,一种是继承Thread类方式,一种是实现Runnable接口方式,方式一我们上一天已经完成,接下来讲解方式二实现的方式。
3、实现Runnable接口创建线程
创建多线程程序的第二种方式:实现Runnable接口java.lang. Runnable,Runnable接口应该由那些打算通过某一线程执行其实例的类来实现。类必须定义一个称为 run 的无参数方法java.lang.Thread类的构造方法,
public Thread(Runnable target)//分配一个带有指定目标新的线程对象。
public Thread(Runnable target,string name)//分配一个带有指定目标新的线程对象并指定名字。
实现Runngble接口创建多线程程序的 好处 :
1.避免了单继承的局限性
一个类只能继承一个类,类继承了Thread类就不能继承其他的类实现了Runnable接口,还可以继承其他的类,实现其他的接口
2.增强了程序的扩展性,降低了程序的耦合性(解耦)
实现Runnable接口的方式,把设置线程任务和开启新线程进行了分离(解耦)实现类中,
重写了run()方法 : 用来设置线程任务,创建Thread类对象, 调用start()方法 : 用来开启新线程
4、匿名内部类的形式
匿名内部类方式实现线程的创建,匿名 : 没有名字
内部类 : 写在其他类内部的类,
匿名内部类作用 : 简化代码,把子类继承父类,重写父类的方法,创建子类对象合一步完成,把实现类实现类接口,重写接口中的方法,创建实现类对象合成一步完成。
匿名内部类的最终产物 : 子类/实现类对象,而这个类没有名字。
//格式
new 父类/接口(){
重写父类/接口中的方法
};
//例子
public static void main(String[] args) {
// Thread类方式
new Thread(){
@Override
public void run() {
for (int i = 0; i < 20; i++) {
System.out.println("InnerClassThread --> "+i);
}
}
}.start();
// Runnable接口方式
new Thread( new Runnable(){
@Override
public void run() {
for (int i = 0; i < 20; i++) {
System.out.println("InnerClassRunnable --> "+i);
}
}
}).start();
}
5、使用Callable接口创建线程
例子1,直接运行线程:
public class ThreadByCalable {
public static void main(String[] args) throws Exception
FutureTask<Integer> futureTask = new FutureTask<>(new MyThreadCallable());
new Thread(futureTask).start();
System.out.println("返回值为:"+futureTask.get());
}
}
class MyThreadCallable implements Callable<Integer>{
@Override
public Integer call() throws Exception {
System.out.println("Callable->线程实现");
return 100;
}
}
例子2,通过线程池线程: