当前位置: 首页 > ds >正文

Java—— 多线程 第一期

什么是多线程

线程是应用软件中互相独立,可以同时运行的功能

有了多线程,我们就可以让程序同时做多件事情

多线程的作用

提高效率

多线程的应用场景

想让多个事情同时运行就需要用到多线程
比如:软件中的耗时操作、所有的聊天软件、所有的服务器

并发和并行

并发:在同一时刻,有多个指令在单个CPU上交替执行
并行:在同一时刻,有多个指令在多个CPU上同时执行

多线程的实现方式

继承Thread类的方式进行实现

步骤:
1.自己定义一个类继承线程Thread
2.重写run方法
3.创建子类的对象,并启动线程

代码演示

哪条线程抢占到CPU,哪条线程执行run中的代码,所有会出现交替执行 

 输出部分展示 

实现Runnable接口的方式进行实现

步骤:
1.自己定义一个类实现Runnable接口
2.重写里面的run方法
3.创建自己的类的对象
4.创建一个Thread类的对象,并开启线程

 输出部分展示 

利用Callable接口和Future接口方式实现 

特点:可以获取到多线程运行的结果
步骤:
1.创建一个类MyCallable实现Callable接口
2.重写call (是有返回值的,表示多线程运行的结果)
3.创建MyCallable的对象(表示多线程要执行的任务)
4.创建Future接口的实现类FutureTask的对象(用于管理多线程运行的结果)
5.创建Thread类的对象,并启动(表示线程)

三种实现方式的比较

实现方式 优点缺点
继承Thread类编程比较简单,可以直接使用Thread类中的方法可以扩展性较差,继承Thread类不能再继承其他的类
实现Runnable接口扩展性强,实现该接口的同时还可以继承其他的类编程相对复杂,不能直接使用Thread类中的方法
实现Callable接口

线程对象Thread的常见成员方法

方法名称说明
String getName()返回此线程的名称
void setName(String name)设置线程的名字(构造方法也可以设置名字)
static Thread currentThread()获取当前线程的对象
static void sleep(long time)让线程休眠指定的时间,单位为毫秒
setPriority(int newPriority)设置线程的优先级
final int getPriority()获取线程的优先级
final void setDaemon(boolean on)设置为守护线程
public static void yield()出让线程/礼让线程
public static void join()插入线程/插队线程 

前6个方法的细节及演示

public class Test {public static void main(String[] args) throws InterruptedException {/*String getName()返回此线程的名称void setName(String name)设置线程的名字(构造方法也可以设置名字)细节:1、如果没有给线程设置名字,线程默认名字的格式:Thread-X(X从0开始的序号)2、如果要给线程设置名字,可以用set方法进行设置,也可以构造方法设置*/MyRun mr = new MyRun();Thread t1 = new Thread(mr, "线程1");Thread t2 = new Thread(mr, "线程2");/*static Thread currentThread()获取当前线程的对象细节:当JVM虚拟机启动之后,会自动的启动多条线程其中有一条线程就叫做main线程他的作用就是去调用main方法,并执行里面的代码在main方法中获取当前的线程就是main线程*/System.out.println(Thread.currentThread().getName());//main/*static void sleep(long time)让线程休眠指定的时间,单位为毫秒细节:1、哪条线程执行到这个方法,那么哪条线程就会在这里停留对应的时间2、方法的参数:就表示睡眠的时间,单位毫秒3、当时间到了之后,线程会自动的醒来,继续执行下面的其他代码*/for (int i = 1; i <= 5; i++) {System.out.println(i);Thread.sleep(1000);//每隔1秒输出一个数字}/*setPriority(int newPriority)	设置线程的优先级final int getPriority()	获取线程的优先级细节:1、Java默认线程是抢占式调度,哪条线程抢占到CPU,哪条线程执行2、线程的优先级越高,越容易抢占到CPU3、线程的优先级从1到10,默认为5*/int p1 = Thread.currentThread().getPriority();System.out.println(p1);//5Thread.currentThread().setPriority(10);int p2 = Thread.currentThread().getPriority();System.out.println(p2);//10}
}

设置为守护线程的细节及演示

线程1的任务:打印1-10

public class MyRun1 implements Runnable{@Overridepublic void run() {for (int i = 1; i <= 10; i++) {System.out.println(Thread.currentThread().getName()+":"+i);}}
}

线程2的任务:打印1-100

public class MyRun2 implements Runnable{@Overridepublic void run() {for (int i = 1; i <= 100; i++) {System.out.println(Thread.currentThread().getName()+":"+i);}}
}

测试类

public class Test2 {public static void main(String[] args) {//final void setDaemon(boolean on)	设置为守护线程//public static void yield()	出让线程/礼让线程//public static void join()	插入线程/插队线程MyRun1 mr1 = new MyRun1();MyRun2 mr2 = new MyRun2();Thread t1 = new Thread(mr1,"线程1");Thread t2 = new Thread(mr2,"线程2");//将t2设置为守护线程//t1结束时,t2也要陆续结束,不管是否执行完毕t2.setDaemon(true);t1.start();t2.start();}
}

线程1打印完10后结束,线程2是守护线程,也要陆续结束,所以没有打印完100程序就结束了

