所谓线程就是就是一个执行单元,而一个进程就是一个程序,简单来说,打开一个qq软件就是一个进程,同时打开一个微信,那就是两个进程。那么线程就是在一个轻量级进程,比如打开微信与A聊天是一个线程,与B聊天就是另一个线程。使用多线程的好处:
1.可以减少程序的响应时间。
2.与进程相比,线程的创建和切换开销更小。
3.使用多线程能够简化程序的结构,便于程序理解和维护。
以下就是来说明线程的创建方法。
首先,先说明本文说明的例子是学生有每个月存钱的习惯,这里举了Tom和Jack的例子,他俩其实互不干涉,谁先到银行谁先存,但是如果是一般地程序,则会等一个人完成所有操作,才会另一个人存,所以需要创建新的线程。下面是文中例子涉及的学生类。
package person;
public class Student {
public int k=0;
public int total;
public int perMonth;
public String name;
public void saveMoney(Student s) {
// TODO Auto-generated method stub
try {
Thread.sleep(1000);
}
catch(InterruptedException e) {
e.printStackTrace();
}
s.k+=1;
if(s.k>=12)
System.out.println(s.name+"存储结束,总共存储了:"+s.total+"元");
else
{
s.total=s.total+s.perMonth;
System.out.println(s.name+"第"+k+"年总共拥有"+s.total+"元");
}
}
}
1.普通程序
普通的情况便是不使用线程的情况,则是按顺序执行,只有前面的执行完之后才能执行后面的程序,具体代码如下所示:
package waytobuildthread;
import person.Student;
public class GeneralTest {
public static void main(String[] args) {
// TODO Auto-generated method stub
Student tom=new Student();
Student jack=new Student();
tom.total=1000;
tom.perMonth=200;
tom.name="Tom";
jack.total=1500;
jack.perMonth=100;
jack.name="Jack";
while(tom.k<12)
tom.saveMoney(tom);
while(jack.k<12)
jack.saveMoney(jack);
}
}
运行程序,得到结果如下:
Tom第1月总共拥有1200元
Tom第2月总共拥有1400元
Tom第3月总共拥有1600元
Tom第4月总共拥有1800元
Tom第5月总共拥有2000元
Tom第6月总共拥有2200元
Tom第7月总共拥有2400元
Tom第8月总共拥有2600元
Tom第9月总共拥有2800元
Tom第10月总共拥有3000元
Tom第11月总共拥有3200元
Tom存储结束,总共存储了:3200元
Jack第1月总共拥有1600元
Jack第2月总共拥有1700元
Jack第3月总共拥有1800元
Jack第4月总共拥有1900元
Jack第5月总共拥有2000元
Jack第6月总共拥有2100元
Jack第7月总共拥有2200元
Jack第8月总共拥有2300元
Jack第9月总共拥有2400元
Jack第10月总共拥有2500元
Jack第11月总共拥有2600元
Jack存储结束,总共存储了:2600元
由上面的运行结果可以知道:只有等Tom12个月存完之后才输出Jack12个月存的过程。这样的过程显然不合理,因此需要引入多线程的概念,以下便是讲解几个创建多线程的方法。
2.继承Thread类
Thread本质上也是实现了Runnable接口的一个实例,它代表一个线程的实例,并且启动线程的唯一方法就是通过Thread类的start()方法。这过程中需要重写run()方法,当调用Thread类的start()方法时,只是使该线程变为可运行状态,而不是直接执行run()方法。以下是通过该方法实现本文示例的代码:
首先是创建线程
package waytobuildthread;
import person.Student;
public class ExtendsThread extends Thread {
public Student s;
public int total;
public int m;
public ExtendsThread(Student s)
{
this.s=s;
}
public void run()
{
while(s.k<12)
s.saveMoney(s);
}
}
然后测试线程:
package waytobuildthread;
import person.*;
public class ExtendsThreadTest {
public static void main(String[] args) {
// TODO Auto-generated method stub
Student tom=new Student();
Student jack=new Student();
tom.total=1000;
tom.perMonth=200;
tom.name="Tom";
jack.total=1500;
jack.perMonth=100;
jack.name="Jack";
ExtendsThread thread1=new ExtendsThread(tom);
thread1.start();
ExtendsThread thread2=new ExtendsThread(jack);
thread2.start();
}
}
最后运行测试程序,得到如下结果:
Jack第1月总共拥有1600元
Tom第1月总共拥有1200元
Jack第2月总共拥有1700元
Tom第2月总共拥有1400元
Tom第3月总共拥有1600元
Jack第3月总共拥有1800元
Tom第4月总共拥有1800元
Jack第4月总共拥有1900元
Tom第5月总共拥有2000元
Jack第5月总共拥有2000元
Jack第6月总共拥有2100元
Tom第6月总共拥有2200元
Tom第7月总共拥有2400元
Jack第7月总共拥有2200元
Jack第8月总共拥有2300元
Tom第8月总共拥有2600元
Jack第9月总共拥有2400元
Tom第9月总共拥有2800元
Jack第10月总共拥有2500元
Tom第10月总共拥有3000元
Jack第11月总共拥有2600元
Tom第11月总共拥有3200元
Tom存储结束,总共存储了:3200元
Jack存储结束,总共存储了:2600元
从上面的结果可以看出,使用多线程来执行该例,则更切实际,即谁先到银行谁先存钱,整个过程是同步的。
3.实现Runnable接口
该方法主要三个步骤:1.实现Runnable接口,实现run()方法。2.创建Thread对象,用实现Runnable接口的对象作为参数实例化该Thread对象。3.调用Thread的start()方法。
实现接口:
package waytobuildthread;
import person.Student;
public class ImplementRunnable implements Runnable {
public Student s;
public int total;
public int m;
public ImplementRunnable(Student s)
{
this.s=s;
}
@Override
public void run() {
// TODO Auto-generated method stub
while(s.k<12)
s.saveMoney(s);
}
}
测试程序:
package waytobuildthread;
import person.Student;
public class RunnableTest {
public static void main(String[] args) {
// TODO Auto-generated method stub
Student tom=new Student();
Student jack=new Student();
tom.total=1000;
tom.perMonth=200;
tom.name="Tom";
jack.total=1500;
jack.perMonth=100;
jack.name="Jack";
ImplementRunnable t1=new ImplementRunnable(tom);
new Thread(t1).start();
ImplementRunnable t2=new ImplementRunnable(jack);
new Thread(t2).start();
}
}
运行程序,结果如下:
Jack第1月总共拥有1600元
Tom第1月总共拥有1200元
Tom第2月总共拥有1400元
Jack第2月总共拥有1700元
Tom第3月总共拥有1600元
Jack第3月总共拥有1800元
Jack第4月总共拥有1900元
Tom第4月总共拥有1800元
Tom第5月总共拥有2000元
Jack第5月总共拥有2000元
Tom第6月总共拥有2200元
Jack第6月总共拥有2100元
Jack第7月总共拥有2200元
Tom第7月总共拥有2400元
Tom第8月总共拥有2600元
Jack第8月总共拥有2300元
Tom第9月总共拥有2800元
Jack第9月总共拥有2400元
Jack第10月总共拥有2500元
Tom第10月总共拥有3000元
Tom第11月总共拥有3200元
Jack第11月总共拥有2600元
Tom存储结束,总共存储了:3200元
Jack存储结束,总共存储了:2600元
由结果可以看出与继承Thread类效果基本相同,而且两者都是需要通过Thread对象的API来控制线程。
Thread类和Runnable接口都是创建线程的方法,但是Runnable接口还是稍有优势,具体如下:
1.避免单继承的限制
2.适合多个相同程序代码的线程去处理同一个资源。
3.线程池只能放入实现Runnable接口和Callable接口的线程,不能直接放入继承Thread类的线程。