一个程序运行后至少有一个进程,一个进程可以包含多个线程,至少有一个线程
原理
一个软件在某一时间里运行,其实CPU只运行了一个进程,但我们感觉很多软件都在运行呢,其实是CPU在做着高速的切换,我们人是很难看出来的,对CPU而言,一时刻只能运行一个进程。
多线程可以解决一个主线程负责执行其中的一个循环,由另一个线程负责其他代码的执行
创建线程方式——继承Thread类h
继承Thread就要实现里面的run方法
创建线程的步骤:
- 定义一个类继承Thread
- 重写run方法
- 创建子类对象
- 调用start方法,开启线程并让线程执行,同时告诉JVM去调用run方法
class Demo extends Thread //继承Thread
{
String name;
Demo(String name)
{
this.name = name;
}
//复写其中的run方法
public void run()
{
for (int i=1;i<=20 ;i++ )
{
System.out.println("name="+name+",i="+i);
}
}
}
class ThreadDemo
{
public static void main(String[] args)
{
//创建两个线程任务
Demo d = new Demo("小强");
Demo d2 = new Demo("旺财");
//d.run(); 这里仍然是主线程在调用run方法,并没有开启两个线程
//d2.run();
d2.start();//开启一个线程
d.run();//主线程在调用run方法
}
}
线程中调用run方法和调用start方法区别:
线程对象调用run方法是不开启线程的,仅是对象调用调用方法,程序调用start开启线程,并且让JVM调用run方法在开启的线程中执行
创建线程的目的
建立单独的执行路径,让多部分代码实现同时执行。也就是说线程创建并执行需要给定的代码(线程的任务)。对于之前所讲的主线程,它的任务定义在main函数
中。自定义线程需要执行的任务都定义在run方法中。Thread类中的run方法内部的任务并不是我们所需要,只有重写这个run方法,既然Thread类已经定义了线程任务的位置,只要在位置中定义任务代码即可。所以进行了重写run方法动作。
当执行线程的任务结束了,线程自动在栈内存中释放了。但是当所有的执行线程都结束了,那么进程就结束了。
获取线程的名称
Thread.currentThread()获取当前线程对象
Thread.currentThread().getName();获取当前线程对象的名称
当主线程执行完成了,并不代表程序就结束了,如果此时还有其他的线程正在执行,程序就仍然在执行
当一个线程发生异常,其他线程还是会继续执行的,异常只会影响到异常所属的线程
创建线程的第二种方式——实现Runnable接口
步骤:
- 定义类实现Runnable接口
- 覆盖接口中的run方法
- 创建Thread类的对象
- 讲Runnable接口的子类对象作为参数传递给Thread类的构造函数
- 调用Thread类的start方法开启线程
class Demo implements Runnable
{
private String name;
Demo(String name)
{
this.name = name;
}
//覆盖了接口Runnable中的run方法。
public void run()
{
for(int i=1; i<=20; i++)
{ System.out.println("name="+name+"..."+Thread.currentThread().getName()+"..."+i);
}
}
}
class ThreadDemo2
{
public static void main(String[] args)
{
//创建Runnable子类的对象。注意它并不是线程对象。
Demo d = new Demo("Demo");
//创建Thread类的对象,将Runnable接口的子类对象作为参数传递给Thread类的构造函数。
Thread t1 = new Thread(d);
Thread t2 = new Thread(d);
//将线程启动。
t1.start();
t2.start();
System.out.println(Thread.currentThread().getName()+"----->");
System.out.println("Hello World!");
}
}
实现Runnable的原理
继承Thread类和实现Runnable接口有什么区别
实现Runnable接口,避免了继承Thread类的单继承局限性,覆盖Runnable接口中的run方法,将线程代码定义到run方法里,创建Thread类的对象,只有创建Thread类的对象才可以创建线程。线程任务已经被封装到了Runnable的run方法里面,而这个run方法所属于Runnable接口的子类对象,所以将这个子类对象作为参数传递给Thread的构造函数,这样线程对象创建时就可以明确要运行的线程任务。
实现Runnable的好处
第二种方式实现Runnable接口避免了单继承的局限性,所以较为常用。实现Runnable接口的方式,更加符合面向对象,线程分为了两部分,一部分是线程对象,一部分是线程任务,继承Thread类,线程对象和线程任务耦合在了一起,一旦创建Thread类的子类对象,既是线程对象,又是线程任务。实现Runnable接口,将线程任务单独分离出来封装成对象,类型就是Runnable接口类型,Runnable接口对线程任务和线程对象进行解耦。