多线程
多线程
1、多线程的定义
1、什么是多线程?
有了多线程,我们就可以让程序同时做多件事情
2、多线程的作用?
提高效率
3、多线程的应用场景?
只要你想让多个事情同时运行就需要用到多线程
比如: 软件中的耗时操作、所有的聊天软件、所有的服务器
4、并发:在同一时刻,有多个指令在单个CPU上交替执行
5、并行:在同一时刻,有多个指令在多个CPU上同时执行
2、多线程的实现方式
1、继承Thread类的方式进行实现
2、实现Runnable接口的方式进行实现
3、利用Callable接口和Future接口方式实现
2.1 继承Thread类的方式进行实现
public class MyThead extends Thread {
@Override
public void run() {
//线程执行代码
for (int i = 0; i < 10; i++) {
System.out.println(getName() + "Hello");
}
}
}
public class Demo01_1 {
public static void main(String[] args) {
//创建线程
MyThead m1 = new MyThead();
MyThead m2 = new MyThead();
//设置线程名称
m1.setName("线程1");
m2.setName("线程2");
//启动线程
m1.start();
m2.start();
}
}
2.2 实现Runnable接口的方式进行实现
public class MyRun implements Runnable {
@Override
public void run() {
//获取当前线程对象
Thread m = Thread.currentThread();
//线程执行代码
for (int i = 0; i < 10; i++) {
System.out.println(m.getName()+"Hello");
}
}
}
public class Demo01_1 {
public static void main(String[] args) {
//多线程执行任务
MyRun mr = new MyRun();
//创建线程对象
Thread t1 = new Thread(mr);
Thread t2 = new Thread(mr);
//设置线程名称
t1.setName("线程1");
t2.setName("线程2");
//启动线程
t1.start();
t2.start();
}
}
2.3 利用Callable接口和Future接口方式实现
特点可以获取多线程运行结果
public class MyCallable implements Callable<Integer> {
@Override
public Integer call() throws Exception {
int sum = 0;
for (int i = 0; i <=100; i++) {
sum += i;
}
return sum;
}
}
public class Demo01_1 {
public static void main(String[] args) throws ExecutionException, InterruptedException {
//多线程执行任务
MyCallable mc = new MyCallable();
//管理多线程运行结果
FutureTask<Integer> ft = new FutureTask<Integer>(mc);
//创建线程对象
Thread t = new Thread(ft);
//启动线程
t.start();
//获取线程结果
Integer result = ft.get();
System.out.println(result);
}
}
3、线程常用方法
略 网上查
4、线程生命周期
略 网上查
5、线程安全问题
5.1 售票问题
public class MyThread extends Thread {
//票数
//有多个对象共享一个变量
static int ticket = 0;
@Override
public void run() {
//执行代码
while (true) {
if (ticket < 100) {
ticket++;
try {
sleep(100);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
System.out.println(getName() + "售出第" + ticket + "票!!");
} else {
break;
}
}
}
}
public class Demo01_1 {
public static void main(String[] args){
//创建线程对象
MyThread m1 = new MyThread();
MyThread m2 = new MyThread();
MyThread m3 = new MyThread();
//设置线程名称
m1.setName("窗口1");
m2.setName("窗口2");
m3.setName("窗口3");
//启动线程
m1.start();
m2.start();
m3.start();
}
}
上面的代码执行会出现多个窗口售卖一张票和多票、少票问题。
解决上面问题使用同步代码块 synchronized
5.2 同步代码块 synchronized
1、同步代码块:把操作共享数据的代码锁起来
2、格式:
synchronized (锁对象){
操作共享数据的代码
}
特点1: 锁默认打开,有一个线程进去了,锁自动关闭
特点2: 里面的代码全部执行完毕,线程出来,锁自动打开
public class MyThread extends Thread {
//票数
//有多个对象共享一个变量
static int ticket = 0;
@Override
public void run() {
//执行代码
while (true) {
//同步代码块
//锁对象 一定是唯一的 通常设置当前类的字节码文件
synchronized (MyThread.class){
if (ticket < 100) {
ticket++;
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(getName() + "售出第" + ticket + "票!!");
} else {
break;
}
}
}
}
}
public class Demo01_1 {
public static void main(String[] args){
//创建线程对象
MyThread m1 = new MyThread();
MyThread m2 = new MyThread();
MyThread m3 = new MyThread();
//设置线程名称
m1.setName("窗口1");
m2.setName("窗口2");
m3.setName("窗口3");
//启动线程
m1.start();
m2.start();
m3.start();
}
}
5.3 同步方法
1、同步方法
就是把synchronized关键字加到方法上
2、格式:
修饰符 synchronized 返回值类型 方法名(方法参) {...}
特点1: 同步方法是锁住方法里面所有的代码
特点2:锁对象不能自己指定
非静态: this
静态:当前类的字节码文件对象
public class MyRunner implements Runnable {
//多个对象共享票数 加上static
static int ticket = 0;
@Override
public void run() {
while (true) {
if (method()) break;
}
}
//同步方法
private synchronized boolean method() {
//票售出完
if (ticket == 100) {
return true;
} else {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
ticket++;
System.out.println(Thread.currentThread().getName() + "售出第" + ticket + "张票!!");
}
return false;
}
}
public class Demo01_2 {
public static void main(String[] args) {
MyRunner my = new MyRunner();
//创建线程
Thread t1 = new Thread(my);
Thread t2 = new Thread(my);
Thread t3 = new Thread(my);
//设置线程名称
t1.setName("窗口1");
t2.setName("窗口2");
t3.setName("窗口3");
//线程启动
t1.start();
t2.start();
t3.start();
}
}
5.4 Lock锁
1、虽然我们可以理解同步代码块和同步方法的锁对象问题
但是我们并没有直接看到在哪里加上了锁,在哪里释放了锁
为了更清晰的表达如何加锁和释放锁,JDK5以后提供了一个新的锁对象Lock
2、Lock实现提供比使用synchronized方法和语句
可以获得更广泛的锁定操作Lock中提供了获得锁和释放锁的方法
void lock(): 获得锁
void unlock():释放锁
3、Lock是接口不能直接实例化,这里采用它的
实现类ReentrantLock来实例化ReentrantLock的构造方法
ReentrantLock():创建一个ReentrantLock的实例
public class MyThread extends Thread {
//票数 多个对象共享一个变量
static int ticket = 0;
//多个对象共享一个变量
static Lock lock = new ReentrantLock();
@Override
public void run() {
//执行代码
while (true) {
//获得锁
lock.lock();
try {
if (ticket == 100) {
break;
} else {
Thread.sleep(10);
ticket++;
System.out.println(getName() + "售出第" + ticket + "票!!");
}
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
//释放锁
lock.unlock();
}
}
}
}
public class Demo01_1 {
public static void main(String[] args){
//创建线程对象
MyThread m1 = new MyThread();
MyThread m2 = new MyThread();
MyThread m3 = new MyThread();
//设置线程名称
m1.setName("窗口1");
m2.setName("窗口2");
m3.setName("窗口3");
//启动线程
m1.start();
m2.start();
m3.start();
}
}
5.5 死锁
不要嵌套两个锁
6、生产者和消费者
6.1 常见方法
方法名称
void wait() 当前线程等待,直到被其他线程唤醒
void notify() 随机唤醒单个线程
void notifyAll() 唤醒所有线程
/**
* 控制生产者和消费者的执行
*/
public class Desk {
//0:有面条 1:没有面条
public static int foodFlag = 0;
//总个数
public static int count = 10;
//锁对象
public static Object lock = new Object();
}
/**
* 消费者
*/
public class Foodie extends Thread {
@Override
public void run() {
//循环
while (true) {
synchronized (Desk.lock) {
if (Desk.count == 0) {
break;
} else {
//判断是否有面条
if (Desk.foodFlag == 0) {
//没有就等待
try {
Desk.lock.wait();
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
} else {
//数量减一
Desk.count--;
//有就开吃
System.out.println("吃货在吃面条,还能吃" + Desk.count + "碗!");
//吃完唤醒厨师
Desk.lock.notifyAll();
//修改桌子状态
Desk.foodFlag = 0;
}
}
}
}
}
}
/**
* 生产者
*/
public class Cook extends Thread{
@Override
public void run() {
//循环
while (true) {
synchronized (Desk.lock) {
if (Desk.count == 0) {
break;
} else {
//判断是否有面条
if (Desk.foodFlag == 1) {
//有就等待
try {
Desk.lock.wait();
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
} else {
//制作食物
System.out.println("厨师做了一碗面条!");
//修改桌子状态
Desk.foodFlag = 1;
//做完唤醒吃货
Desk.lock.notifyAll();
}
}
}
}
}
}
public class Demo01_3 {
public static void main(String[] args) {
//创建线程对象
Cook cook = new Cook();
Foodie foodie = new Foodie();
//设置线程名称
cook.setName("厨师");
foodie.setName("吃货");
//启动线程
cook.start();
foodie.start();
}
}
6.2 线程状态
线程的状态
新建状态 (NEW )----创建线程对象
就绪状态 (RUNNABLE)----start方法
阻塞状态 (BLOCKED)----无法获得锁对象
等待状态( WAITING)----wait方法
计时等待 ( TIMED WAITING )----sleep方法
结束状态 (TERMINATED)----全部代码运行完毕
7、线程池
7.1 创建线程池
创建线程池 无上限
public class Demo01_4 {
public static void main(String[] args) {
//创建线程池对象 无上限
ExecutorService pool= Executors.newCachedThreadPool();
//提交任务
pool.submit(new MyRunnable());
pool.submit(new MyRunnable());
pool.submit(new MyRunnable());
pool.submit(new MyRunnable());
//销毁线程池
pool.shutdown();
}
}
class MyRunnable implements Runnable{
@Override
public void run() {
System.out.println("线程池");
}
}
创建3个线程池
public class Demo01_4 {
public static void main(String[] args) {
//创建3个线程池
ExecutorService pool= Executors.newFixedThreadPool(3);
//提交任务
pool.submit(new MyRunnable());
pool.submit(new MyRunnable());
pool.submit(new MyRunnable());
pool.submit(new MyRunnable());
//销毁线程池
pool.shutdown();
}
}
class MyRunnable implements Runnable{
@Override
public void run() {
System.out.println("线程池");
}
}
时间:2023-06-25 晚上
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 25岁的心里话
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现