提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档
前言
提示:以下是本篇文章正文内容,下面案例可供参考
一、线程与进程的关系?
1.最小资源分配单元与最小执行单元
进程顾名思义就是指正在执行的程序,同时进程也是操作系统进行资源分配的最小单位,进程可进一步细化为一个或多个线程,故线程隶属于进程,早期的程序都是单线程的,后来因为不断省级的业务需求,同时为了提高程序的执行效率,同一个进程中往往有多个线程在执行任务,所以从这个角度来说,线程是进程中的最小执行单元,作为执行的最小执行单元,线程也就成了操作系统进行任务调度的最小单元
2.互相依赖
一个进程,即一个运行的应用程序中有无数个线程为该进程执行任务,故无数个线程共同执行并完成了它们所属的进程的任务,而进程作为操作系统资源分配的最小单位,为所有内部线程提供来自操作系统分配的资源,二者之间的关系可以简单地总结为:
一个线程只能属于一个进程,一个进程中至少有一个线程(主线程),进程启动时,需要向操作系统注册,并由操作系统为该进程分配运行时内存,并将该进程装入内存空间.
这也就很好地解释了线程为什么不能脱离进程而存在,线程依赖于进程所在的运行时存在,而该内存是由操作系统分配的临时内存,这意味着一旦该进程终止运行,该内存便会由操作系统进行回收
二、JAVA中创建线程两种常用方式
1.继承Thread类
代码如下(示例):
public class MyThread extends Thread{
//继承Thread类并实现run()方法
@Override
public void run() {
for (int i = 0; i < 1000; i++) {
System.out.println(Thread.currentThread().getName()+":"+(i+1));
}
}
}
这是Java中实现线程的最简单的方式,可以看到实质是对Thread类做了一个简单的扩展
该方法要求用户自定义一个继承了Thread的类,同时需要实现Thread类中的run()
Thread类是java中提供的对线程进程管理的类,可以看到run()中主要写了该线程需要执行的任务,
即希望线程完成什么样的任务
为什么一定要实现run()
public class Thread implements Runnable {
/* Make sure registerNatives is the first thing <clinit> does. */
private static native void registerNatives();
static {
registerNatives();
}
通过上面这一段代码我们可知Thread类继承了Runnable接口,而在Runnale接口中
public interface Runnable {
/**
* When an object implementing interface {@code Runnable} is used
* to create a thread, starting the thread causes the object's
* {@code run} method to be called in that separately executing
* thread.
* <p>
* The general contract of the method {@code run} is that it may
* take any action whatsoever.
*
* @see java.lang.Thread#run()
*/
public abstract void run();
}
可以看到Runnable接口中只有一个抽象方法run()
该方法定义了线程需要执行的任务
在实现了run()方法之后,只需要在主方法也就是主线程中创建线程对象并调用线程对象的start()即可
例如:
public static void main(String[] args) {
//实现多线程的第一种方式,自定义类继承Thread类,并重写run()
//创建线程类对象并启动MyThread线程
MyThread mt=new MyThread();
//调用start()启动当前线程
mt.start();
}
需要注意的是start()的作用是启动当前线程对象去执行run()中定义的线程任务,start()的实质是由操作系统对当前线程进行任务调度(这一过程是需要向操作系统注册的)
2.实现Runnable接口
代码如下(示例):
class Mythread1 implements Runnable{
@Override
public void run() {
for (int i = 0; i < 10; i++) {
System.out.println(Thread.currentThread().getName()+"优先级为");
System.out.println(Thread.currentThread().getPriority());
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
对比第一种直接继承的方式,二者均实现了Runnable接口中的run();其原因是Thread线程管理类也实现了Runnale这个顶级接口,而Runnable只唯一定义了run()方法
同样两种方式中run()的作用是相同的,即定义线程任务功能.
采用实现Runnable接口方式,我们需要在主线程中创建线程管理类Thread,同时将实现了
Runnable接口的自定义类的实例当做线程任务类对象传入,以下代码作为参考
public static void main(String[] args) {
//创建两个线程任务类,实现交替打印输出当前线程的优先级
Thread thread=new Thread(new Mythread1());
thread.setName("线程1");
thread.setPriority(1);
Thread thread1=new Thread(new Mythread1());
thread1.setName("线程2");
thread1.setPriority(10);
//启动两个线程交替打印
thread.start();
thread1.start();
}
public Thread(Runnable target) {
this(null, target, "Thread-" + nextThreadNum(), 0);
}
通过分析第二段源码我们可以知道Mythread1对象被当做target参数传入Thread的有参构造方法中
但在实际应用中我们多用第二种方式,原因在于第一种方式是直接继承并实现了Thread类中的run(),众所周知类与类之间的继承是单继承的,故第二种实现方式很好地避免了单继承的局限性,除此之外,采用第二种实现方式可以使得多个线程共享同一个Runnable接口实现类对象,即多个线程可以对同一个对象进行处理,这点是第一种方式无法媲美的。