线程和任务的关系
- 脱离了任务的线程是没有意义的
new Thread().start();//没有意义
-
线程对象是通过Thread类来创建的
任务是通过Runnable接口来定义的 -
继承Thread类/实现Runnable接口/实现Callable接口
Thread线程执行的流程
线程一定要执行任务吗?
Thread构造器:无参构造就是不需要指定任务
有参构造需要指定线程任务
public Thread(Runnable target)
流程:
- 创建线程对象的时候直接传入它的任务
- 启动线程,start,就绪状态,等待获取CPU资源
- 一旦拿到CPU资源,开始执行它的任务,调用Thread的run方法
public void run(){
if(target != null){
target.run();
}
}
Thread类中定义了一个Runnable target成员变量
用来接受创建线程对象时,传入的任务
- 创建线程对象,传入任务
- 将任务赋值给target成员变量
- 线程执行的时候,操作target成员变量
//1、创建线程对象
Thread thread = new Thread(()->{
for(int i = 0; i < 10; i++){
System.out.println(i);
}
});
//2、启动线程
thread.start();
实现多线程的两种形式对比
接口更好用,Callable用的比较少
名称 | 继承 | 接口 |
---|---|---|
定义 | 创建一个类Mythread extends Thread | Runable1 implements Runnable |
缺点 | 直接将任务的实现写入到了线程类当中,耦合度太高灵活性差 | |
优点 | 可以解耦合 | |
注意 | 重写run() | 重写run() |
继承
public class MyThread extends Thread{
@Override
public void run() {
// TODO Auto-generated method stub
for(int i = 0; i < 10; i++){
System.out.println(i);
}
}
}
接口
//Runnable1
public class Runnable1 implements Runnable{
@Override
public void run() {
for(int i = 0; i < 100; i++)
System.out.println("11111111111111111111111");
}
}
//Runnable2
public class Runnable2 implements Runnable{
@Override
public void run() {
for(int i = 0; i < 100; i++)
System.out.println("222222222222222222222");
}
}
//Main函数
Runnable1 runnable1 = new Runnable1();
Runnable2 runnable2 = new Runnable2();
Thread thread = new Thread(runnable1);
thread.start();
Thread thread2 = new Thread(runnable2);
thread2.start();
//thread1和thread2会交替执行
lambda表达式
lambda函数式编程:可以将方法的实现作为参数进行传递
实现接口的方式:
- 使用匿名内部类的形式来减少类的定义
//匿名内部类
Thread thread3 = new Thread(new Runnable() {
@Override
public void run() {
for(int i = 0; i < 100; i++)
System.out.println("33333333333");
}
});
thread3.start();
- 继续使用lambda进一步简化代码,只把方法的实现进行传值,而不需要关注其他内容
Thread thread4 = new Thread(
()->{
for(int i = 0; i < 100; i++)
System.out.println("44444444444444444444444");
}
);
thread4.start();
线程休眠
Thread t1 = new Thread(()->{
for(int i = 0; i < 5; i++)
System.out.println(1);
});
t1.start();
try {
t1.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
Thread t2 = new Thread(()->{
for(int i = 0; i < 5; i++)
System.out.println(2);
});
t2.start();
//先1,1秒后2
- 创建t1对象并启动
- 让t1对象启动然后休眠1s
- 让t2对象启动
- sleep方法到底是让哪个线程休眠
不在于谁调用sleep,而在于sleep写在哪,写到t1里面才是让t1休眠
跟调用者无关
Thread t1 = new Thread(()->{
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
for(int i = 0; i < 5; i++)
System.out.println(1);
});
t1.start();
Thread t2 = new Thread(()->{
for(int i = 0; i < 5; i++)
System.out.println(2);
});
t2.start();
//先停1秒,然后2最后1