线程的创建
什么是线程
线程(英语:thread)是操作系统能够进行运算调度的最小单位。它被包含在进程之中,是进程中的实际运作单位。一条线程指的是进程中一个单一顺序的控制流,一个进程中可以并发多个线程,每条线程并行执行不同的任务。在Unix System V及SunOS中也被称为轻量进程(lightweight processes),但轻量进程更多指内核线程(kernel thread),而把用户线程(user thread)称为线程。——百度百科
就像团队与个人的关系。团队接到一个任务,会把任务发放下去,实际的执行是由一个人来完成的,每个人可以进行不同的工作。
线程的实现
在Java中实现线程有三种方式。第一种方式是继承Thread类,第二种方式是实现Runnable接口。第三种方式是实现Callable接口。不管哪种方式,最后的目标都是为了实例化Thread类。
继承Thread类实现线程,代码如下:
//继承Thread类
public class MyThread extends Thread{
int sum=0;
//创建子线程的有参构造方法
public MyThread(String name){
super(name);
}
//重写run()方法
@Override
public void run() {
while(sum++<10) {
System.out.println(getName() + "-------" + sum);
}
}
}
//main函数
public static void main(String[] args) {
//创建线程对象
//MyThread thread1=new MyThread();
//thread1.setName("thread1");
//这里直接传入了线程名称 效果和上面代码一样
MyThread thread1=new MyThread("thread1");
MyThread thread2=new MyThread("thread2");
//启动线程
thread1.start();
thread2.start();
}
实现Runnable接口实现线程,代码如下:
//实现Runnable接口
public class MyRunnable implements Runnable {
int sum=0;
//重写run()方法
@Override
public void run() {
while(sum++<10){
System.out.println(Thread.currentThread().getName()+"------"+sum);
}
}
}
//main函数
public static void main(String[] args) {
//创建Runnable接口的实现类对象
MyRunnable myRunnable=new MyRunnable();
//创建Thread对象
Thread thread1=new Thread(myRunnable,"Thread1");
Thread thread2=new Thread(myRunnable,"Thread2");
//启动线程
thread1.start();
thread2.start();
}
实现Callable接口实现线程,代码如下:
//实现Callable接口
public class MyCallable implements Callable {
int num=0;
//重写call()方法 注意返回值类型不是void
@Override
public Object call() throws Exception {
while(num++<10){
System.out.println(Thread.currentThread().getName()+"------"+num);
}
return num;
}
}
//main函数
public static void main(String[] args) throws ExecutionException, InterruptedException {
//创建Callable接口实现类对象
MyCallable myCallable=new MyCallable();
//使用FutureTask封装Callable接口
FutureTask<Object> ft1=new FutureTask<Object>(myCallable);
//创建线程对象
Thread thread1=new Thread(ft1,"Thread_1");
FutureTask<Object> ft2=new FutureTask<Object>(myCallable);
Thread thread2=new Thread(ft2,"Thread_2");
//启动线程
thread1.start();
thread2.start();
//使用FutureTask对象的get()方法获取返回值
System.out.println(ft1.get());
System.out.println(ft2.get());
}
上面的实现代码中我们可以发现
(1)继承Thread类方式的一般使用过程
- 新建一个类MyThread(类名可以随便起)继承Thread类
- 重写Thread类的run()方法,run()方法里面的内容就是我们启动线程时执行的内容
- 构造MyThread对象
- 使用线程对象
(2)实现Runnable接口方式的一般使用过程
- 新建一个类MyRunnable(类名可以随便起)实现Runnable接口
- 重写Thread类的run()方法,run()方法里面的内容就是我们启动线程时执行的内容
- 构造MyRunnable对象,然后利用Thread类构造方法Thread(Runnable target)创建线程对象
- 使用线程对象线程
(3)实现Callable接口的一般使用过程
- 新建一个类继承(类名可以随便起)实现Callable接口
- 重写Callable的call()方法
- 创建Callable实现类对象 用FutureTask类封装Callable实现类对象
- 使用Thread对象的Thread(Runnable target)构造方法创建线程对象
- 使用线程对象
Notes:
① Runnable接口和Callable接口的使用基本上没有区别。最大的区别就在于Callable接口call()方法的返回值类型是Object,而Runnable接口的run()方法返回值是void
② Runnable和Callable接口并不是Thread类,要想Runnable接口和Callable接口像Thread类一样使用,必须通过一定操作将实现Runnable接口的类或者实现Callable接口的类转为Thread类。我认为,这是实现接口和继承类方式实现线程的最大不同。
- 在run()方法体内,想要使用Thread的方法(以获取线程名称为例)。如果继承Thread类的方式,只需要 getName() 就好;而如果实现Runnable接口或者Callable接口的方式,就需要利用Thread类的静态方法currentThread() 返回当前线程对象,如:Thread.currentThread().getName()
- 在Thread对象实例化过程中,继承Thread类的方式直接实例化自己的MyThread类就好,如:MyThread thread=new MyThread(); 而实现Runnable接口方式在实例化自己的MyRunnable类之后还需要通过Thread类的构造方法转为Thread对象,如:MyRunnable runnable=new MyRunnable();Thread thread=new Thread(runnable); Callable类参考上面代码。
③ 因为实现接口的方式实现线程,都需要对接口的实现类进行进一步转化,最终创建Thread对象。我们可以在创建Thread对象时传入相同的接口实现类,这样就会实现线程资源的共享。
④ 一个类只能继承一个类,但可以实现多个接口。因此,在需要使用线程的地方,最好使用实现接口的方式。
⑤ Thread类有三个静态成员返回线程的优先级。分别是static int MAX_PRIORITY ——返回线程可以拥有最大优先级,static int MIN_PRIORITY ——返回线程可以拥有最小优先级, static int NORM_PRIORITY ——返回线程默认分配优先级 。线程的优先级一般在1~10之间。这个具体想要了解可以点击下方链接:
⑥ 运行上面第二段代码和第三段代码的话,会发现运行的结果毫无顺序可言,总感觉代码出了问题。前面③中提到过,线程之间会共享资源,而线程访问资源是随机的,一个线程在对资源进行操作未完成时,另一个线程可能也在对线程进行操作,就会出现我们看到的混乱的运行结果。详细的解决办法可以点击下方链接:
链接会尽快更新,请耐心等待。。。