先说下线程的几种状态(5种):
新建状态(New):当线程对象对创建后,即进入了新建状态,如:Thread t = new MyThread();
就绪状态(Runnable):当调用线程对象的start()方法(t.start();),线程即进入就绪状态。处于就绪状态的线程,只是说明此线程已经做好了准备,随时等待CPU调度执行,并不是说执行了t.start()此线程立即就会执行;
运行状态(Running):当CPU开始调度处于就绪状态的线程时,此时线程才得以真正执行,即进入到运行状态。注:就 绪状态是进入到运行状态的唯一入口,也就是说,线程要想进入运行状态执行,首先必须处于就绪状态中;
阻塞状态(Blocked):处于运行状态中的线程由于某种原因,暂时放弃对CPU的使用权,停止执行,此时进入阻塞状态,直到其进入到就绪状态,才 有机会再次被CPU调用以进入到运行状态。根据阻塞产生的原因不同,阻塞状态又可以分为三种:
1.等待阻塞:运行状态中的线程执行wait()方法,使本线程进入到等待阻塞状态;
2.同步阻塞 -- 线程在获取synchronized同步锁失败(因为锁被其它线程所占用),它会进入同步阻塞状态;
3.其他阻塞 -- 通过调用线程的sleep()或join()或发出了I/O请求时,线程会进入到阻塞状态。当sleep()状态超时、join()等待线程终止或者超时、或者I/O处理完毕时,线程重新转入就绪状态。
死亡状态(Dead):线程执行完了或者因异常退出了run()方法,该线程结束生命周期。
ps:另线程阻塞wait与sleep的区别:字面意思理解为,wait为等待,直到被唤醒;而sleep为睡眠,直到设定的时间,所以wait需要手动唤醒,而sleep不需要。
ps:stop与interrupt为中断线程:
interrupt:首先说interrupt, 它没有stop那么的粗暴,因为可以用catch捕捉到InterruptedException这个异常。
stop(这个已经被废弃,线程阻塞):获取的资源,获取的锁都不释放,相当于无限期的等待下去了,因此不推荐的。因为此时线程直接终止,没有catch异常的机会, 无法对线程结束这一行为作出任何补救动作。
无论是interrupt还是stop都是不安全的做法,因为如果我们在线程进行时打开了某些资源,那么这样粗暴的结束资源将无法正确关闭
多线程的几种创建方式:
1.继承Thread类:
package com.xingsfdz.javabase.thread;
/**
*
* @类名称 ThreadDemo.java
* @类描述 <pre>多线程创建方式之继承Thread</pre>
* @作者 xingsfdz xingsfdz@163.com
* @创建时间 2020年4月7日 下午10:27:12
* @版本 1.00
*
* @修改记录
* <pre>
* 版本 修改人 修改日期 修改内容描述
* ----------------------------------------------
* 1.00 xingsfdz 2020年4月7日
* ----------------------------------------------
* </pre>
*/
public class ThreadDemo {
public static void main(String[] args) {
//设置线程名字,不设置则使用默认构造 0 1 2 3...这样
Thread.currentThread().setName("主线程");
MyThread myThread = new MyThread();
myThread.setName("子线程:");
//开启线程
myThread.start();
for(int i = 0;i<5;i++){
System.out.println(Thread.currentThread().getName() + i);
}
}
}
//实现方式之继承Thread
class MyThread extends Thread{
//重写run()方法
public void run(){
for(int i = 0;i < 5; i++){
System.out.println(Thread.currentThread().getName() + i);
}
}
}
//注意:由执行结果可以看出,并非按照顺序,而是通过抢占cpu资源,抢到则执行。
执行结果:
main thread0
子线程:0
子线程:1
子线程:2
main thread1
子线程:3
main thread2
子线程:4
main thread3
main thread4
2.实现Runnable接口
package com.xingsfdz.javabase.thread;
/**
*
* @类名称 RunnableDemo.java
* @类描述 <pre>实现Runnable接口方式</pre>
* @作者 xingsfdz xingsfdz@163.com
* @创建时间 2020年4月7日 下午11:02:54
* @版本 1.00
*
* @修改记录
* <pre>
* 版本 修改人 修改日期 修改内容描述
* ----------------------------------------------
* 1.00 xingsfdz 2020年4月7日
* ----------------------------------------------
* </pre>
*/
public class RunnableDemo {
public static void main(String[] args) {
//设置线程名字,不设置则使用默认构造 0 1 2 3...这样
Thread.currentThread().setName("主线程");
Thread myThread = new Thread(new MyRunnable());//作为参数传入
myThread.setName("实现Runnable接口方式,子线程:");
//开启线程
myThread.start();
for(int i = 0;i<5;i++){
System.out.println(Thread.currentThread().getName() + i);
}
}
}
//实现方式之实现Runnable
class MyRunnable implements Runnable{
//重写run()方法
public void run(){
for(int i = 0;i < 5; i++){
System.out.println(Thread.currentThread().getName() + i);
}
}
}
结果:
主线程0
实现Runnable接口方式,子线程:0
实现Runnable接口方式,子线程:1
实现Runnable接口方式,子线程:2
主线程1
实现Runnable接口方式,子线程:3
实现Runnable接口方式,子线程:4
主线程2
主线程3
主线程4
3.实现Callable接口(带返回值的):
package com.xingsfdz.javabase.thread;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;
/**
*
* @类名称 CallableDemo.java
* @类描述 <pre>带返回值的,实现Callable接口方式</pre>
* @作者 xingsfdz xingsfdz@163.com
* @创建时间 2020年4月7日 下午11:29:20
* @版本 1.00
*
* @修改记录
* <pre>
* 版本 修改人 修改日期 修改内容描述
* ----------------------------------------------
* 1.00 xingsfdz 2020年4月7日
* ----------------------------------------------
* </pre>
*/
public class CallableDemo implements Callable<Integer>{
public static void main(String[] args) {
CallableDemo demo = new CallableDemo();
//执行Callable 方式,需要FutureTask 实现实现,用于接收运算结果
FutureTask<Integer> task = new FutureTask<>(demo);//代码规范,泛型写一边即可。
Thread t = new Thread(task);
t.start();
System.out.println("主线程可以做点别的,等待demo计算的返回结果,下面设置了2秒的等待");
Integer res = 0;
try {
res = task.get();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (ExecutionException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println("通过实现接口方式线程返回结果:" + res);
}
//相当于 Thread里面的run()方法
@Override
public Integer call() throws Exception {
System.out.println("业务处理...");
Thread.sleep(2000);//毫秒
return 1;//返回值
}
}
执行结果:
主线程可以做点别的,等待demo计算的返回结果,下面设置了2秒的等待
业务处理...
通过实现接口方式线程返回结果:1
4.使用线程池:
package com.xingsfdz.javabase.thread;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;
/**
*
* @类名称 ThreadPoolDemo.java
* @类描述 <pre>线程池方式创建线程</pre>
* @作者 xingsfdz xingsfdz@163.com
* @创建时间 2020年4月8日 下午1:50:18
* @版本 1.00
*
* @修改记录
* <pre>
* 版本 修改人 修改日期 修改内容描述
* ----------------------------------------------
* 1.00 xingsfdz 2020年4月8日
* ----------------------------------------------
* </pre>
*/
public class ThreadPoolDemo {
public static void main(String[] args) {
//Executor还有其他很多之类,可以根据 自己需求使用
Executor threadPool = Executors.newFixedThreadPool(5);//创建一个5个线程的线程池
//向线程池里面提交10个任务
for(int i = 0;i < 10; i++){
threadPool.execute(new Runnable(){
@Override
public void run() {
System.out.println(Thread.currentThread().getName());
}
});
}
}
}
运行结果:
pool-1-thread-1
pool-1-thread-5
pool-1-thread-4
pool-1-thread-2
pool-1-thread-3
pool-1-thread-2
pool-1-thread-4
pool-1-thread-5
pool-1-thread-1
pool-1-thread-3
结论:可以发现,任务使用的线程始终是创建的线程池里面的五个线程。而不是十个线程,五个线程循环使用,处理完了,继续处理下一个任务。
5.匿名内部类方式创建(适合只使用一次,不需要知道类名的)
6.定时器
package com.xingsfdz.javabase.thread;
import java.util.Timer;
import java.util.TimerTask;
/**
*
* @类名称 QuartzDemo.java
* @类描述 <pre>多线程创建定时器方式</pre>
* @作者 xingsfdz xingsfdz@163.com
* @创建时间 2020年4月7日 下午11:08:25
* @版本 1.00
*
* @修改记录
* <pre>
* 版本 修改人 修改日期 修改内容描述
* ----------------------------------------------
* 1.00 xingsfdz 2020年4月7日
* ----------------------------------------------
* </pre>
*/
public class QuartzDemo {
public static void main(String [] args){
Timer timer = new Timer();
//使用内部类方式演示
timer.schedule(new TimerTask(){
@Override
public void run() {
System.out.println("使用timertask 方式运行线程...");
}
}, 0, 1000);//单位毫秒,此处标识相隔1s
}
}
使用timertask 方式运行线程...
使用timertask 方式运行线程...
使用timertask 方式运行线程...
使用timertask 方式运行线程...
使用timertask 方式运行线程...
使用timertask 方式运行线程...
使用timertask 方式运行线程...
使用timertask 方式运行线程...
使用timertask 方式运行线程...
使用timertask 方式运行线程...
使用timertask 方式运行线程...
使用timertask 方式运行线程...
使用timertask 方式运行线程...
使用timertask 方式运行线程...
使用timertask 方式运行线程...
使用timertask 方式运行线程...
7.Lambda(可以代替内部类的方式)
package com.xingsfdz.javabase.thread;
import java.util.Arrays;
import java.util.List;
/**
*
* @类名称 LambdaDemo.java
* @类描述 <pre>使用Lambda方式处理</pre>
* @作者 xingsfdz xingsfdz@163.com
* @创建时间 2020年4月8日 下午2:08:31
* @版本 1.00
*
* @修改记录
* <pre>
* 版本 修改人 修改日期 修改内容描述
* ----------------------------------------------
* 1.00 xingsfdz 2020年4月8日
* ----------------------------------------------
* </pre>
*/
public class LambdaDemo {
public static void main(String[] args) {
List<Integer> values = Arrays.asList(10, 20, 30, 40, 50);
//通常局部变量使用基础类型,类变量使用引用类型
int res = add(values);
System.out.println("计算结果:" + res);
}
public static int add (List<Integer> values){
//使用Lambda操作流,对集合values
return values.parallelStream().mapToInt(a -> a).sum();
}
//注意:我们看源码可以看到 这是1.8 在接口Collection里面新增的一个默认方法。 default Stream<E> parallelStream()
//而且通过注释我们也可以看到这是一个并行的方法,不是串行的。
}
运行结果:
计算结果:150
或者:
package com.xingsfdz.demo.util.lambda;
public class TestLamInfaImpl implements TestLamInfa, Runnable{
public static void main(String args[]){
TestLamInfaImpl test = new TestLamInfaImpl();
test.run();//传统的 需要手动实现Runnable
testLambda();//Lambda方式,一句话搞定,不需要手动实现Runnable接口(函数式接口,隐式实现)
}
@Override
public void run() {
// TODO Auto-generated method stub
System.out.println("传统写法的实现Runnable接口的线程");
}
//语法格式一:无参数,无返回值的方式,还有别有参有返回值的方式
public static void testLambda(){
Runnable runnable = () ->System.out.println("Lambda方式启动线程");
runnable.run();
}
}
结果:
传统写法的实现Runnable接口的线程
Lambda方式启动线程
8.Spring实现多线程:
使用java代码配置:Config.java
package com.xingsfdz.demo.util.Thread.spring;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.EnableAsync;
/**
*
* @类名称 Config.java
* @类描述 <pre>一下操作相当于我们前期的spring配置文件的操作</pre>
* @作者 xingsfdz xingsfdz@163.com
* @创建时间 2020年4月8日 下午2:18:47
* @版本 1.00
*
* @修改记录
* <pre>
* 版本 修改人 修改日期 修改内容描述
* ----------------------------------------------
* 1.00 xingsfdz 2020年4月8日
* ----------------------------------------------
* </pre>
*/
//标识为配置文件
@Configuration
//扫描的包
@ComponentScan("com.xingsfdz.demo.util.Thread.spring")
//支持异步,不使用该注解,则不会执行后面的方法
@EnableAsync
public class Config {
}
编写一个服务类:ServiceDemo
package com.xingsfdz.demo.util.Thread.spring;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
//注入容器管理
@Service
public class ServiceDemo {
//注解为支持异步
@Async
public void a(){
while(true){
System.out.println("处理a方法");
try {
Thread.sleep(3000);//单位 毫秒
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
@Async
public void b(){
while(true){
System.out.println("处理b方法");
try {
Thread.sleep(3000);//单位 毫秒
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
运行测试:
package com.xingsfdz.demo.util.Thread.spring;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
/**
*
* @类名称 SpringDemo.java
* @类描述 <pre>spring方式多线程</pre>
* @作者 xingsfdz xingsfdz@163.com
* @创建时间 2020年4月8日 下午2:15:25
* @版本 1.00
*
* @修改记录
* <pre>
* 版本 修改人 修改日期 修改内容描述
* ----------------------------------------------
* 1.00 xingsfdz 2020年4月8日
* ----------------------------------------------
* </pre>
*/
public class SpringDemo {
public static void main(String args[]){
AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(Config.class);//加载配置文件
ServiceDemo sd = ac.getBean(ServiceDemo.class);//获得类实例对象
sd.a();
sd.b();
}
}
运行结果:循环执行表示异步执行成功