- 进程:是指一个内存中正在运行的应用程序。每个进程都有自己的内存空间和系统资源。进程是系统资源分派和调度的基本单位。
- 线程:线程是进程的一个实体,是操作系统中独立运行的基本单位,只在运行时拥有少量资源,线程是CPU调度和分派的基本单位;
- 单线程:一个进程如果只有一条执行路径,则称为单线程程序;
- 多线程:一个进程有多个执行路径,则称为多线程程序;
- 进程和线程的区别总结:
- 进程是系统资源分派和调度的基本单位,而线
程是CPU调度和分派的基本单位; - 进程拥有独立资源而线程几乎不拥有独立资源。
- 进程是系统资源分派和调度的基本单位,而线
- 多线程的实现方式:
- 继承Thread类:
public class ThreadDemo_inherit {
public static void main(String[] args) {
//获得当前线程
Thread mainThread=Thread.currentThread();
System.out.println("线程名称:"+mainThread.getName());
//3.启动线程(start())
Thread thread=new MyThread();
thread.start();
}
}
//1.定义一个myThread类继承Thread类
class MyThread extends Thread{
//2.在myThread类中重写run方法创建myThread类的对象
@Override
public void run() {
//具体业务代码
System.out.println("你好线程");
Thread thread=Thread.currentThread();
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("线程名称:"+thread.getName());
}
}
/*
线程名称:main
你好线程
线程名称:Thread-0
*/
-
- 实现runnable接口:
public class ThreadDemo_runn {
public static void main(String[] args) {
//3.创建实现Runnable接口的类的对象
MyThread2 myThread2=new MyThread2();
//4.创建线程,把myrunnable对象作为构造方法的参数
Thread thread=new Thread(myThread2);
thread.start();
}
}
//1.定义类实现Runnable接口
class MyThread2 implements Runnable{
//2.重写方法
@Override
public void run() {
//具体业务代码
Thread thread=Thread.currentThread();
System.out.println("线程执行"+thread.getName());
}
}
//线程执行Thread-0
-
- 2的另一种写法:匿名内部类
public class ThreadDemo_runn2 {
public static void main(String[] args) {
//匿名内部类
Thread thread=new Thread(new Runnable() {
@Override
public void run() {
//具体业务代码
Thread t=Thread.currentThread();
System.out.println("线程执行"+t.getName());
}
});
//启动线程
thread.start();
}
}
-
- 2的另一种写法:lambda表达式
public class ThreadDemo_runn3 {
public static void main(String[] args) {
// lambda表达式
Thread thread=new Thread(()->{
//具体业务代码
Thread t=Thread.currentThread();
System.out.println("线程执行:"+t.getName());
});
thread.start();
}
}
-
- 创建线程并初始化
public class ThreadDemo_5 {
public static void main(String[] args) {
//创建线程并初始化
Thread thread=new Thread() {
@Override
public void run() {
Thread t = Thread.currentThread();
System.out.println("线程执行:" + t.getName());
}
};
thread.start();
}
}
以上五个创建线程的方式有一个共同的问题,就是没返回值,也就是当线程执行完,主线程是无法拿到新线程的执行结果的,因此有了以下方法
-
- callable+Future:
import java.util.Random;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;
/**
* 实现callable新建线程
*/
public class ThreadDemo_6 {
public static void main(String[] args) throws ExecutionException, InterruptedException {
//创建Callable实例
MyCallable myCallable = new MyCallable();
//用于接收Callable结果的对象
FutureTask<Integer> futureTask = new FutureTask<>(myCallable);
//创建新线程
Thread thread=new Thread(futureTask);
//启动线程
thread.start();
//接收新线程的结果
int result = futureTask.get();
System.out.println(Thread.currentThread().getName()+"---新线程的返回结果为:"+result);
}
}
/**
* Callable<T>泛型可以是任意类型
*/
class MyCallable implements Callable<Integer>{
@Override
public Integer call() throws Exception {
//生成随机数0—9
int randomNum=new Random().nextInt(10);
System.out.println(Thread.currentThread().getName()+"---随机数:"+randomNum);
return randomNum;
}
}
/**
* Thread-0---随机数:4
* main---新线程的返回结果为:4
*/
-
- 6+lambda:
import java.util.Random;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;
public class ThreadDemo_7 {
public static void main(String[] args) throws ExecutionException, InterruptedException {
FutureTask<String> futureTask = new FutureTask<>(new Callable<String>() {
@Override
public String call() throws Exception {
//新线程执行的业务代码
String[] arr=new String[]{"java","woaijava","Thread"};
String result=arr[new Random().nextInt(3)];
System.out.println(Thread.currentThread().getName()+"---字符串:"+result);
return result;
}
});
//创建新线程
Thread thread=new Thread(futureTask);
//启动线程
thread.start();
//接收新线程的结果
String result = futureTask.get();
System.out.println(Thread.currentThread().getName()+"---新线程的返回结果为:"+result);
}
}
/**
* Thread-0---字符串:woaijava
* main---新线程的返回结果为:woaijava
*/
- 线程常用构造方法
方法 | 说明 |
---|---|
Thread() | 创建线程对象 |
Thread(Runnable target) | 使用Runnable对象创建对象 |
Thread(String name) | 创建线程对象并命名 |
Thread(Runnable target, String name) | 使⽤ Runnable 对象创建线程对象,并命名 |
Thread(ThreadGroup group, Runnable target) | 线程可以被⽤来分组管理,分好的组即为线程 组 |
Thread t1 = new Thread();
Thread t2 = new Thread(new MyRunnable());
Thread t3 = new Thread("这是我的名字");
Thread t4 = new Thread(new MyRunnable(), "这是我的名字");
- 线程分组使用:
import java.util.Random;
public class ThreadDemo_10 {
public static void main(String[] args) {
// 1.创建一个线程分组
ThreadGroup group=new ThreadGroup("thread-group");
// 2.定义一个公共的任务
Runnable runTask=new Runnable(){
@Override
public void run() {//业务
int num=new Random().nextInt(3);
//n秒后到达终点
try {
Thread.sleep(num*1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
//得到执行此方法的线程
Thread t=Thread.currentThread();
System.out.println(t.getName()+"--选手到达终点"+num+"s");
}
};
// 3.线程(运动员)
Thread t1=new Thread(group,runTask);
Thread t2=new Thread(group,runTask);
Thread t3=new Thread(group,runTask);//**
t1.start();
t2.start();
t3.start();
//所有人到达终点宣布成绩
while(group.activeCount()!=0){
}
System.out.println("宣布成绩");
}
}
** 如果在主线程启动新的线程 那么新线程和主线程各自独立运行 即要等所有人到达终点后宣布成绩
- 线程常用属性:
属性 | 获取⽅法 |
---|---|
1ID | getId() |
2名称 | getName() |
3状态 | getState() |
4优先级 | getPriority() |
5是否守护(后台)线程 | isDaemon() |
6是否存活 | isAlive() |
7是否被中断 | isInterrupted() |
8让出执⾏权 | yield() |
-
- ID 是线程的唯⼀标识,不同线程不会重复,是动态分配名称是各种调试⼯具⽤到,可能重复
- 名称可能会重复,是各种调试⼯具⽤到
- 状态表示线程当前所处的⼀个情况,优先级⾼的线程理论上来说更容易被调度到
public class ThreadDemo_11 {
public static void main(String[] args) throws InterruptedException {
Thread t=new Thread(new Runnable() {
@Override
public void run() {
Thread tt=Thread.currentThread();
System.out.println("线程状态2:"+tt.getState());
}
});
//打印线程的状态
System.out.println("线程状态:"+t.getState());
t.start();
//再次打印线程状态
Thread.sleep(1000);
System.out.println("线程状态3:"+t.getState());
}
}
/**
* 线程状态:NEW
* 线程状态2:RUNNABLE
* 线程状态3:TERMINATED
*/
-
- 多个线程的执行是随机的,优先级高的理论上说更容易被调度到(高优先级获取cpu时间片的概率高)
public class ThreadDemo_priority {
private final static int MAXCOUNT=1000;
public static void main(String[] args) {
Thread t1=new Thread(new Runnable() {
@Override
public void run() {
Thread t=Thread.currentThread();
int priority=t.getPriority();
for (int i = 0; i < MAXCOUNT; i++) {
System.out.println(t.getName()+"--优先级:"+priority);
}
}
},"线程1");
t1.setPriority(10);
Thread t2=new Thread(new Runnable() {
@Override
public void run() {
Thread t=Thread.currentThread();
int priority=t.getPriority();
for (int i = 0; i < MAXCOUNT; i++) {
System.out.println(t.getName()+"--优先级:"+priority);
}
}
},"线程2");
t2.setPriority(Thread.NORM_PRIORITY);
Thread t3=new Thread(new Runnable() {
@Override
public void run() {
Thread t=Thread.currentThread();
int priority=t.getPriority();
for (int i = 0; i < MAXCOUNT; i++) {
System.out.println(t.getName()+"--优先级:"+priority);
}
}
},"线程3");
t3.setPriority(Thread.MIN_PRIORITY);
t1.start();
t2.start();
t3.start();
}
}
-
- 关于守护线程(后台线程),需要记住⼀点:JVM会在⼀个进程的所有⾮后台(守护)线程结束后,才会结束运⾏。
守护线程示例
public class ThreadDemo_Daemon {
public static void main(String[] args) throws InterruptedException {
Thread t=Thread.currentThread();
System.out.println(t.getName()+"---是否是守护线程:"+t.isDaemon());
Thread t1=new Thread(()->{
Thread tt1=Thread.currentThread();
System.out.println(tt1.getName()+"---是否是守护线程:"+tt1.isDaemon());
Thread t2=new Thread(()->{
Thread tt2=Thread.currentThread();
System.out.println(tt2.getName()+"---是否是守护线程:"+tt2.isDaemon());
},"子线程的子线程");
t2.start();
},"子线程");
t1.setDaemon(true);
t1.start();
//将主线程休眠
Thread.sleep(1000);
}
}
/**
* main---是否是守护线程:false
* 子线程---是否是守护线程:true
* 子线程的子线程---是否是守护线程:true
*/
-
-
- 对比用户线程和守护线程
-
public class ThreadDemo_Daemon2 {
public static void main(String[] args) throws InterruptedException {
// userThread();
daemonThread();
Thread.sleep(500);
}
private static void daemonThread() {
Thread t=new Thread(()->{
for (int i = 0; i < 10; i++) {
System.out.println("执行:"+i);
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
t.setDaemon(true);
t.start();
}
/**
* 用户线程
*/
private static void userThread() {
Thread t=new Thread(()->{
for (int i = 0; i < 10; i++) {
System.out.println("执行:"+i);
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
t.start();
}
}
/**
* 执行:0
* 执行:1
* jvm不会等待守护线程执行完再退出,只会等待用户线程执行完才会退出
*/
-
-
- 线程的分类:
-
用户线程( main 默认用户线程):默认情况下在用户线程下创建也是用户线程
-
守护线程(后台线程):守护线程是为⽤户线程服务的,⽤户线程全部结束之后,守护线程会跟随结束 ,默认情况下在守护线程下创建的子线程也是守护线程。
-
start方法的调用在设置类型之后
-
-
- 线程的分类:
-
是否存活,即简单的理解,为 run ⽅法是否运⾏结束了
1.
-
public class ThreadDemo_isAlive {
public static void main(String[] args) throws InterruptedException {
Thread t=new Thread(()->{
//得到当前线程
Thread tt=Thread.currentThread();
System.out.println("线程是否存活2:"+tt.isAlive());
});
System.out.println("线程是否存活:"+t.isAlive());
t.start();
System.out.println("线程是否存活3:"+t.isAlive());
Thread.sleep(1000);
System.out.println("线程是否存活4:"+t.isAlive());
}
}
/**
* 线程是否存活:false
* 线程是否存活3:true
* 线程是否存活2:true
* 线程是否存活4:false
*/
-
-
- 利用isAlive()方法确认线程是否执行完毕:
-
public class ThreadDemo_isAlive2 {
public static void main(String[] args) {
Thread t=new Thread(()->{
for (int i = 0; i < 10; i++) {
try {
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("线程执行完了");
});
t.start();
while(t.isAlive()){
}
System.out.println("确认线程执行完了");
}
}
/**
* 线程执行完了
* 确认线程执行完了
*/
-
-
- .isAlive() 类似于 join()
join() 线程等待:等待某个线程执行完之后,再执行后续的代码 优点:写法优雅 阻塞等待 消耗系统资源少
join() :等待线程结束
join(long millis):最多等待多少毫秒
- .isAlive() 类似于 join()
-
/**
* join示例
*/
public class ThreadDemo_join {
public static void main(String[] args) throws InterruptedException {
Thread t1=new Thread(()->{
//t1开始上班
System.out.println("t1开始上班");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
//
System.out.println("t1下班");
});
t1.start();
//等待t1执行完,再执行后面的代码
t1.join();
Thread t2=new Thread(()->{
//t开始上班
System.out.println("t2开始上班");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
//
System.out.println("t2下班");
});
t2.start();
}
}
-
- 线程终止的方法
- 自定义标识符终止线程
- 线程终止的方法
package ThreadFun;
public class ThreadInterrupt {
private volatile static boolean flag=false;
public static void main(String[] args) throws InterruptedException {
Thread thread=new Thread(()->{
while(!flag){
System.out.println("正在转帐");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("a 差点误了大事");
});
thread.start();
Thread.sleep(2000);
//终止线程
System.out.println("有内鬼终止交易~");
flag=true;
}
}
-
-
- 调用isInterrupted()来终止线程
-
package ThreadFun;
public class ThreadInterrupt2 {
public static void main(String[] args) throws InterruptedException {
Thread thread=new Thread(()->{
// while(!Thread.interrupted()){
while(!Thread.currentThread().isInterrupted()){
System.out.println("正在转帐。。");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// e.printStackTrace();
break;
}
}
System.out.println("啊啊差点误了大事");
});
thread.start();
Thread.sleep(3000);
thread.interrupt();
System.out.println("有内鬼。。");
}
}
/**
* 正在转帐。。
* 正在转帐。。
* 有内鬼。。
* 啊啊差点误了大事
*/
-
-
- interrupt() 需要配合Thread.interrupted/Thread.currentThread().isInterrupted()使用
- isInterrupted()和interrupted的区别:
- interruped()属于静态方法,所有程序都可以直接调用的方法,而isInterrupted()属于某个实例的方法
- interrupted()在使用完会重置,而另外一个不会
- isInterrupted()和interrupted的区别:
- interrupt() 需要配合Thread.interrupted/Thread.currentThread().isInterrupted()使用
-
-
- yield():出让cpu执行权,让线程调度器重新调度线程,但还是有一定的几率再依次调用到出让cpu的线程上,这一次就会执行线程的方法了,因为yield()已经执行
public class ThreadDemoYield {
public static void main(String[] args) {
Thread t1=new Thread(()->{
Thread cThread=Thread.currentThread();
for (int i = 0; i < 100; i++) {
//让出cpu执行权
Thread.yield();
System.out.println("执行了线程:"+cThread.getName());
}
},"张三");
t1.start();
new Thread(()->{
Thread cThread=Thread.currentThread();
for (int i = 0; i < 100; i++) {
System.out.println("执行了线程:"+cThread.getName());
}
},"李四").start();
}
}
- 休眠当前线程:
- 使⽤ sleep 休眠
public class ThreadDemoSleep {
public static void main(String[] args) throws InterruptedException {
Thread thread=new Thread(()->{
try {
Thread.sleep(1000*60*60);
} catch (InterruptedException e) {
System.out.println("我接收到了终止指令");
// e.printStackTrace();
}
});
thread.start();
Thread.sleep(1000);
System.out.println("子线程终止 thread");
thread.interrupt();
}
}
/**
* 子线程终止 thread
* 我接收到了终止指令
*/
-
- 使⽤ TimeUnit 休眠
//使用TimeUtil休眠
public class ThreadTimeUtil {
public static void main(String[] args) throws InterruptedException {
System.out.println("主线程开始了"+ LocalDateTime.now());
TimeUnit.SECONDS.sleep(1);
// Thread.sleep(1000*60*60*24);
System.out.println("主线程又开始执行了"+LocalDateTime.now());
}
}
//主线程开始了2022-04-01T18:17:44.074
//主线程又开始执行了2022-04-01T18:17:45.086