 输出部分展示 

 出让线程细节及演示

继承Thread类

public class MyThread extends Thread{public MyThread() {}//构造方法不能继承,要使用父类的构造方法需要利用super关键字public MyThread(String name) {super(name);}@Overridepublic void run() {for (int i = 1; i <= 100; i++) {System.out.println(getName()+":"+i);//出让线程,使别的线程容易抢占到CPU//可以让线程尽可以的交替进行Thread.yield();}}
}

测试类 

public class Test3 {public static void main(String[] args) {//public static void yield()	出让线程/礼让线程MyThread t1 = new MyThread("线程1");MyThread t2 = new MyThread("线程2");t1.start();t2.start();}
}

让线程尽可以的交替进行

 输出部分展示 

插入线程细节及演示

继承Thread类

public class MyThread extends Thread{@Overridepublic void run() {for (int i = 1; i <= 5; i++) {System.out.println(i);}}
}

测试类 

public class Test4 {public static void main(String[] args) throws InterruptedException {//public static void join()	插入线程/插队线程MyThread t = new MyThread();t.start();//将t线程插入到当前线程前,当前线程为main线程//只有t线程执行完毕,main线程才开始执行t.join();for (int i = 10; i <=15 ; i++) {System.out.println(i);}}
}

只有t线程执行完毕,main线程才开始执行 

线程的生命周期

多线程的安全问题

有多个线程操作一段有共享数据的代码,A线程还没有把操作后得到的数据输出出来,其他线程可能就修改了共享数据的值,导致A线程输出的数据被改变,破坏了数据

保证多线程安全的方式

同步代码块

把操作共享数据的代码锁起来,在一个线程执行这些代码时,别的线程在代码外等待。只有这个线程执行完毕后,锁打开,再让抢占到CPU的线程执行。

格式:
synchronized(锁){

        操作共享数据的代码

}

注意:锁对象只要唯一就行,一般使用该类所在文件的字节码作为锁对象(类名.class)

特点1:锁默认打开,有一个线程进去了,锁自动关闭
特点2:里面的代码全部执行完毕,线程出来,锁自动打开

代码演示

ABC三个窗口卖100张电影票

 MyThread类

public class MyThread extends Thread {//定义票数,static共享static int ticket = 0;public MyThread() {}public MyThread(String name) {super(name);}@Overridepublic void run() {while (true) {synchronized (MyThread.class) {try {Thread.sleep(100);} catch (InterruptedException e) {throw new RuntimeException(e);}if (ticket == 100) {break;}ticket++;System.out.println(getName() + "卖出第" + ticket + "张票");}}}
}

 测试类

public class Test1 {public static void main(String[] args) {MyThread t1 = new MyThread("窗口A");MyThread t2 = new MyThread("窗口B");MyThread t3 = new MyThread("窗口C");t1.start();t2.start();t3.start();}
}

 输出部分展示 

同步方法

就是把锁中的代码抽取成一个方法,再把关键字synchronized加到方法上

格式:
修饰符 synchronized 返回值类型 方法名(方法参数) {

        ...

}

特点1:同步方法的方法体是锁里面所有的代码

特点2:锁对象不能自己指定,自动设定为如下形式

