1.继承Thread类
先看代码
- 创建线程类
//创建一个类,继承Thread
public class myThread extentce Thread{
//重写Thread类中的run()方法,(run方法中写自己的业务逻辑)
@Override
public void run() {
for (int i = 0; i < 10; i++) {
System.out.println("i = " + i);
}
}
}
- 定义一个测试类
public class ThreadDemo {
public static void main(Stirng[] args){
//创建自定义线程类的子类对象
myThread t = new myThread();
//调用start()方法开启线程
t.start();
//主方法任务
for (int i = 0; i < 10; i++) {
System.out.println(Thread.currentThread().getName()+"i = " + i);
}
}
}
- 注意说明
1.不可以使用线程类对象直接调用run()方法,这样只是普通的调用方法,不会开启线程.只能使用线程对象调用start()方法开启,交由jvm执行.
2.不要直接创建Thread线程对像,对象再调用start()方法开启线程,这样操作也可以开启线程,但是Thread类中的run()方法没有被重写,就没有任何业务逻辑,毫无意义.
3.创建线程的目的?
建立程序单独执行的路径,可以让多个任务同时执行,在业务里使用线程,我们要给他指定要执行的任务才有意义.
4.多线程的内存运行
多线程执行任务时,每个线程在栈内存中,都有一片自己的运行空间,进行方法的压栈和弹栈,当某个线程任务执行完成后,就自动在内存中释放掉了,当所有线程任务执行完毕之后,进程也就结束了.
5.获取线程的名称
currentThread():返回当前线程对象的引用,
getName():返回线程名称。
String name = Thread.currentThread().getName();
2.实现Runnable接口
- 创建线程类实现Runnable接口
public class MyThread implements Runnable {
//重写run()方法
@Override
public void run() {
for (int i = 0; i < 10; i++) {
System.out.println("线程"+ Thread.currentThread().getName() + i);
}
}
}
- 定义测试类
public class ThreadDemo{
public static void main(String[] args){
//创建自定义线程类对象
MyThread t = new MyThread ();
//创建线程对象,传入自定义线程类对象
Thread thread = new Thread( t )
//开启线程
thread.start();
//主方法业务代码
for (int i = 0; i < 10; i++) {
System.out.println(Thread.currentThread().getName() + i);
}
}
}
- 注意说明
1.继承Thread类和实现Runnable接口的区别
实现Runnable接口可以避免单继承Thread类的局限性,而且只有创建Thread类对象才可以使用开启线程,线程任务已被封装到Runnable接口的run()方法中,该接口所属Runnable接口的子类对像,把子类对象作为参数传递到Thread类的构造方法中,线程对象创建时就可以明确要执行的线程任务。
2.实现Runnable接口的好处
一般最常用的创建线程的方式就是实现Runnable接口,即避免了单继承的局限性,又更加符合面向对象思想,把线程分为两部分,一部分是线程对象,一部分是线程任务。继承Thread类,线程对象和线程任务耦合在一起。实现Runnable接口,将线程任务单独分离出来封装成对象,Runnable接口对线程任务和线程对像进行解耦.
3.使用线程池
3.1实现Runnable接口的方式
-
使用线程池的好处
在业务中,每个请求都创建一个线程的话,开销是非常大的.创建和销毁线程会消耗大量的系统资源,如果在一个jvm里创建太多线程,可能会导致系统过度消耗内存而资源不足.我们就要采取一些办法尽可能的减少线程创建和销毁的次数.线程池就是用来解决线程生命周期开销问题和资源不足问题.通过对多个任务重复使用线程,线程创建的开销就会被分摊到多个任务上,由于在请求到达时线程已经存在,所以消除了线程创建时带来的延迟,就可以立即为请求服务,使程序响应更快,而且适当的调整线程的数目可以防止出现资源不足的情况. -
线程池都是由线程池工厂创建,再调用线程池中的方法获取线程,再通过线程去执行任务.
Executors:线程池创建工厂类
public static ExecutorService newFixedThreadPool(int nThreads):返回线程池对象
ExecutorService:线程池类
Future<?> submit(Runnable task):获取线程池中的某一个线程对象,并执行
Future接口:用来记录线程任务执行完毕后产生的结果。线程池创建与使用 -
实现Runnable接口,使用线程池
public class ThreadDemo{
public static void main(String[] args){
//创建线程池对象 指定线程数量
ExecutorService service = Executors.newFixedThreadPool(2);
//创建线程类对象
MyThread t = new MyThread();
//从线程池中获取线程对象 传入自定义线程类对象
service.submit(demo);
service.submit(demo);
//submit方法调用结束后,程序并不终止,是因为线程池控制了线程的关闭。将使用完的线程又归还到了线程池中,需要手动关闭.
service.shutdown();
}
}
- 自定义线程类
public class MyThread implements Runnable {
@Override
public void run() {
System.out.println("来把锤子");
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("锤子来了");
System.out.println("锤子用完之后放回工具箱里.");
}
}
3.2 实现callable接口的方式
Callable接口:与Runnable接口功能相似,用来指定线程的任务。其中的call()方法,用来返回线程任务执行完毕后的结果,call方法可抛出异常。
ExecutorService:线程池类
Future submit(Callable task):获取线程池中的某一个线程对象,并执行线程中的call()方法
Future接口:用来记录线程任务执行完毕后产生的结果。线程池创建与使用.
使用步骤:
(1):创建线程池对象
(2):创建callable接口的子类对象
(3):提交callable接口的子类对象
(4):关闭线程池