笔者这几天在学习Java中的多线程技术,看的是传智博客毕向东老师的《JavaSE基础视频》,以及电子工业出版社出版的《java学习手册》,现将线程中有关线程创建的部分整理出来。
一:线程的基本概念
【进程】:正在进行中的程序(直译)
【 线程】:进程中的一个负责程序执行的控制单元(执行路径)一个进程中可以有多可执行路径称之为线程
线程必须要有父进程,系统没有为秀按成分配资源,他与进程中的其他线程共享该进程的系统资源
【 多线程】:一个进程可以同时运行几个任务
一个进程中至少要有一个线程
开启多线程是为了同时运行多部分代码,每一个线程都有自己的运行的内容,这个内容可以称之为线程要执行的任务。
【多线程好处】解决了多部分代码(任务)同时运行的问题
【多线程弊端】线程太多会导致效率的降低
应用程序的执行都是CPU在作者快速的切换完成的,这个切换时随机的
jvm启动时就启动了多个线程,其中至少有两个线程可以分析的出来
1 指行main函数的线程
2负责垃圾回收的线程
二:如何创建线程
在Java中,线程也是一种对象,但并非任何对象都可以成为现线程,只要实现Runnable接口的类的对象才能成为线程,所以创建线程必须实现Runnable接口,创建线程共有两种方法
1>继承Thread类(Thread类本身以实现Runnable接口)
步骤:1定义一个类继承 Thread类
2覆盖Thread类的run方法
3直接创建Thread类的子类对象创建线程
4调用start方法开启线程并调用线程的任务run方法执行
思考:jvm创建的主线程的任务都定义在了main函数中,而自定义的线程的任务在哪儿呢
答:Thread类用于描述线程,而线程是需要任务的,所以Thread类也对任务描述,这个任务就通过Thread类
中的run方法来体现,也就是说,run方法就是封装自定义线程任务的函数,run方法中定义的就是要运行的任务代码
下面是一个例子
public class Demo extends Thread {
private String name;
Demo(String name)
{
this.name=name;
}
public void run()
{
for(int x=0;x<5;x++)
{
System.out.println("x="+x+"..."+"name="+name+"..."+Thread.currentThread().getName());
}
}
}
public class ThreadDemo2 {
public static void main(String[] args) {
Demo d1=new Demo("旺财");
Demo d2=new Demo("xiaoqiang");
d1.start();
d2.start();
}
}
由于CPU是随机分配的,所以每次运行出来的结果也是不同的,下面是一个运行结果
Thread.currentThread().getName的意思是得到当前运行的程序的名字
在主函数中,只有创建的对象调用了start方法后,才能去启动线程,这里不能写成调用run方法
如果写成run,程序运行结果如下,只有一个主线程,并没有启动多线程,当问题的规模足够大时,
这样是很浪费时间的
2>实现Runnable接口
步骤:1定义类实现Runnable接口
2覆盖接口中的run方法,将线程的任务封装到run方法中
3通过Thread类创建线程对象,并将Runnable接口的子类对象作为构造函数的参数进行传递
4调用线程对象的start方法开启线程
这是API中对接口的解释,下面是一个例子
public class Demo implements Runnable{
//extends object准备扩展Demo类的功能,让其中的内容可以作为线程
//的任务执行,通过接口的形式完成
public void run()
{
show();
}
public void show()
{
for(int i=0;i<5;i++)
System.out.println(Thread.currentThread().getName()+"..."+i);
}
}
public class ThreadDemo {
public static void main(String[] args) {
Demo d1=new Demo();
Demo d2=new Demo();
Thread t1=new Thread(d1);
Thread t2=new Thread(d2);
t1.start();
t2.start();
}
}
一下是程序的一个随机运行结果
三:创建线程两种方式的比较
由于Java只支持单一继承,当用接口来实现的时候,就很好的避免了这个问题,正如API所说的,如果我们只是想
覆盖run方法时 ,这时候定义一个子类去继承Thread类会显得没有必要,Runnable接口的事项将线程的任务从子类中分开出来,进行了单独的封装,按照面相对象的思想将任务封装成对象那个,避免了Java单继承的局限性,所以创建线程的第二种方法较为常用。当然第一种方法也有好处,它比较容易理解,写起来也较为简单。