        非静态:this
        静态:当前类的字节码文件对象

代码演示

ABC三个窗口卖100张电影票

MyThread类

public class MyThread extends Thread {//定义票数,static共享static int ticket = 0;public MyThread() {}public MyThread(String name) {super(name);}@Overridepublic void run() {while (true) {if (method()) break;}}//同步方法private synchronized boolean method() {try {Thread.sleep(100);} catch (InterruptedException e) {throw new RuntimeException(e);}if (ticket == 100) {return true;}ticket++;System.out.println(getName() + "卖出第" + ticket + "张票");return false;}
}

测试类

public class Test1 {public static void main(String[] args) {MyThread t1 = new MyThread("窗口A");MyThread t2 = new MyThread("窗口B");MyThread t3 = new MyThread("窗口C");t1.start();t2.start();t3.start();}
}

 输出部分展示 

Lock锁

同步代码块和同步方法中不能直观的看到在哪里加上了锁,在哪里释放了锁
为了更清晰的表达如何加锁和释放锁,JDK5以后提供了一个新的锁对象Lock
Lock提供比使用synchronized方法和语句更广泛的锁定操作

Lock是接口,采用它的实现类ReentrantLock来实例化
ReentrantLock的构造方法ReentrantLock():创建一个ReentrantLock的实例 

Lock中提供了获得锁和释放锁的方法,可以手动上锁、手动释放锁
void lock():获得锁
void unlock():释放锁

代码演示

ABC三个窗口卖100张电影票

MyThread类

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;public class MyThread extends Thread {//定义票数,static共享static int ticket = 0;//获得锁对象,static共享(唯一)static Lock l = new ReentrantLock();public MyThread() {}public MyThread(String name) {super(name);}@Overridepublic void run() {while (true) {try {//手动上锁l.lock();Thread.sleep(100);if (ticket == 100) {break;}ticket++;System.out.println(getName() + "卖出第" + ticket + "张票");} catch (InterruptedException e) {throw new RuntimeException(e);} finally {//即使break跳出循环,也会执行finally中的代码//手动解锁l.unlock();}}}
}

测试类

public class Test1 {public static void main(String[] args) {MyThread t1 = new MyThread("窗口A");MyThread t2 = new MyThread("窗口B");MyThread t3 = new MyThread("窗口C");t1.start();t2.start();t3.start();}
}

 输出部分展示

 

http://www.xdnf.cn/news/9243.html

相关文章:

  • 存储引擎系列--LSM不同Compaction策略性能分析对比
  • STM32:Modbus通信协议核心解析:关键通信技术
  • 【大模型面试每日一题】Day 29:简单介绍一下混合精度训练的技术要点及潜在风险
  • Kubernetes Service 类型与实例详解
  • Mybatis中的两个动态SQL标签
  • (先发再改)测试流程标准文档
  • 【面试题】如何测试即时通信功能:A给B发送一条了信息:hello
  • ‌加密 vs 电子签名:公钥私钥的奇妙冒险
  • 大数据学习(121)-sql重点问题
  • IP2366调试问题总结
  • 第12次07 :邮箱的验证
  • 57、【OS】【Nuttx】编码规范解读(五)
  • ET CircularBuffer 类
  • Cadence学习笔记之---PCB过孔替换、封装更新,DRC检查和状态查看
  • 动态贴纸的实时渲染原理:美颜SDK中的特效引擎开发实录
  • 化工厂电动机保护升级记:当Profinet遇上DeviceNet
  • 【数字图像处理】_笔记
  • Webpack 5 模块联邦(Module Federation)详解与实战
  • 多头注意力 vs 单头注意力:计算量与参数量区别
  • MySQL日志文件有哪些?
  • 一、docker安装以及配置加速
  • [免费]SpringBoot+Vue在线教育(在线学习)系统(高级版)【论文+源码+SQL脚本】
  • Python打卡训练营Day37
  • 《仿盒马》app开发技术分享-- 新增地址(端云一体)
  • AI算力网络光模块市场发展分析
  • 第二章 1.1 数据采集安全风险概述
  • 程序编码规范,软件设计规范
  • 【产品经理】产品经理知识体系
  • Mysql性能优化方案
  • 洛谷题目:P2785 物理1(phsic1)- 磁通量 题解 (本题较难)