1、继承Thread
public class Threadextend extends Thread{
@Override
public void run() {
for(int i=0;i<=100;i++){
System.out.print("A"+i+ "\t");
}
}
}
public class Main {
public static void main(String[] args) {
Threadextend trea=new Threadextend("小强");
//start 表示开启线程
trea.start();
for(int i=0 ;i <100;i++){
System.out.print("B"+i +"\t");
}
System.out.println(trea.getName());
}
}
结果如下,随机打印
2、实现runnable接口
实现Runnable接口比继承Thread类所具有的优势:
1. 适合多个相同的程序代码的线程去共享同一个资源。 (注意我说的是适合,这里我从网上看到了一些人的回答(比如 https://www.cnblogs.com/fxust/p/8998696.html ),都说thread不能资源共享,可能是断章取义吧,Thread可以实现资源共享的,只不过是现实开发中它不适合资源共享,因为它如果想资源共享的话可以将共享的资源设置成静态的,因为静态资源的生命周期是和类绑在一起的,和对象没有关系。但是这样做的坏处就是,如果此时有两个不一样的任务的话,同时都调用这个类,那么就不能实现了,因为静态变量不能区分这两个任务了 )
2、 可以避免java中的单继承的局限性。 (就是说接口的好处了,实现完接口后还可以继承)
3、增加程序的健壮性,实现解耦操作,代码可以被多个线程共享,代码和线程独立。
(从代码架构角度:具体的任务(run方法)应该和“创建和运行线程的机制(Thread类)”解耦)
4、线程池只能放入实现Runable或Callable类线程,不能直接放入继承Thread的类。
class ThreadRunnable implements Runnable{
@Override
public void run() {
for(int i=0;i<=100;i++){
System.out.print("A"+i+ "\t");
}
}
}
public static void main(String[] args) {
ThreadRunnable threadRunnable=new ThreadRunnable();
Thread thread = new Thread(threadRunnable, "小强");
thread.start();
for(int i=0 ;i <100;i++){
System.out.print("B"+i +"\t");
}
System.out.println(thread.getName());
}
结果同上
3、jdk1.5后,实现callable接口
和以上相比,callable更加强大一些
1:相比run()方法,可以有返回值
2:方法可以抛出异常
3:支持泛型的返回值
4:需要借助FutureTask类,比如获取返回结果
class ThreadCallable implements Callable{
int result=0;
@Override
public Integer call() throws Exception {
for(int i=0;i<=100;i++){
System.out.println(i);
result +=i;
}
return result;
}
}
public static void main(String[] args) {
ThreadCallable threadCallable = new ThreadCallable();
FutureTask result= new FutureTask<>(threadCallable);
new Thread(result).start();
try {
System.out.println(result.get());
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
}
4、jdk1.5后,线程池
提前创建好多个线程,放入线程池中,使用时直接获取,使用完放回池中。可以避免频繁创建销毁、实现重复利用。提高响应速度 (减少了创建新线程的时间) 降低资源消耗(重复利用线程池中线程,不需要每次都创建) 便于线程管理。
4.1体系结构
如上图,顶级接口为Executor,真正的线程池接口是ExecutorService,下面是对上图的说明
4.2 工具类:Executors
Executors.newCachedThreadPool():创建一个可根据需要创建新线程的线程池
Executors.newFixedThreadPool(n); 创建一个可重用固定线程数的线程池
Executors.newSingleThreadExecutor() :创建一个只有一个线程的线程池
Executors.newScheduledThreadPool(n):创建一个线程池,它可安排在给定延迟后运
行命令或者定期地执行。 返回值类型为ScheduleThreadPoolExeccutor。
class ThreadRunnable implements Runnable{
@Override
public void run() {
for(int i=0;i<=100;i++){
System.out.print(Thread.currentThread().getName()+ "\t"+ "A"+i+ "\t");
}
}
}
线程池与Runnable的使用方式
public static void main(String[] args) {
// 创建线程池
ExecutorService pool = Executors.newFixedThreadPool(5);
ThreadRunnable trb=new ThreadRunnable();
//为线程池分配任务
for(int i=0;i<10;i++){
pool.submit(trb);
}
//关闭线程池,shutdown只是将线程池的状态设置为SHUTWDOWN状态,
//正在执行的任务会继续执行下去,没有被执行的则中断。而shutdownNow
//则是将线程池的状态设置为STOP,正在执行的任务则被停止,没被执行任务的则返回。
pool.shutdown();
}
线程池与Callable的使用方式, 带有返回值Future
// 创建线程池
ExecutorService pool = Executors.newFixedThreadPool(5);
List> list = new ArrayList<>();
//为线程池分配任务
for(int i=0;i<10;i++){
Future future = pool.submit(new Callable() {
@Override
public Integer call() throws Exception {
int sum=0;
for (int j = 0; j <100 ; j++) {
sum+=j;
}
return sum;
}
});
list.add(future);
}
//关闭线程池
pool.shutdown();
for (Future future : list) {
System.out.println(future.get());
}
5、延申:start() 和run()有什么区别呢?
run():只是调用了一个普通方法,并没有启动另一个线程,程序还是会按照顺序执行相应的代码。线程被调度的时候,执行的操作
start():重新开启一个线程,此线程进入到就绪状态,不必等待其他线程运行完,只要得到cup资源就可以运行该线程,如下图,这个图在后面的博文中会有详细的讲解