Java线程(上)
1.线程
1.什么是进程
一个正在执行中的程序就是一个进程,系统会为这个进程发配独立的【内存资源】。进程是程序的一次执行过程,它有自己独立的生命周期,它会在启动程序时产生,运行程序时存在,关闭程序时消亡。
2.什么是线程
线程是由进程创建的,是进程的一个实体,是具体干活的人,一个进程可能有多个线程。线程不独立分配内存,而是共享进程的内存资源,线程可以共享CPU的计算资源。
一个进程可以有多个线程,线程是程序执行中一个单一的顺序控制流程,现在线程才是程序执行流的最小单元,各个线程之间共享程序的内存空间(也就是所在进程的内存空间),上下文切换速度也高于进程。
3.上下文切换
从任务管理器中我们可以看到,这台电脑上运行着263个进程,3670个线程,但是我只有16个逻辑内核,这足以证明对于每一个逻辑内核他在执行的过程当中也是按照时间片执行不同的线程的。
但是这里有几个问题:
- 我们的进程可以直接创建、调度线程吗?QQ运行了一会说我累了,不想执行了,微信你来吧!这显然是不合理的。
- QQ执行了一会,不执行了,那等其他线程执行完成之后,又轮上QQ了,QQ还能记得刚才运行到哪里了吗?
针对第一个问题,任何一个用户的线程是不允许调度其他的线程的,所有的线程调用都由一个大管家统一调度,这个大管家就是系统内核。
第二个问题,下一个执行时想要知道上一次的执行结果,就必须在上一次执行之后,讲运行时的数据进行保存,那么整个过程就出来了。

*** 什么是上下文:***
其中,用户线程执行的过程我们称之为【用户态】,内核调度的状态称之为【内核态】,每一个线程运行时产生的数据我们称之为【上下文】,线程的每次切换都需要进行用户态到内核态的来回切换,同时伴随着上下文的切换,是一个比较消耗资源的操作,所以一个计算机当中不是线程越多越好,线程如果太多也是有可能拖垮整个系统的。
4. 并行与并发
- 并行:两个任务同时进行,比如同一时间内A在聊天,B在打游戏
- 并发:两个任务请求同时进行,但是处理器只会去处理其中的一个,根据时间片轮转(涉及一些调度算法),让我们看起来是同时进行的
2. Java中的线程
1. Java中实现线程的模型
- 创建任务: 可以发现Thread构造方法中需要传入Runnable的实现类,所以我们可以先去实现Runnable
- 创建线程: 我们创建Thread对象
- 启动线程: 调用Thread对象的.start()方法启动
public class Demo2 {
public static void main(String[] args) {
//任务封装
Task task = new Task();
//线程封装
Thread thread = new Thread(task);
//线程启动
thread.start();
}
}
class Task implements Runnable{
@Override
public void run() {
System.out.println("work!");
}
}当然我们发现不止这样实现线程,我们还可以下面这样实现
2.Thread实现多线程
- Thread类当中也有run()方法,我们可以手写一个类继承Thread然后重写run()方法实现多线程
public class Demo3 { public static void main(String[] args) throws InterruptedException { WorkThread workThread=new WorkThread(); workThread.start(); Thread.sleep(1000); System.out.println("主线程结束"); } } class WorkThread extends Thread{ static int b=0; @Override public void run() { for(int i=0;i<5;i++){ System.out.println(b++); } } }
3. Runnable实现多线程
- Runnable就是给线程的任务,那么我们去重写这个任务分配给线程也是一种实现多线程的一种方式
public class Demo4 {
public static void main(String[] args) {
Thread thread=new Thread(new ImRunnable());
thread.start();
}
}
class ImRunnable implements Runnable{
@Override
public void run() {
System.out.println("Runnable重写实现任务");
}
}4. Lammbda实现多线程
public class Demo5 {
public static void main(String[] args) {
new Thread(()-> System.out.println("线程1")).start();
new Thread(()-> System.out.println("线程2")).start();
System.out.println("主线程");
}
}执行结果:

5. 实现Callable和FutureTask
- 实现Callable接口,重写call(),封装线程要执行的任务
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;
public class Demo6 {
public static void main(String[] args) throws ExecutionException, InterruptedException {
FutureTask futureTask=new FutureTask(new MyCallable());
new Thread(futureTask).start();
System.out.println(futureTask.get());//获得返回结果
System.out.println("主线程");
}
}
class MyCallable implements Callable{
@Override
public String call() throws Exception {
Thread.sleep(1000);
return "hello,线程!";
}
}6. 获取线程中的一些属性
1. 设置获取当前线程的名字
public class Demo7 {
public static void main(String[] args) {
Thread t1=new Thread("线程1"){
@Override
public void run() {
System.out.println(this.getName());//获取当前线程名
}
};
Thread t2=new Thread(){
@Override
public void run() {
System.out.println(this.getName());//获取当前线程名
}
};
t2.setName("线程2");
t1.start();
t2.start();
}
}2. 获取当前线程的引用对象
public class Demo8 {
public static void main(String[] args) {
Thread thread = Thread.currentThread();//main线程
System.out.println(thread.getId());
System.out.println(thread.getName());
System.out.println(thread.getThreadGroup());
System.out.println(thread.getPriority());
System.out.println(thread.getState());
Thread thread1 = new Thread("线程1"){
@Override
public void run() {
System.out.println(Thread.currentThread().getName());
System.out.println(Thread.currentThread().getId());
}
};
thread1.start();
}
}
7. 线程休眠
- 使用sleep方法,sleep()参数是休眠的毫秒时间
- 可以使用return进行返回,可以使用程序逻辑判断返回,还可以使用interrupt(),但是会抛出异常,所以建议前两者,还有个stop()以及被弃用,不推荐使用 线程中断:
public class Demo9 { public static void main(String[] args) throws InterruptedException { Thread t1= new Thread(){ @Override public void run() { System.out.println("线程1开始"); try { Thread.sleep(1000); System.out.println("继续执行"); } catch (InterruptedException e) { throw new RuntimeException(e); } } }; t1.start(); Thread.sleep(500); t1.interrupt(); System.out.println("主线程执行"); } }
- 如果我们想暂停线程呢?
public static void main(String[] args) { Thread t = new Thread(() -> { System.out.println("线程开始运行!"); Thread.currentThread().suspend(); //暂停此线程 System.out.println("线程继续运行!"); }); t.start(); try { Thread.sleep(3000); //休眠3秒,一定比线程t先醒来 t.resume(); //恢复此线程 } catch (InterruptedException e) { e.printStackTrace(); } }

8.守护线程
- 什么是守护线程:Java提供两种类型的线程:
用户线程和守护程序线程。守护线程旨在为用户线程提供服务,并且仅在用户线程运行时才需要。 - 不要把操作系统重的守护进程和守护线程相提并论!
- 守护进程在后台运行运行,不需要和用户交互,本质和普通进程类似。而守护线程就不一样了,当其他所有的非守护线程结束之后,守护线程自动结束,也就是说,Java中所有的线程都执行完毕后,守护线程自动结束,因此守护线程不适合进行IO操作,只适合打打杂:
1. 守护线程的使用环境: 守护线程对于后台支持任务非常有用,例如垃圾收集,释放未使用对象的内存以及从缓存中删除不需要的条目。大多数JVM线程都是守护线程。在比如qq等等聊天软件,主程序是非守护线程,而所有的聊天窗口是守护线程,当在聊天的过程中,直接关闭聊天应用程序时,聊天窗口也会随之关。包括word中我们在书写文字的时候,还有线程帮我们进行拼写检查,这都是守护线程。
2. 守护线程的创建:
public class Demo10 { public static void main(String[] args) throws InterruptedException { Thread t = new Thread(() -> { while (true){ try { System.out.println("守护线程:程序正常运行中..."); Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } } }); t.setDaemon(true); //设置为守护线程(必须在开始之前,中途是不允许转换的) t.start(); for (int i = 0; i < 5; i++) { System.out.println("用户线程正在进行"); Thread.sleep(1000); } } } ``` 注意:在守护线程中产生的新线程也是守护的!
9. Join()方法
- 当我们希望一个线程等待另一个线程执行完成后再继续进行,我们可以使用
join()方法来实现线程的加入:public class Demo11 { public static void main(String[] args) { Thread t1 = new Thread(() -> { System.out.println("线程1开始运行!"); for (int i = 0; i < 50; i++) { try { Thread.sleep(100); } catch (InterruptedException e) { throw new RuntimeException(e); } System.out.println("1打印:"+i); } System.out.println("线程1结束!"); }); Thread t2 = new Thread(() -> { System.out.println("线程2开始运行!"); for (int i = 0; i < 50; i++) { System.out.println("2打印:"+i); if(i == 10){ try { System.out.println("线程1加入到此线程!"); t1.join(); //在i==10时,让线程1加入,先完成线程1的内容,在继续当前内容 } catch (InterruptedException e) { e.printStackTrace(); } } } }); t1.start(); t2.start(); } }我们发现t2运行到条件后,然后t1加入,t1加入之后执行到t1结束才执行t2! ```
10. yield()线程礼让
- 将cpu资源让位给同级的线程
public class Demo11 { public static void main(String[] args) { Thread t1 = new Thread(() -> { System.out.println("线程1开始运行!"); for (int i = 0; i < 50; i++) { if(i % 5 == 0) { System.out.println("让位!"); Thread.yield(); } System.out.println("1打印:"+i); } System.out.println("线程1结束!"); }); Thread t2 = new Thread(() -> { System.out.println("线程2开始运行!"); for (int i = 0; i < 50; i++) { System.out.println("2打印:"+i); } }); t1.start(); t2.start(); } }
我们会发现更多的是执行线程2,所以线程2会先结束,但在执行的过程中只会让位但不会一直让!而join则是等待结束!
11.优先级别
实际上,Java程序中的每个线程并不是平均分配CPU时间的,为了使得线程资源分配更加合理,Java采用的是抢占式调度方式,优先级越高的线程,优先使用CPU资源!我们希望CPU花费更多的时间去处理更重要的任务,而不太重要的任务,则可以先让出一部分资源。线程的优先级一般分为以下三种:
- MIN_PRIORITY 最低优先级
- MAX_PRIORITY 最高优先级
- NOM_PRIORITY 常规优先级
public static void main(String[] args) {
Thread t = new Thread(() -> {
System.out.println("线程开始运行!");
});
t.start();
t.setPriority(Thread.MIN_PRIORITY); //通过使用setPriority方法来设定优先级
}12. 线程组
- 线程组一开始就要给定!
public class Demo12 { public static void main(String[] args) { ThreadGroup threadGroup=new ThreadGroup("线程组1"); Thread t1= new Thread(threadGroup,"t1"){ @Override public void run() { System.out.println("t1运行"); for(int i=0;i<10;i++) System.out.println(Thread.currentThread().getThreadGroup()); } }; t1.start(); System.out.println(Thread.currentThread().getThreadGroup()+"main结束"); } }
3. 线程状态
- 线程状态:新建,运行,阻塞,等待,销毁
- 这个枚举类阐述了一个线程的生命周期中,总共有以下6种状态
状态 描述 【NEW】 这个状态主要是线程未被Thread.start()调用前的状态。 【RUNNABLE】 线程正在JVM中被执行,等待来自操作系统(如处理器)的调度。 【BLOCKED】 阻塞,因为某些原因不能立即执行需要挂起等待。 【WAITING】 无限期等待,由于线程调用了 Object.wait(0),Thread.join(0)和LockSupport.park其中的一个方法,线程处于等待状态,其中调用wait,join方法时未设置超时时间。【TIMED_WAITING】 有限期等待, 线程等待一个指定的时间,比如线程调用了 Object.wait(long),Thread.join(long),LockSupport.parkNanos,LockSupport.parkUntil方法之后,线程的状态就会变成TIMED_WAITING【TERMINATED】 终止的线程状态,线程已经完成执行。
public class Demo1 {
public static void main(String[] args) throws InterruptedException {
MyThread myThread=new MyThread(new MyTask());
myThread.start();
System.out.println("线程就绪");
Thread.sleep(1000);
System.out.println("主线程执行完毕");
}
}
class MyThread extends Thread{
public MyThread(Runnable task){
super(task);//初始化线程以及所携带的任务
System.out.println("新建状态");//1
}
}
class MyTask implements Runnable{
@Override
public void run() {
System.out.println("进入阻塞状态(休眠)");//与就绪状态顺序不确定,但一定在main结束前执行
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
System.out.println("运行状态");
}
}