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

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();}
}

 输出部分展示

 

相关文章:

  • cursor/vscode连接低版本的系统(glibc<2.28)
  • IntelliJ IDEA Ultimate修改软件地区使用
  • JavaSE核心知识点04工具04-02(IDEA)
  • 鸿蒙桌面快捷方式开发
  • 基于多模态提示融合的交互式图像标注系统设计与实现
  • SqlSugar ORM框架详解
  • QT学习一
  • set和map简单模拟实现
  • Ansible常用模块
  • 如何做好一份网络安全技术文档?
  • java中的线程安全的集合
  • 航空航天领域对滚珠丝杆的精度要求有多高?
  • 汉诺集团CDN+富氢水机全球发布:科技赋能健康,革新饮水革命
  • Java大师成长计划之第31天:Docker与Java应用容器化
  • 消防营区管理升级:豪越科技智能仓储与装备管理的力量
  • 解锁webpack:处理跨域devserver、摇树treeshaking、图片压缩sharp
  • xhr、fetch和axios
  • 第Y1周打卡——调用官方权重进行检测
  • O2OA服务器配置与管理-自定义消息提醒
  • 【Java学习笔记】单例设计模式
  • 北京软件开发公司滕迎江/镇江百度seo
  • 禹城有做网站/免费站推广网站在线
  • 做网站 php java/做百度推广的网络公司
  • 做牛仔的时尚网站/360搜索首页网址是多少
  • 拆分盘网站建设/新产品推广
  • 江西网站icp备案注销/汕头百度关键词推广