文章目录
1.线程创建的三种方式
1.1继承Thread类
代码示例:
package thread_5_9;
public class Demo03 {
static class MyThread extends Thread{
@Override
public void run() {
//写你的业务代码
//打印当前线程的名称
System.out.println("子线程: "+Thread.currentThread().getName());
}
}
public static void main(String[] args) {
MyThread myThread = new MyThread();
myThread.start();
System.out.println("主线程: "+Thread.currentThread().getName());
}
}
运行结果:
主线程: main
子线程: Thread-0
1.2实现Runnable接口
代码示例:
package thread_5_9;
public class Demo05 {
static class MyRunnable implements Runnable{
@Override
public void run() {
System.out.println("子线程-->"+Thread.currentThread().getName());
}
}
public static void main(String[] args) {
//创建runnable的实现类对象
MyRunnable myRunnable = new MyRunnable();
//创建线程对象
Thread t = new Thread(myRunnable);
//启动线程
t.start();
}
}
运行结果:
子线程-->Thread-0
注意:
我们可以看出,前两种方式没有本质的区别,最终都是调用了Thread类的start方法来开启线程。但是在外观上有很大的区别,第一种方式是继承了Thread类,Java是单继承,继承了Thread类就不能继承其他类;而第二种是实现了Runnable接口,区别于第一种,较为方便
1.3实现Callable接口
示例代码:
package thread_5_9;
import java.util.Random;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;
/*
创建一个带返回值的线程
*/
public class Demo08 {
static class MyCallable implements Callable<Integer> {
@Override
public Integer call() throws Exception {
int num = new Random().nextInt(100);
System.out.println("线程名:"+Thread.currentThread().getName()+" 随机数:"+num);
return num;
}
}
public static void main(String[] args) throws ExecutionException, InterruptedException {
//1.创建Callable的实现类对象
MyCallable myCallable = new MyCallable();
//2.使用FutureTask来接收callable对象
FutureTask<Integer> task = new FutureTask<>(myCallable);
//3.创建线程并设置任务
Thread thread = new Thread(task);
//4.启动线程
thread.start();
int num = task.get();
System.out.println("线程返回结果:"+num);
}
}
运行结果:
线程名:Thread-0 随机数:28
线程返回结果:28
1.4三种方式比较
- Thread:继承方式,不建议使用,因为java是单继承的,继承了Thread类,就不能继承其他类
- Runable:比Thread更加灵活
- Callable:Thread和Runnable都是重写的run()方法并且没有返回值,而Callable是重写的call()方法并且有返回值,返回值用FutureTask来接收
- 不需要返回值时就用Runnable,需要返回值时就用Callable
- Thread类是实现Runnable,Callable封装成FutureTask,FutureTask实现RunnableFuture,RunnableFuture继承Runnable,所以Callable也算是一种Runnable,所以三种实现方式本质上都是Runnable实现
2.线程分组
示例:
package thread_5_9;
public class App {
public static void main(String[] args) {
ThreadGroup threadGroup = new ThreadGroup("线程分组");
Thread t1 = new Thread(threadGroup, new Runnable() {
@Override
public void run() {
try {
Thread.sleep(1000);
System.out.println("选手1到达终点了");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
t1.start();
Thread t2 = new Thread(threadGroup, new Runnable() {
@Override
public void run() {
try {
Thread.sleep(1200);
System.out.println("选手2到达终点了");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
t2.start();
//等待分组中的所有线程执行结束
while(threadGroup.activeCount() != 0){
}
System.out.println("比赛结束了");
}
}
运行结果:
选手1到达终点了
选手2到达终点了
比赛结束了
3.Thread类及常见方法
3.1Thread类的常见构造方法
3.2Thread类的常见属性
示例:
package thread_5_9;
/*
演示线程的常见方法
*/
public class Demo11 {
public static void main(String[] args) throws InterruptedException {
Thread t1 = new Thread(new Runnable() {
@Override
public void run() {
Thread thread = Thread.currentThread();
System.out.println("线程ID:"+thread.getId());
System.out.println("线程名称:"+thread.getName());
System.out.println("线程状态:"+thread.getState());
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"张三");
t1.start();
Thread.sleep(100);
System.out.println("t1线程状态:"+t1.getState());
System.out.println("优先级:"+t1.getPriority());
System.out.println("是否是守护线程:"+t1.isDaemon());
System.out.println("是否存活:"+t1.isAlive());
System.out.println("是否被中断:"+t1.isInterrupted());
}
}
运行结果:
线程ID:11
线程名称:张三
线程状态:RUNNABLE
t1线程状态:TIMED_WAITING
优先级:5
是否是守护线程:false
是否存活:true
是否被中断:false
3.2.1线程优先级
- 线程的优先级取值范围为1到10,默认优先级为5
- 线程的优先级越高,CPU执行权重越高
示例:
package thread_5_9;
public class Demo12 {
public static void main(String[] args) {
Thread t1 = new Thread(new Runnable() {
@Override
public void run() {
int a = 0;
for (int i = 0; i < 10_0000_0000; i++) {
a++;
}
System.out.println("线程t1执行结束");
}
});
t1.start();
Thread t2 = new Thread(new Runnable() {
@Override
public void run() {
int a = 0;
for (int i = 0; i < 10_0000_0000; i++) {
a++;
}
System.out.println("线程t2执行结束");
}
});
t2.start();
Thread t3 = new Thread(new Runnable() {
@Override
public void run() {
int a = 0;
for (int i = 0; i < 10_0000_0000; i++) {
a++;
}
System.out.println("线程t3执行结束");
}
});
t3.start();
t1.setPriority(5); //默认优先级
t2.setPriority(1); //优先级最低
t3.setPriority(10); //优先级最高
}
}
运行结果:
线程t3执行结束
线程t1执行结束
线程t2执行结束
3.2.2线程分类
- 用户线程:默认创建的线程就是用户线程
- 守护线程(后台线程):守护线程是为用户线程服务,当用户线程执行完成后,守护线程也会跟随用户线程一起结束
注意事项:
1.守护线程的设置必须放在开启线程之前
2.在守护线程中创建的线程默认是守护线程
守护线程的使用场景:
1.垃圾回收器
2.监控检测任务
示例:
package thread_5_9;
public class Demo13 {
public static void main(String[] args) {
Thread t1 = new Thread(new Runnable() {
@Override
public void run() {
for (int i = 0; i < 10; i++) {
System.out.println("i: "+i);
try {
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
});
//t1.setDaemon(true);
t1.start();
System.out.println("t1是否为守护线程:"+t1.isDaemon());
System.out.println("main是否为守护线程:"+Thread.currentThread().isDaemon());
}
}
运行结果:
t1是否为守护线程:false
main是否为守护线程:false
i: 0
i: 1
i: 2
i: 3
i: 4
i: 5
i: 6
i: 7
i: 8
i: 9
去掉注释后:
t1是否为守护线程:true
main是否为守护线程:false
i: 0
3.2.3线程练习
题目:
使用两个线程打印出AABBCCDD,每个线程只能打印ABCD
代码示例:
package thread_5_9;
public class Demo14 {
public static void main(String[] args) {
Thread t1 = new Thread(new Runnable() {
@Override
public void run() {
String str = "ABCD";
for(char c:str.toCharArray()){
System.out.print(c);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
});
t1.start();
Thread t2 = new Thread(new Runnable() {
@Override
public void run() {
String str = "ABCD";
for(char c:str.toCharArray()){
System.out.print(c);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
});
t2.start();
}
}
3.3启动一个线程-start( )
run( ) vs start( )
- run方法是一个对象的普通方法,它使用的是主线程来执行任务的
- start方法是线程的开启方法,它使用新的线程来执行任务
- run方法可以执行多次,start方法只能执行一次
代码示例:
package thread_5_9;
public class Demo16 {
public static void main(String[] args) {
Runnable r = new Runnable() {
@Override
public void run() {
System.out.println("线程名:"+Thread.currentThread().getName());
}
};
Thread thread = new Thread(r);
thread.run();
thread.run();
thread.run();
thread.start();
thread.start();
}
}
运行结果:
线程名:main
线程名:main
线程名:main
线程名:Thread-0
Exception in thread "main" java.lang.IllegalThreadStateException
at java.lang.Thread.start(Thread.java:708)
at thread_5_9.Demo16.main(Demo16.java:18)
3.4中断一个线程-interrupt()
1.自定义全局标识来实现中断
示例:
package thread_5_9;
public class Demo17 {
//全局变量
private static boolean flag = false;
public static void main(String[] args) throws InterruptedException {
Thread t1 = new Thread(()->{
while(!flag){
//休眠线程
try {
Thread.sleep(100);
System.out.println("我正在转账……");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("差点误了大事");
},"张三");
t1.start();
//休眠主线程一段时间
Thread.sleep(310);
//停止交易
System.out.println("停止交易……");
flag = true;
}
}
运行结果:
我正在转账……
我正在转账……
我正在转账……
停止交易……
我正在转账……
差点误了大事
2.使用Thread中的interrupted来中断
代码示例:
package thread_5_9;
public class Demo18 {
public static void main(String[] args) throws InterruptedException {
Thread t1 = new Thread(()->{
while(!Thread.interrupted()){
try {
Thread.sleep(100);
System.out.println("我正在转账……");
} catch (InterruptedException e) {
//e.printStackTrace();
break;
}
}
System.out.println("差点误了大事");
});
t1.start();
Thread.sleep(310);
System.out.println("停止交易");
t1.interrupt();
}
}
运行结果:
我正在转账……
我正在转账……
我正在转账……
停止交易
差点误了大事
小结:
- 使用interrupt可以及时的终止线程,而使用自定义的全局变量终止线程的方式,比较温柔不能及时的终止
- 两种判断线程状态的方式。第一种:Thread.interrupted()
第二种:Thread.currentThread.isInterrupted() - Thread.interrupted()第一次接收到终止状态是true,之后会将状态复位(恢复为false),即调用后清除标志位
- Thread.currentThread.isInterrupted()只用来获得线程状态,不会复位,不会清除标志位
3.5等待一个线程-join()
有时,我们需要等待一个线程完成它的工作后,才能进行自己的下一步工作。例如,张三只有等李四转账成功,才决定是否存钱,这时我们需要一个方法明确等待线程的结束。
代码示例:
package thread_5_9;
public class Demo21 {
public static void main(String[] args) throws InterruptedException {
//定义一个公共的任务
Runnable runnable = new Runnable() {
@Override
public void run() {
String name = Thread.currentThread().getName();
System.out.println(name+"上班");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(name+"下班");
}
};
Thread t1 = new Thread(runnable,"张三");
Thread t2 = new Thread(runnable,"李四");
t1.start();
t1.join();
t2.start();
}
}
运行结果:
张三上班
张三下班
李四上班
李四下班
3.6获得当前线程引用
代码示例:
package thread_5_9;
public class Demo03 {
static class MyThread extends Thread{
@Override
public void run() {
//写你的业务代码
//打印当前线程的名称
System.out.println("子线程: "+Thread.currentThread().getName());
}
}
public static void main(String[] args) {
MyThread myThread = new MyThread();
myThread.start();
System.out.println("主线程: "+Thread.currentThread().getName());
}
}
3.7线程休眠
代码示例:
package thread_5_9;
import java.util.concurrent.TimeUnit;
public class Demo01 {
public static void main(String[] args) throws InterruptedException {
String str = "即佛牌精品店是陈建平你师父或是看到破开始比较搜狐号奇偶皮卡食品厂模拟配件什么票不能说明课程编号搜索" +
"键盘你视频女排说明书牌是什么吗";
for(char c:str.toCharArray()){
System.out.print(c);
//线程休眠方式一:Thread.sleep()
//Thread.sleep(200);
//线程休眠方式二:
//TimeUnit.SECONDS.sleep(1);
//线程休眠方式三:
Thread.sleep(TimeUnit.SECONDS.toMillis(1));
}
}
}
4.线程的状态
4.1线程的所有状态
线程的状态是一个枚举类型 Thread.State
package thread_5_9;
public class Demo22 {
public static void main(String[] args) {
for(Thread.State state:Thread.State.values()){
System.out.println(state);
}
}
}
运行结果:
NEW
RUNNABLE
BLOCKED
WAITING
TIMED_WAITING
TERMINATED
4.2线程状态图
示例:
package thread_5_9;
public class Demo23 {
public static void main(String[] args) throws InterruptedException {
Thread t1 = new Thread(()->{
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
});
System.out.println("执行之前:"+t1.getState());
t1.start();
System.out.println("start之后:"+t1.getState());
Thread.sleep(100);
System.out.println("休眠100毫秒后:"+t1.getState());
//等待t1执行结束
t1.join();
System.out.println("t1执行结束后:"+t1.getState());
}
}
运行结果:
执行之前:NEW
start之后:RUNNABLE
休眠100毫秒后:TIMED_WAITING
t1执行结束后:TERMINATED
4.3yield()方法
yield() 大公无私,让出 CPU的执行权,让线程进入就绪状态,等待重新获取CPU执行时间