Java多线程
线程简介
想要学习线程就不得不先了解进程和线程的关系
线程及一个进程中的多个子任务,例如电脑中打开了一个视频播放器(一个进程),该播放器会进行画面、声音、字幕等多个子任务(多个线程),在Java语言中线程就是独立的执行路径。
核心概念:
- 在程序运行时,即视没有自己创建线程,后台也会有多个线程,如主线程、gc线程(垃圾回收);
- main()称之为主线程,为系统的入口,用于执行整个程序;
- 在一个进程中,如果开辟了多个线程,线程的运行有调度器安排调度,调度器是与操作系统紧密相关的,先后顺序是不能人为的干预的
- 对同一份资源操作时,会存在资源抢夺的问题,需要加入并发控制;
- 线程会带来额外的开销,如cpu调度时间,并发控制开销。
- 每个线程在自己的工作内存交互,内存控制不当会造成数据不一致
线程实现
线程的创建
三种不同的实现方式:
- 继承Thread类
package com.jerry.threadTest;
/**
* Created by JerrySmith on 2022/3/24.
*
* 如何创建一个多线程
*
*/
//方式一:继承Thread类,并重写run()方法,调用start开启线程
public class Test01 extends Thread {
@Override
public void run() {
//run方法线程体
for (int i = 0; i < 10; i++) {
System.out.println("我在学习多线程"+i);
}
}
public static void main(String[] args) {
//主方法main线程
Test01 test01 = new Test01();
//使用start()方法开启线程
test01.start();
for (int i = 0; i < 10; i++) {
System.out.println("我是main线程"+i);
}
}
}
/**
* 我是main线程0
我是main线程1
我是main线程2
我是main线程3
我是main线程4
我在学习多线程0
我是main线程5
我是main线程6
我是main线程7
我在学习多线程1
我是main线程8
我是main线程9
我在学习多线程2
我在学习多线程3
我在学习多线程4
我在学习多线程5
我在学习多线程6
我在学习多线程7
我在学习多线程8
我在学习多线程9
从执行结果来看,两个线程同步进行,cpu交叉调度
注意,线程开启不一定立即执行,由cpu分配调度
* */
- 实现Runnable接口
package com.jerry.threadTest;
/**
* Created by JerrySmith on 2022/3/24.
*
* 创建线程方式二:实现Runnable接口
*
*/
public class Test02 implements Runnable{
public static void main(String[] args) {
//创建实现Runnable接口类的对象
Test02 test02 = new Test02();
//创建一个线程对象 把实现了Runnable接口类的对象丢进去
Thread thread = new Thread(test02);
//使用线程对象的start()方法开启线程
thread.start();
for (int i = 0; i < 10; i++) {
System.out.println("我是main方法"+i);
}
}
/**
* 重写了Runnable接口中的run方法
*/
@Override
public void run() {
for (int i = 0; i < 10; i++) {
System.out.println("我是run方法"+i);
}
}
}
- 实现Callable接口(不常用)
package com.jerry.threadTest;
import java.util.concurrent.*;
/**
* Created by JerrySmith on 2022/3/24.
*
* 实现Callable接口
*
*/
public class Test05 implements Callable<Boolean>{
@Override
public Boolean call() throws Exception {
for (int i = 0; i < 10; i++) {
System.out.println("我是call方法");
}
return true;
}
public static void main(String[] args) throws ExecutionException, InterruptedException {
//创建线程类的对象
Test05 test05 = new Test05();
//创建执行服务器
ExecutorService service = Executors.newFixedThreadPool(1);//nThreads:线程数
//提交执行
Future<Boolean> sr = service.submit(test05);
//获取结果
Boolean aBoolean = sr.get();
System.out.println(aBoolean);
//关闭服务
service.shutdown();
}
}
初始线程并发问题
代码演示:《买票》10张票,小明、老师、黄牛党一起买
package com.jerry.threadTest;
/**
* Created by JerrySmith on 2022/3/24.
*
* 线程同步问题,代码测试
* 《买票》
* 黄牛党拿到了第10张票
* 小明拿到了第9张票
* 小明拿到了第8张票
* 黄牛党拿到了第7张票
* 小明拿到了第5张票
* 黄牛党拿到了第6张票
* 小明拿到了第4张票
* 黄牛党拿到了第4张票
* 小明拿到了第3张票
* 黄牛党拿到了第3张票
* 小明拿到了第2张票
* 黄牛党拿到了第1张票
*
* 不可能拿到同一张票,所有出现了线程并发的问题
*/
public class Test03 implements Runnable{
private int votesNum = 10;
@Override
public void run() {
while (true){
if (votesNum<=0) {
break;
}
try {
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"拿到了第"+(votesNum--)+"张票");
}
}
public static void main(String[] args) {
Test03 test03 = new Test03();
new Thread(test03,"小明").start();
new Thread(test03+"老师").start();
new Thread(test03,"黄牛党").start();
}
}
线程状态
- 新生状态:当使用关键词new一个线程的时候,线程处于新生状态
- 就绪状态:使用star关键词启动线程后,线程会进入就绪状态,等待cpu的调度
- 启动状态:当cpu调度到线程后,线程开始启用执行
- 阻塞状态:当用户使用线程休眠或等待或同步锁定时,线程会进入阻塞状态,阻塞解除之后线程会再次回到就绪状态等待cpu的调度
- dead:线程中断或者结束后,就会进入死亡状态,不可以再次启动
package com.jerry.threadTest;
/**
* Created by JerrySmith on 2022/3/31.
*
* 测试线程状态
*
*
*/
public class StaTest {
public static void main(String[] args) throws InterruptedException {
//new一个线程对象,重写run方法
Thread thread = new Thread(()->{
for (int i = 0; i < 5; i++) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("线程执行了");
});
//检测线程的状态
//未启动时
Thread.State state = thread.getState();
System.out.println(state);
//启动后
thread.start();
state = thread.</