获取中...

-

Just a minute...

Java创建线程的三种方式

  • 继承Thread
  • 实现Runnable
  • 实现Callable

继承Thread创建线程

通过继承Thread的方式,可以很快的创建一个线程,Thread中有一个run()方法,只需要重写该方法,将需要另外开启一个线程处理数据(比如一个耗时的计算任务)的逻辑放在里面,然后调用start()方法便可以开启一个新的线程。这里注意调用的是start()方法,如果调用的是run()方法,这个方法是一个普通方法,整个代码的逻辑就和单线程一样。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
public class MyThread extends Thread {

/**
* 重写run方法,实现自己的逻辑
*/
@Override
public void run() {
System.out.println("进入:" + Thread.currentThread().getName());
try {
TimeUnit.SECONDS.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}

System.out.println("退出:" + Thread.currentThread().getName());

}

public static void main(String[] args) {

System.out.println("进入:" + Thread.currentThread().getName());

MyThread myThread = new MyThread();
myThread.start();

System.out.println("退出:" + Thread.currentThread().getName());

}
}

运行结果:

1
2
3
4
进入:main
退出:main
进入:Thread-0
退出:Thread-0

上面通过继承Thread创建了一个线程,通过Thread方法打印当前线程的名称,并让这个线程睡眠10秒。由于线程睡眠了10秒,在main线程运行结束后,自定义的线程还在运行,可见这是一个独立的线程。

实现Runnable创建线程

通过实现Runnabel接口,实现里面的run()方法,将业务逻辑放在run()方法中,然后将实现的Runnable对象作为参数传递给Thread,调用start()方法,也可以创建线程。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
public class MyRunnableThread implements Runnable {

/**
* 实现run方法,将业务逻辑放在里面
*/
@Override
public void run() {
System.out.println("进入:" + Thread.currentThread().getName());
try {
TimeUnit.SECONDS.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("退出:" + Thread.currentThread().getName());

}

public static void main(String[] args) {

System.out.println("进入:" + Thread.currentThread().getName());

MyRunnableThread myRunnableThread = new MyRunnableThread();
Thread thread = new Thread(myRunnableThread);
thread.start();

System.out.println("退出:" + Thread.currentThread().getName());
}
}

上面的程序运行结果和继承Thread创建线程一样。如果业务逻辑比较简单,在Java8之后,可以使用lambda表达式实现,代码更加简洁。

实现Callable创建线程

通过实现Callable也可以创建线程,和实现Runnable类似,不同的点是Callable是实现call()方法,这个方法可以返回值或者抛出异常,通过FutureTask,可以获取到返回的值。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
public class MyCallableThread implements Callable {
/**
* 实现call方法,将业务逻辑放在里面
*/
@Override
public Object call() {
System.out.println("进入:" + Thread.currentThread().getName());
try {
TimeUnit.SECONDS.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("退出:" + Thread.currentThread().getName());
return "return Data";
}

public static void main(String[] args) {

System.out.println("进入:" + Thread.currentThread().getName());

MyCallableThread callableThread = new MyCallableThread();

FutureTask futureTask = new FutureTask(callableThread);

new Thread(futureTask).start();

try {
System.out.println("自定义线程返回的值为:" + futureTask.get());
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}

System.out.println("退出:" + Thread.currentThread().getName());

}
}

运行结果:

1
2
3
4
5
进入:main
进入:Thread-0
退出:Thread-0
自定义线程返回的值为:return Data
退出:main

可以看到,通过FutureTask,可以拿到线程中返回的值,但是一旦使用了get()方法,就会让获取返回值的线程阻塞,知道计算线程返回结果,那这样和在同一个线程中计算有什么区别呢?区别在于主线程可以同步做一些可以并行的操作,节约时间。

总结

上面的三种方式都可以创建一个线程,既然这样,为什么还需要三种方式呢?首先通过Callable这种方式可以获取到返回值,这个特点比较明显。而继承Thread和实现Runnable这两种方式的区别主要是两点:(1)由于Java不能多继承,而可以多实现,这样通过实现的方式,线程类可以继承其他的类。(2)Runnable其实是一个任务,并不是线程本身,而是将任务作为参数传递给线程。可以将复杂的计算任务封装到Runnable中,使用多个线程进行计算。当然,这里由于是多个线程计算同一个任务,就不免涉及到多线程的问题。

相关文章
评论
分享
  • Java多线程之同步容器

    同步容器的作用Java中的容器主要为List、set、Map、Queue,这些容器都有不同的实现类,比如ArrayLIst、HashSet、HashMap、PriorityQueue等等,但是大部分都是非线程安全的,意味着在多线程访问...

    Java多线程之同步容器
  • Java多线程之ThreadLocal

    ThreadLocal的作用ThreadLocal的作用是提供线程内的局部变量,这个变量在同一个线程的生命周期内起作用,可以减少同一个线程内多个方法之前传入公共资源的复杂性,并且隔离其他线程。下面是一个使用ThreadLocal的例子...

    Java多线程之ThreadLocal
  • Java多线程之Lock

    引入Lock的原因Java提供了一个关键字synchronized用来保证同步访问,那为什么还需要使用Lock?最根本的原因就是使用synchronized会造成线程的阻塞,资源同时只能被一个线程访问,程序效率低。比如,多个线程查询读...

    Java多线程之Lock
  • Java多线程之synchronized

    多线程引发的安全问题由于线程是共享进程中的所有资源的,那么每个线程都可以去操作进程中的某一个资源,当多个线程操作同一个资源的时候就可能出现线程安全问题。比如两个线程对同一个实例变量做加1的操作,然后打印该变量,如果变量的初始值为0,当...

    Java多线程之synchronized
  • 线程和进程

    什么是进程在百度百科中,进程有两种定义,一种是狭义定义:进程是正在运行的程序实例,比如微信、Tomcat、MySQL,这些就是一个个进程。另一种就是广义的定义:进程是一个具有一定独立功能的程序关于某个数据集合的一次运行活动,它是操作...

    线程和进程
  • Redis过期策略和内存淘汰策略

    Redis处理过期的keyRedis会将每个设置了过期时间的key保存在一个独立的字典中,以后会定期扫描这个字典来删除过期了的key。除了定期扫描删除策略之外,Redis同时采用惰性删除策略,在客户端访问key的时候,Redis会对这...

    Redis过期策略和内存淘汰策略
  • Redis持久化:AOF

    什么是AOF持久化Redis有两种持久化方式,RDB和AOF。RDB是将Redis中的数据保存一份到RDB文件中,而AOF不是保存键值对数据,而是保存服务器执行的写命令来记录数据库的状态。 AOF持久化的实现原理AOF持久化的实现可...

    Redis持久化:AOF
  • Redis持久化:RDB

    什么是RDB持久化RDB持久化是Redis的持久化方式之一,也被称为快照持久化,是将某一个时间节点的Redis内存中数据保存到磁盘上,生成一个RDB文件。之后,用户可以将这份文件进行备份,复制到其他的服务器上创建具有相同状态的Redi...

    Redis持久化:RDB
  • jps命令详解

    jps命令的作用jps命令是JDK中自带的命令,位于bin目录下,其作用是显示当前用户下系统中所有的Java进程的相关信息,比如pid。有了这个命令,可以很方便的显示Java进程,不过显示的是当前用户的所有Java进程,不能根据项目名...

    jps命令详解
Please check the parameter of comment in config.yml of hexo-theme-Annie!