Java-day14(多线程)

多线程

0.基本概念

程序:为完成特定任务,用某种编程语言编写的一组指令的集合(静态

进程:程序的一次执行过程,或正在执行的一个程序(动态过程)

线程:程序内部的一条执行路径,若某个程序支持同一时间执行多个线程,即支持多线程

1.多线程的创建和使用

继承Thread类创建多线程

  • 一个线程只能执行一次start()
  • 不能通过Thread类的run方法去启动一个线程

例:

package thead;  //1.创建继承于Thread的子类
class SubThread extends Thread{//2.重写Thread类的run方法,方法内实现此子类线程要完成的功能public void run() {for(int i = 1;i <= 100;i++) {System.out.println(Thread.currentThread().getName() + ":" + i); }}
}
public class test1 {public static void main(String[] args) {//4.创建子类对象SubThread s = new SubThread();//5.调用start()方法,启动线程;再调用相应的run方法s.start();//主线程执行for(int i = 1;i <= 100;i++) {System.out.println(Thread.currentThread().getName() + ":" + i);}}
}实现Runnable接口创建多线程
package thead; 
//创建多线程的方式二:通过实现的方式
//1.创建实现Runnable接口的类
class PrintNum implements Runnable{//2.重写run的方法public void run() {for(int i = 1;i <= 100;i++) {System.out.println(Thread.currentThread().getName() + ":" + i);}}
}
public class test4 {public static void main(String[] args) {//3.创建一个Runnable接口的实现类的对象PrintNum p = new PrintNum();//4.将此对象作为形参传递给Thread类的构造器中Thread t1 = new Thread(p);//想启动多线程,必须调用start()t1.start();Thread t2 = new Thread(p);t2.start();}}

继承Thread类与实现Runnable接口的区别

  • 1.都继承了Runnable接口(Thread类实现Runnable接口)
  • 2.实现的方式优于继承的方式(避免Java单线程的局限性;如果多个线程要操作同一份资源(或数据),更合适使用实现的方式)

案例:模拟火车站售票,开启三个窗口,总票数100张

继承

package project;
//模拟火车站售票,开启三个窗口,总票数100张
class Window extends Thread{static int ticket = 100; //总票数public void run() {		while(true) {if(ticket > 0) {System.out.println(Thread.currentThread().getName() + "售票口:票号为:" + ticket--);	}else {break;}}}
}
public class test1 {public static void main(String[] args) {Window w1 = new Window();Window w2 = new Window();Window w3 = new Window();w1.setName("1号");w2.setName("2号");w3.setName("3号");w1.start();w2.start();w3.start();}
}

实现

package project; 
//模拟火车站售票,开启三个窗口,总票数100张
class Window1 implements Runnable{int ticket = 100; //总票数public void run() {		while(true) {if(ticket > 0) {System.out.println(Thread.currentThread().getName() + "售票口:票号为:" + ticket--);	}else {break;}}	}
}
public class test2 {public static void main(String[] args) {Window1 w = new Window1();Thread t1 = new Thread(w);Thread t2 = new Thread(w);Thread t3 = new Thread(w);t1.setName("1号");t2.setName("2号");t3.setName("3号");t1.start();t2.start();t3.start();}
}

多线程的优点

  • 1.提高应用程序的响应,对图形化界面更有意义,可增强用户体验
  • 2.提供计算机CPU利用率
  • 3.改善程序结构,将复杂的进程分为多个线程,独立运行,利于理解和修改

Thread类的主要方法与配置线程优先级

在这里插入图片描述
在这里插入图片描述

package thead;          
/** 线程的主要方法* 1.start():启动线程并执行相应的run()方法* 2.run():子线程要执行的代码放入run()方法中* 3.currentThread():静态的,调取当前的线程* 4.getName():获取线程的名字* 5.setName():设置线程名* 6.yield():调用此方法的线程释放当前CPU的执行权* 7.join():在A线程中调用B线程的join()方法,当执行到此方法时,A停止,B执行,等B全部执行完后,A再继续执行剩下的任务。* 8.isAlive():检测线程是否存活* 9.sleep(long l):显式的让当前线程睡眠l毫秒* 10.线程通信:wait()  notify()  notifyAll()后续介绍* 11.线程优先级(默认5,最大10,最小1):只是提高了抢到的CPU执行权的概率* 		getPriority():返回线程的优先级* 		setPriority(int newPriority):改变线程的优先级*/class SubThread1 extends Thread{public void run() {for(int i = 1;i <= 100;i++) {
//			try {   
//				Thread.currentThread().sleep(1000);
//			} catch (InterruptedException e) {
//				// TODO Auto-generated catch block
//				e.printStackTrace();
//			}System.out.println(Thread.currentThread().getName() + ":" + Thread.currentThread().getPriority() + ":" + i);}}
}
public class test2 {public static void main(String[] args) {SubThread1 s1 = new SubThread1();s1.setName("线程1号");s1.setPriority(Thread.MAX_PRIORITY);s1.start();Thread.currentThread().setName("======主线程=====");for(int i = 1;i <= 100;i++) {System.out.println(Thread.currentThread().getName() + ":" + Thread.currentThread().getPriority() + ":" + i);
//			if(i % 10 == 0) {
//				Thread.currentThread().yield();
//			}
//			if(i == 10) {
//			try { 
//				s1.join();
//			} catch (InterruptedException e) {
//				e.printStackTrace();
//			}
//		}}System.out.println(s1.isAlive());}
}

练习
在这里插入图片描述

package thead;       class SuThread1 extends Thread{//1-100奇数public void run() {for(int i = 1;i <= 100;i = i + 2) {System.out.println(Thread.currentThread().getName() + " : " + i);}}
}class SuThread2 extends Thread{//1-100偶数public void run() {for(int i = 2;i <= 100;i = i + 2) {System.out.println(Thread.currentThread().getName()+ " : "  + i); }}
}class test3{public static void main(String[] args) {SuThread1 st1 = new SuThread1();SuThread2 st2 = new SuThread2();st1.setName("子线程1号");st1.start();st2.setName("子线程2号");st2.start();//		//继承Thread类的匿名类的对象
//		new Thread() {
//			public void run() {
//				for(int i = 2;i <= 100;i = i + 2) {
//					System.out.println(Thread.currentThread().getName()+ " : "  + i); 
//				}
//			}
//		}.start();}
}

2.线程的生命周期

生命周期中通常要经历的5种状态
新建:当一个Thread类或子类的对象被声明并创建时,新生的线程对象处于新建状态
就绪:处于新建状态的线程被start()后,将进入线程队列等待CPU时间片,此时它已具备运行的条件
运行:当就绪的线程被调度并获得处理器资源时,便进入运行状态,run()方法定义了线程的操作和功能
阻塞:在某种特殊情况下,被人为挂起或执行输入输出操作时,让出CPU并临时中止自己的执行,进入阻塞状态
死亡:线程完成全部工作或线程被提前强制中止
在这里插入图片描述

3.线程的同步(重点)

在上面列举的火车售票案例中存在售错票,重票等的情况,存在线程安全问题
原因:由于线程在操作共享数据过程中,未执行完毕的情况下,另外的线程参与进来,导致共享的数据存在安全问题
解决:必须让一个线程操作共享数据完毕以后,其他线程才有机会参与共享数据的操作
Java利用线程同步来解决线程安全问题

方法一:同步代码块

synchronized(同步监视器){//需要同步的代码(即为操作共享数据的代码);}
  • 1.同步监视器:由一个任意类的对象来充当。哪个线程获此监视器,谁就执行括号中被同步的代码。俗称:锁
  • 2.共享数据:多线程共同操作的同一个数据(变量)
  • 要求:所有的线程必须共用同一把锁

例:

package project;
//模拟火车站售票,开启三个窗口,总票数100张
class Window2 implements Runnable{int ticket = 100; //总票数(共享数据)Object obj = new Object();public void run() {		while(true) {synchronized(obj){if(ticket > 0) {try {Thread.currentThread().sleep(10);} catch (InterruptedException e) {// TODO Auto-generated catch blocke.printStackTrace();}System.out.println(Thread.currentThread().getName() + "售票口:票号为:" + ticket--);	}else {break;}}}}
}
public class test4 {public static void main(String[] args) {Window2 w = new Window2();Thread t1 = new Thread(w);Thread t2 = new Thread(w);Thread t3 = new Thread(w);t1.setName("1号");t2.setName("2号");t3.setName("3号");t1.start();t2.start();t3.start();}
}

注:在实现方式中可以用this表示当前对象(同步监视器),若在继承的方式中慎用this

方法二:同步方法

public synchronized void show() {//需要同步的代码(即为操作共享数据的代码);}

例:

package project;  
class Window3 implements Runnable{int ticket = 100; public void run() {		while(true) {show();}}public synchronized void show() {	if(ticket > 0) {try {Thread.currentThread().sleep(10);} catch (InterruptedException e) {// TODO Auto-generated catch blocke.printStackTrace();}System.out.println(Thread.currentThread().getName() + "售票口:票号为:" + ticket--);	}}
}
public class test3 {public static void main(String[] args) { Window3 w = new Window3();Thread t1 = new Thread(w);Thread t2 = new Thread(w);Thread t3 = new Thread(w);t1.setName("1号");t2.setName("2号");t3.setName("3号");t1.start();t2.start();t3.start();}
}

互斥锁

在这里插入图片描述

单例之懒汉式的线程安全

例:

package thead; 
/* 单例之懒汉式的线程安全:使用同步机制 */
//对于静态方法而言,使用当前类本身充当锁
class Singletion{private Singletion() {}private static Singletion instance = null;public static Singletion getInstance() {if(instance == null) {synchronized(Singletion.class) {if(instance == null) {instance = new Singletion();}}}return instance;}
}
public class test5 {public static void main(String[] args) {// TODO Auto-generated method stubSingletion s1 = Singletion.getInstance();Singletion s2 = Singletion.getInstance();System.out.println(s1 == s2);}
}
  • 线程同步的弊端:同一时间只能有一个线程访问共享数据,效率变低

在这里插入图片描述
在这里插入图片描述

练习
在这里插入图片描述

package project;
//线程安全练习
//存在多线程,存在共享数据,需同步
class  Account{double balance;//余额public Account() {}//存取public synchronized void deposit(double amt) {balance += amt;try {Thread.currentThread().sleep(10);} catch (InterruptedException e) {e.printStackTrace();}System.out.println(Thread.currentThread().getName() + ":" + balance );}
}
class Customer extends Thread{Account account;public Customer(Account account) {this.account = account;}public void run() {for(int i = 0;i < 3;i++) {account.deposit(1000);}}
}public class test5{
public static void main(String[] args) {Account acct = new Account();Customer c1 = new Customer(acct);Customer c2 = new Customer(acct);c1.setName("甲");c2.setName("乙");c1.start();c2.start();}
}

线程死锁

在这里插入图片描述
例:

package thead;
/* 死锁:处理线程同步时容易出现 */
//一个线程抓住一半锁,另一个线程抓住一半锁,互不相让
public class test6 {static StringBuffer sb1 = new StringBuffer();static StringBuffer sb2 = new StringBuffer();public static void main(String[] args) {new Thread() {public void run() {synchronized(sb1) {try {Thread.currentThread().sleep(10);}catch(InterruptedException e) {e.printStackTrace();}}sb1.append("A");synchronized(sb2){sb2.append("B");System.out.println(sb1);System.out.println(sb2);}}}.start();new Thread() {public void run() {synchronized(sb2) {try {Thread.currentThread().sleep(10);}catch(InterruptedException e) {e.printStackTrace();}}sb1.append("C");synchronized(sb1){sb2.append("D");System.out.println(sb1);System.out.println(sb2);}}		}.start();
}
}

4.线程的通信

wait()
让当前线程挂起并放弃CPU,同步资源,使用别的线程可访问并修改共享资源,而当前线程排队等候再次对资源的访问

notify()
唤醒正在排队等待同步资源的线程中优先级最高者结束等待

ontifyAll()
唤醒正在排队等待资源的所有线程结束等待

以上三个方法(Java.lang.Object提供)只有在synchronized()方法或synchronized代码块中才能使用,否则会报异常

例1:

package thead;  
/* 两个线程交替打印1~100的数 */
//线程通信:wait(),让当前线程停止;notify(),notifyAll()唤醒wait()停止的1个或多个线程
class PrintfNum implements Runnable{int num = 1;public void run() {while(true) {synchronized (this) {//解决线程安全问题notify();if (num <= 100) {try {Thread.currentThread().sleep(10);} catch (InterruptedException e) {// TODO Auto-generated catch blocke.printStackTrace();} //使隐藏的线程安全问题暴露处理System.out.println(Thread.currentThread().getName() + ":" + num);num += 1;} else {break;}try {wait();} catch (InterruptedException e) {e.printStackTrace();}}}}
}
public class test7 {public static void main(String[] args) {PrintfNum p = new PrintfNum();Thread t1 = new Thread(p);Thread t2 = new Thread(p);t1.setName("A");t2.setName("B");t1.start();t2.start();}}
生产者/消费者问题

生产者(Productor)将产品交给店员(Clerk),而消费者(Customer)从店员处取走。店员一次只能持有一定数量的产品(如:20).如果生产者试图多生产会被叫停,如果店中有空位才会通知生产者生产;如果店中没有产品,店员会告诉消费者等一下,有产品后在通知消费者取走产品。
:可能会出现的问题:

  • 1.生产者比消费者快,消费者会漏掉一些数据没有取到

  • 2.消费者比生产者快,消费者会取到相同的数据

package thead; 
//生产者/消费者问题
//多线程:生产者,消费者;共享数据:产品数量;存在数据通信//店员
class Clerk{int product;     public synchronized void addProduct() {if(product < 20) {product += 1;System.out.println(Thread.currentThread().getName() + ":生产到第" + product + "产品了!" );notify();}elsetry {wait();} catch (InterruptedException e) {// TODO Auto-generated catch blocke.printStackTrace();}}public synchronized void consumProduct() {if(product <= 0) {try {wait();} catch (InterruptedException e) {// TODO Auto-generated catch blocke.printStackTrace();}}else {System.out.println(Thread.currentThread().getName() + ":消费到第" + product + "产品了!" );product -= 1;notify();}}
}
//生产者
class Producer implements Runnable{Clerk clerk;public Producer(Clerk clerk) {this.clerk = clerk;}public void run() {System.out.println("生产者开始生产产品");while(true) {try {Thread.currentThread().sleep(100);} catch (InterruptedException e) {// TODO Auto-generated catch blocke.printStackTrace();}clerk.addProduct();}}
}//消费者
class Customer implements Runnable{
Clerk clerk;public Customer(Clerk clerk) {this.clerk = clerk;}public void run() {System.out.println("消费者开始消费产品");while(true) {try {Thread.currentThread().sleep(100);} catch (InterruptedException e) {// TODO Auto-generated catch blocke.printStackTrace();}clerk.consumProduct();}}
}public class test8 {public static void main(String[] args) {// TODO Auto-generated method stubClerk clerk = new Clerk();Producer producer = new Producer(clerk);Customer customer = new Customer(clerk);Thread t1 = new Thread(producer);//一个生产者线程Thread t3 = new Thread(producer);//一个生产者线程Thread t2 = new Thread(customer);//一个生产者线程t1.setName("生产1号");t2.setName("消费者");t3.setName("生产2号");t1.start();t2.start();t3.start();}
}

练习
在上次练习的基础上,实现交替存款

package project;
//线程安全练习
//存在多线程,存在共享数据,需同步
//扩展实现交替打印class  Account{double balance;//余额public Account() {}//存取public synchronized void deposit(double amt) {notify();balance += amt;try {Thread.currentThread().sleep(10);} catch (InterruptedException e) {e.printStackTrace();}System.out.println(Thread.currentThread().getName() + ":" + balance );try {wait();} catch (InterruptedException e) {// TODO Auto-generated catch blocke.printStackTrace();}}
}
class Customer extends Thread{Account account;public Customer(Account account) {this.account = account;}public void run() {for(int i = 0;i < 3;i++) {account.deposit(1000);}}
}public class test5{
public static void main(String[] args) {Account acct = new Account();Customer c1 = new Customer(acct);Customer c2 = new Customer(acct);c1.setName("甲");c2.setName("乙");c1.start();c2.start();}
}

感谢大家的支持,关注,评论,点赞!
参考资料:
尚硅谷宋红康20天搞定Java基础下部

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/109504.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

CSS文字居中对齐学习

CSS使用text-align属性设置文字对齐方式&#xff1b;text-align:center&#xff0c;这样就设置了文字居中对齐&#xff1b; <!DOCTYPE html> <html><head><meta charset"UTF-8"><title>css 水平居中</title><style>.box …

Truenas scale 初始化设置

Truenas 安装 官网下载镜像Rufu刻录镜像实体机安装 参考 司波图YouTube, 张先生的深夜课堂&#xff0c; 小曾和他的朋友们 。 设置 设置时区 System setting -> General -> Localization 设置固定路由器IP Network -> Interface 中点击已经获取到路由器分配的 I…

Java版企业电子招标采购系统源码—企业战略布局下的采购寻源

功能模块&#xff1a; 待办消息&#xff0c;招标公告&#xff0c;中标公告&#xff0c;信息发布 描述&#xff1a; 全过程数字化采购管理&#xff0c;打造从供应商管理到采购招投标、采购合同、采购执行的全过程数字化管理。通供应商门户具备内外协同的能力&#xff0c;为外部供…

计算机专业毕业设计项目推荐06-工作室管理系统(Java+Vue+Mysql)

工作室管理系统&#xff08;JavaSpringVueMysql&#xff09; **介绍****系统总体开发情况-功能模块****各部分模块实现****最后想说的****联系方式** 介绍 本系列(后期可能博主会统一为专栏)博文献给即将毕业的计算机专业同学们,因为博主自身本科和硕士也是科班出生,所以也比较…

D3JS简介

D3JS 什么是D3js D3.js是一个流行的JavaScript数据可视化库&#xff0c;它提供了一系列的API和工具&#xff0c;用于创建交互式的数据图表、地图等可视化效果。以下是一些D3.js的特点和用途&#xff1a; 数据驱动&#xff1a;D3.js基于数据驱动的思想&#xff0c;将数据和视觉…

JavaSE、JavaEE与Spring的概念和异同点剖析以及规范13 个分析

JavaSE、JavaEE与Spring的概念和异同点剖析以及规范13 个分析 目录概述需求&#xff1a; 设计思路实现思路分析1.什么是JavaSE2.是JavaEE3.什么是Spring 参考资料和推荐阅读 Survive by day and develop by night. talk for import biz , show your perfect code,full busy&…

uniapp打包微信小程序。报错:https://api.weixin.qq.com 不在以下 request 合法域名列表

场景&#xff1a;在进行打包上传测试时&#xff0c;发现登录失效&#xff0c;但在测试中【勾选不效应合法域名】就可以。 出现原因&#xff1a;我在获取到用户code后&#xff0c;直接使用调用官方接口换取openid 解决方案&#xff1a; 可以把code带给后端&#xff0c;让他们返…

java word文档 转 html文件

用java将word转为html文档 1、简介2、添加依赖3、代码示例 1、简介 最近&#xff0c;因项目需要&#xff0c;需要对word文档进行解析拆分&#xff0c;感觉直接解析word有点麻烦&#xff0c;于是想到&#xff0c;先将word转为html文档&#xff0c;然后用jsoup解析html文件更方便…

港联证券:基本面分析的基本知识?

作为一种股票投资剖析办法&#xff0c;根本面剖析是用来研究一家公司在剖析期内的运营状况的一种办法&#xff0c;它重视公司的财政数据、盈余才能、市场占有率以及宏观经济要素等方面&#xff0c;为投资者供给了一个深化了解公司价值的方法。下面&#xff0c;本文将从多个视点…

Jprofiler的使用查看oom

一、安装 idea安装插件 安装客户端 链接 IDEA配置Jprofiler执行文件 二、产生oom import java.util.ArrayList; import java.util.List;//测试代码 public class TestHeap {public static void main(String[] args) {int num 0;List<Heap> list new ArrayList&l…

读高性能MySQL(第4版)笔记05_优化服务器设置

1. 除非遇到异常情况&#xff0c;否则不需要调整配置 1.1. 不要“调优”服务器&#xff0c;不要使用比率、公式或“调优脚本”作为设置配置变量的基础 1.1.1. 在互联网上搜索配置建议并不总是一个好主意&#xff0c;你会在博客、论坛等找到很多糟糕的建议 1.1.2. 很难判断谁…

【案例】光电兼修的Alpha Cen,如何应对上升期的甜蜜烦恼?

Alpha Cen&#xff08;比邻星光&#xff09;是一家挺酷的公司&#xff0c;专注于超表面技术芯片研发。 所谓超表面芯片&#xff0c;就是通过控制类似下图的一个个独立纳米级别单元实现光操控。用微纳光学超表面技术能够平面化集成唯一还不在wafer上的光学器件&#xff0c;这意…