Java多线程
目录
1.进程
- 应用进程的执行实例。
- 有独立的内存空间和系统资源
2.线程
- CPU调度和分派的基本单元
- 执行运算的最小单位,可完成一个独立的顺序控制流程
2.1.多线程
- 如果在一个进程中同时运行了多个线程,用来完成不同的工作,则称之为“多线程”。
- 多个线程交替占用CPU资源,而非真正的并行执行。
优点:
- 充分利用CPU的资源
- 简化编程模型
- 带来良好的用户体验
线程的创建和启动
- 继承java.lang.Thread类
- 实现java.lang.Runnable接口
- 实现Callable接口
实现线程的步骤:
- 定义线程
- 创建线程对象
- 启动线程
- 终止线程
继承Thread类
- 定义xx类继承Thread类
- 重写run()方法,编写线程执行体
- 创建线程对象,调用start()方法启动线程
public class MyThread extends Thread{
public void run() {
for (int i = 1; i <= 20; i++) {
System.out.println(Thread.currentThread().getName()+":"+i);
}
}
}
public class TestThread {
public static void main(String[] args) {
//创建线程对象
MyThread myThread = new MyThread();
MyThread myThread1 = new MyThread();
//启动线程
myThread.start();
myThread1.start();
}}
- 多个线程交替执行,不是真正的”并行“
- 线程每次执行时长由分配的CPU时间片长度决定
------------------------------------------------------------------------------------
调用run()方法和start()方法执行线程的区别:
当主线程main中调用run()方法来启动线程的时候,会依次调用run()方法,会失去线程的意义,run()方法会被当做main()方法中的一个普通方法来执行。
------------------------------------------------------------------------------------
实现Runnable接口
- 定义MyRunnable类实现Runnable接口
- 实现run()方法,编写线程执行体
- 创建线程对象,调用start()方法启动线程
public class MyThread2 implements Runnable{
public void run() {
for (int i = 1; i <= 20 ; i++) {
System.out.println(Thread.currentThread().getName()+":"+i);
}
}
}
public class TestThread2 {
public static void main(String[] args) {
//创建线程对象
Runnable runnable = new MyThread2();
//Thread thread = new Thread(runnable);
Thread thread = new Thread(runnable,"myThread1");
Thread thread2 = new Thread(runnable,"myThread2");
thread.start();
thread2.start();
}}
------------------------------------------------------------------------------------
实现Callable接口
- 定义XXXX类实现Callable接口
- 重写 Object call() 方法
- 创建线程对象,使用 FutureTask 类来包装Callable对象,该FutureTask对象封装了该Callable对象的call()方法的返回值。(FutureTask是一个包装器,它通过接受Callable来创建,它同时实现了 Future 和 Runnable 接口。)
- 使用 FutureTask 对象作为 Thread 对象的 target 创建并启动新线程。
- 调用 FutureTask 对象的 get()方法来获得子线程执行结束后的返回值
public class MyThread5 implements Callable{
@Override
public Object call() throws Exception {
for (int i = 1; i <= 20 ; i++) {
System.out.println(Thread.currentThread().getName()+":"+i);
}
return null;
}
}
public class TestThread5 {
public static void main(String[] args) {
//创建线程对象
MyThread5 myThread5 = new MyThread5();
FutureTask fTask= new FutureTask(myThread5);
Thread thread = new Thread(fTask);
thread.start();
}}
2.2.线程的状态
创建状态:新建了一个线程对象,该对象就处于新建状态( new 的时候)。
就绪状态:调用 start() 时。
阻塞状态:阻塞状态时线程因为某种原因放弃CPU使用权,暂时停止运行,直到线程进入就绪状态,才有机会转为运行状态。
运行状态:执行 run() 方法时。
死亡状态:线程执行结束或退出run()方法。
2.3.线程的调度
线程调度指按照特定机制为多个线程分配CPU的使
方 法 | 说 明 |
void setPriority(int newPriority) | 更改线程的优先级 |
static void sleep(long millis) | 在指定的毫秒数内让当前正在执行的线程休眠 |
void join() | 等待该线程终止 |
static void yield() | 暂停当前正在执行的线程对象,并执行其他线程(礼让) |
void interrupt() | 中断线程 |
boolean isAlive() | 测试线程是否处于活动状态 |
wait() | 暂停一个线程 |
notify() | 唤起一个线程 |
------------------------------------------------------------------------------------
线程的优先级:
- 线程优先级由1~10表示,1最低,默认优先级为5
- 优先级高的线程获得CPU资源的概率较大
------------------------------------------------------------------------------------
sleep:
设置sleep的值可简单实现交替执行,但并不完全准确!
创建两个子线程,每个线程均输出20次消息数字、“你好”、线程名!
public class MyThread extends Thread{
public void run() {
for (int i = 1; i <= 20 ; i++) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(i+"."+"你好,来自线程"+Thread.currentThread().getName());
}
}
}
public class TestThread {
public static void main(String[] args) {
// 创建线程对象
MyThread myThread = new MyThread();
MyThread myThread1 = new MyThread();
// 启动线程
myThread.setName("A");
myThread.start();
myThread1.setName("b");
myThread1.start();
}
}
------------------------------------------------------------------------------------
join:
线程的强制运行:
- 强制执行当前线程,join写在哪个线程,就阻塞谁
public final void join()
public final void join(long mills)
public final void join(long mills,int nanos)
package dh07.demo02;
//join
public class TestMyThread {
public static void main(String[] args) {
// 创建子线程对象
Runnable runnable = new MyThread();
Thread thread = new Thread(runnable, "myThread");
thread.start();
// 主线程main做的事情
for (int i = 1; i <= 20; i++) {
System.out.println(Thread.currentThread().getName() + ":" + i);
//当i=5时,强制把thread线程加入执行
if (i == 5) {
try {
thread.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
------------------------------------------------------------------------------------
yield:
- 暂停当前线程,允许其他具有相同优先级的线程获得运行机会。
- 该线程处于就绪状态,不会进入阻塞状态。
- 只是提供一种可能,但是不能保证一定会实现礼让。
public class MyThread2 implements Runnable{
public void run() {
for (int i = 1; i <= 10 ; i++) {
String tname = Thread.currentThread().getName();
System.out.println(tname+"线程=="+i);
if (i==3) {
System.out.print(tname+"线程礼让:");
Thread.yield();
}
}
}
}
// yield
public class TestMyThread2 {
public static void main(String[] args) {
// 创建子线程对象
Runnable runnable = new MyThread2();
Thread thread1 = new Thread(runnable, "A");
Thread thread2 = new Thread(runnable, "B");
thread1.start();
thread2.yield();
thread2.start();
}
}
2.4.多线程共享数据
多个线程操作同一共享数据时,将引发数据不安全问题。
引发的原因:多线程共同操作数据时,引发的冲突(如延迟时,操作未全部完成等等)
同步方法
使用synchronized 为当前线程声明一把锁。
- 使用 synchronized 修饰的方法控制对类成员变量的访问
-
访问修饰符 synchronized 返回类型 方法名(参数列表){……}
-
synchronized 访问修饰符 返回类型 方法名(参数列表){……}
-
同步代码块:
-
使用 synchronized 关键字修饰的代码块
-
synchronized (syncObject){ //需要同步的代码 }
-
如果 syncObject 为需同步的对象,通常为 this
-
效果与同步方法相同
-
-
同步代码块
- 多个并发线程访问同一资源的同步代码块时
- 同一时刻只能有一个线程进入synchronized(this)同步代码块
- 当一个线程访问一个synchronized(this)同步代码块时,其他 synchronized(this)同步代码块同样被锁定
- 当一个线程访问一个synchronized(this)同步代码块时,其他线程可以访问该资源的非synchronized(this)同步代码
------------------------------------------------------------------------------------
线程同步:即各线程之间要有个先来后到,不能一窝蜂。
线程同步其实是“排队”:上锁,排队,一个一个来,不能同时操作
线程安全的类型:
- ArrayList类的add()方法为非同步方法。
- 当多个线程向同一个ArrayList对象添加数据时,可能出现数据不一致问题。
方法是否同步 | 效率比较 | 适合场景 | |
线程安全 | 是 | 低 | 多线程并发共享资源 |
非线程安全 | 否 | 高 | 单线程 |
常见类型对比:
- Hashtable && HashMap
- Hashtable
- 继承关系
- 实现了Map接口,Hashtable 继承 Dictionary 类
- 线程安全,效率较低
- 键和值都不允许为null
- 继承关系
- HashMap
- 继承关系
- 实现了Map接口,继承 AbstractMap 类
- 非线程安全,效率较高
- 键和值都允许为null
- 继承关系
- Hashtable
总结
练习:习题跳转
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· Manus爆火,是硬核还是营销?
· 终于写完轮子一部分:tcp代理 了,记录一下
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 单元测试从入门到精通