【Java】深入理解Java线程

1 相关概念

并发:两个或多个事件在同一时间段内发生【多个任务交替执行】
并行:两个或多个事件在同一时刻发生【多个任务同时执行】
进程:进入内存的程序
内存:所有应用程序都要进入到内存中执行 临时存储RAM
线程:进程的一个执行单元,负责程序的执行
一个程序至少有一个进程,一个进程可以包含多个线程
CPU:中央处理器,对数据进行计算,指挥软件和硬件
单线程:CPU在多个线程之间做高速的切换,轮流执行多个线程,效率低
多线程:多个线程在多个任务之间做高速的切换,速度是单线程的多倍,多个线程之间互不影响

线程调度

  • 分时调度:所有线程轮流使用CPU,平均分配每个线程占用CPU的时间
  • 抢占式调度:优先让优先级高的线程使用CPU,如果优先级相同,则随机选择一个,Java中使用抢占式调度。

2 主线程

主线程:执行main方法的线程
主线程的过程:JVM执行main方法,main方法进入到栈内存,JVM会找操作系统开辟一条main方法的执行路径,CPU根据路径来执行main方法,这个路径就是主线程。
单线程:执行从main方法开始自上而下依次执行

public class Person {private String name;public Person(String name) {this.name = name;}public void run(){for (int i = 0; i < 3; i++) {System.out.println(name + " " + i);}}
}
public class MainThread {public static void main(String[] args) {Person p1 = new Person("张三");p1.run();Person p2 = new Person("李四");p2.run();}
}

在这里插入图片描述

3 创建多线程程序-法1

3.1 创建Thread类的子类

  1. 创建Thread类的子类
  2. 在子类中重写run方法,设置线程任务
  3. 创建子类对象
  4. 调用start方法,开始新的线程,执行run方法

main压栈执行后,在堆内存中创建线程子类对象,栈中保存对象地址。如果调用run方法,run方法压栈执行,则是单线程处理。如果调用start方法,会开辟新的栈空间执行run方法。CPU可以选择不同的栈空间。

start使线程开始执行,JVM调用线程的run方法,两个线程并发运行
(main线程)<----->(创建新线程执行run)

public class MyThread extends Thread {@Overridepublic void run() {for (int i = 0; i < 5; i++) {System.out.println("run " + i);}}
}
public class MyThreadTest {public static void main(String[] args) {MyThread mt = new MyThread();mt.start();for (int i = 0; i < 5; i++) {System.out.println("main " + i);}}
}

同优先级下,随机抢占,谁抢到谁执行
在这里插入图片描述

3.2 Thread类常用方法

获取线程名称:getName()、Tread.currentTread()

Tread t = new Tread();
sout(t.getName());//名称
Tread t = Tread.currentTread();
sout(t);//名称

设置线程名称:setName()、构造方法参数传递线程名称

public class MyThreadTest {public static void main(String[] args) {MyThread mt1 = new MyThread("张三");mt1.start();MyThread mt2 = new MyThread();mt2.setName("李四");mt2.start();}
}
public class MyThread extends Thread {public MyThread(String name) {super(name);public MyThread() {}@Overridepublic void run() {System.out.println(getName());}
}

线程休眠:sleep(long millis) 毫秒结束后程序继续执行

模拟秒表 示例:

public class MyThreadTest {public static void main(String[] args) throws InterruptedException {for (int i = 1; i <= 60; i++) {System.out.println(i);Thread.sleep(1000);}}
}

4 创建多线程程序-法2【推荐使用】

4.1 创建Runnable实现类

  1. 创建Runanable接口的实现类
  2. 实现类中重写run方法,设置线程任务
  3. 创建实现类的对象
  4. 创建Thread类对象,构造方法中传递Runnale的实现类对象
  5. 调用Thread类中的start方法
public class RunnableImpl implements Runnable {@Overridepublic void run() {for (int i = 0; i < 5; i++) {System.out.println(Thread.currentThread()+" "+ i);}}
}
public class MyThreadTest {public static void main(String[] args) throws InterruptedException {RunnableImpl run = new RunnableImpl();Thread t = new Thread(run);t.start();for (int i = 0; i < 5; i++) {System.out.println(Thread.currentThread() + " " + i);}}
}

在这里插入图片描述

4.2 两种实现方法的区别

Runnable的优点

  • 避免了单继承的局限性,类继承了Thread类就不能继承其他类了,实现Runnable接口还可以实现其他接口。
  • 增强了程序的扩展性,降低了程序的耦合性(解耦)。把设置线程任务开启线程进行了分离。实现类中重写run方法来设置线程任务,创建Thread类对象调用start来开启新线程。想要什么任务,就传递什么实现类对象。

5 匿名内部类创建线程

匿名内部类:简化代码。
把1.子类继承父类 2.重写父类 3.创建子类对象 —> 一步完成
把1.实现实现类接口 2.重写接口方法 3.创建实现类对象 —> 一步完成

public class MyThreadTest {public static void main(String[] args) throws InterruptedException {//Thread法new Thread(){@Overridepublic void run() {for (int i = 0; i < 5; i++) {System.out.println(Thread.currentThread() + " " + i);}}}.start();//Runnable法new Thread(new Runnable() {@Overridepublic void run() {for (int i = 0; i < 5; i++) {System.out.println(Thread.currentThread() + " " + i);}}}).start();}
}

6 线程安全

共享资源产生安全问题

public class RunnableImpl implements Runnable {//共享票源private int tickets = 100;@Overridepublic void run() {//重复卖票while(true){if(tickets > 0){try {Thread.sleep(10);} catch (InterruptedException e) {e.printStackTrace();}System.out.println("卖第"+tickets+"张票");tickets--;}}}
}
public class MyThreadTest {public static void main(String[] args) throws InterruptedException {RunnableImpl r = new RunnableImpl();Thread t1 = new Thread(r);Thread t2 = new Thread(r);Thread t3 = new Thread(r);t1.start();t2.start();t3.start();}
}

出现了重复的票

窗口Thread-2在卖第100张票
窗口Thread-0在卖第100张票
窗口Thread-1在卖第100张票
窗口Thread-1在卖第97张票
窗口Thread-2在卖第97张票
窗口Thread-0在卖第97张票
窗口Thread-0在卖第94张票
窗口Thread-2在卖第94张票

出现了不存在的票

窗口Thread-2在卖第0张票
窗口Thread-1在卖第-1张票

线程安全问题都是由全局变量静态变量引起的。若每个线程中对全局变量、静态变量只有读操作,而无写操作,一般来说,这个全局变量是线程安全的;若有多个线程同时执行写操作,一般都需要考虑线程同步,否则的话就可能影响线程安全。

注意:访问共享数据的时候,无论是否时区CPU执行权,其他线程只能等待,等当前线程完全结束后,其他线程再继续。

7 同步技术的原理

使用了一个锁对象,这个对象叫同步锁,也叫对象监视器。多个线程一起抢夺CPU执行权,谁抢到了,谁执行run方法,遇到同步代码块。

此时,抢到CPU的当前线程T0会检查同步代码块是否有锁对象,如果有,则获取锁对象,进入到同步中进行。

另一进程T1抢到CPU后发现没有锁对象了,则进入阻塞状态,等待锁对象的归还,直到上一进程T0执行完同步代码块才归还锁对象T1进程才可以获取到锁对象,进入到同步中执行。

同步中的线程,没有执行完毕不会释放锁,同步外的线程没有锁对象,无法进入同步代码块。同步保证了只有一个线程再同步中执行共享数据,保证安全,但牺牲了效率。

7.1 同步方法

定义同步方法解决线程安全问题

  1. 把访问了共享数据的代码取出来,放到一个方法中
  2. 在方法上添加synchronized

同步方法也会锁住方法内部,只让一个线程执行,锁对象是实现类对象new RunnableImpl(),也就是this。

public class RunnableImpl implements Runnable {//共享票源private int tickets = 100;@Overridepublic void run() {while(true){sell();}}public synchronized void sell(){if(tickets > 0){try {Thread.sleep(10);} catch (InterruptedException e) {e.printStackTrace();}String name = Thread.currentThread().getName();System.out.println("窗口"+name+"在卖第"+tickets+"张票");tickets--;}}
}

7.2 静态同步方法

加static关键字,锁对象不是this,this是创建对象之后产生的,static优先于对象的创建,静态同步方法的锁对象是本类的class属性—>class文件对象(反射)

RunnableImpl.class

7.3 Lock锁

JDK1.5之后出现Lock接口,实现了synchronized方法和语句,可获得更广泛的锁操作。

  1. 在成员位置创建一个ReentrantLock对象
  2. 在可能会出现安全问题的代码前调用Lock接口的lock方法获取锁
  3. 在可能会出现安全问题的代码后调用Lock接口的unlock方法释放锁
public class RunnableImpl implements Runnable {//共享票源private static int tickets = 100;Lock lock = new ReentrantLock();@Overridepublic void run() {while(true){lock.lock();if(tickets > 0){try {Thread.sleep(10);String name = Thread.currentThread().getName();System.out.println("窗口"+name+"在卖第"+tickets+"张票");tickets--;} catch (InterruptedException e) {e.printStackTrace();}finally {lock.unlock();}}}}
}

8 线程状态

  • 新建状态:new
  • 运行状态:runnable
  • 阻塞状态:blocked
  • 死亡状态:terminated
  • 休眠状态:time_waiting【等待时间】
  • 永久等待:waiting【等待唤醒】

new —> start() + CPU —> runnable
new —> start() - CPU —> blocked
在这里插入图片描述

runnable —> stop() / run(over) —> terminated
runnable —> sleep / wait —> timed_waiting
在这里插入图片描述
timed_waiting —> time over - CPU—> blocked
timed_waiting —> time over + CPU—> runnable
在这里插入图片描述
runnable —> Object.wait() —> waiting
waiting —> Object.notify() + CPU —> runnable
waiting —> Object.notify() - CPU —> blocked

9 线程通信

9.1 等待唤醒案例

  1. 创建消费者线程:申请资源种类和数量,调用wait方法,放弃CPU,进入waiting状态。
  2. 创建生产者线程:产生资源之后,调用notify唤醒消费者。
  3. 两个线程必须被同步代码块包裹,确保只有一个在执行。
  4. 同步的锁对象必须唯一,只有锁对象可以调用wait和notify方法。
public class WaitAndNotify {public static void main(String[] args) {//锁对象Object obj = new Object();//消费者new Thread(){@Overridepublic void run() {while(true){synchronized (obj){System.out.println("申请资源");try {obj.wait();} catch (InterruptedException e) {e.printStackTrace();}}System.out.println("拿到资源");System.out.println("------------");}}}.start();//生产者new Thread(){@Overridepublic void run() {while (true){try {System.out.println("准备资源");Thread.sleep(500);} catch (InterruptedException e) {e.printStackTrace();}synchronized (obj){obj.notify();System.out.println("资源已备好");}}}}.start();}
}

Object.wait(long m):无参数的wait需要等待notify唤醒,有参数的wait等到时间结束后,进入到runnable(有CPU)或者blocked(无CPU)状态,相当于sleep(long m),但如果时间结束前,notify被调用,则提前醒来。

Object.notifyAll():唤醒监视器上所有的线程。

9.2 生产者和消费者案例

  1. 包子
public class BaoZi {String pi;//包子皮String xian;//包子馅boolean flag = false;//是否有包子
}
  1. 包子铺
public class BaoZiPu extends Thread {private BaoZi bz;//锁对象public BaoZiPu(BaoZi bz){this.bz = bz;}@Overridepublic void run() {int count = 0;//持续生产包子while(true){synchronized (bz){if(bz.flag == true){try {bz.wait();} catch (InterruptedException e) {e.printStackTrace();}}//被唤醒后执行 包子铺生产包子if(count % 2 == 0){bz.pi = "薄皮";bz.xian = "三鲜";}else {bz.pi = "厚皮";bz.xian = "牛肉";}count++;System.out.println("包子铺正在生产:"+bz.pi+bz.xian+"包");try {Thread.sleep(3000);} catch (InterruptedException e) {e.printStackTrace();}bz.flag = true;bz.notify();System.out.println(bz.pi+bz.xian+"包已生产好,吃货可以开始吃包子了");}}}
}
  1. 吃货
public class ChiHuo extends Thread {private BaoZi bz;public ChiHuo(BaoZi bz){this.bz = bz;}@Overridepublic void run() {while(true){synchronized (bz){if(bz.flag == false){try {bz.wait();} catch (InterruptedException e) {e.printStackTrace();}}System.out.println("吃货正在吃"+bz.pi+bz.xian+"包");//吃完包子bz.flag = false;//唤醒包子铺bz.notify();System.out.println("吃货已经吃完了"+bz.pi+bz.xian+"包,包子铺开始生产包子");System.out.println("===================================================");}}}
}
  1. 测试类
public class Test {public static void main(String[] args) {BaoZi bz = new BaoZi();BaoZiPu bzp = new BaoZiPu(bz);ChiHuo ch = new ChiHuo(bz);bzp.start();//生产包子ch.start();//吃包子}
}

10 线程池

10.1 概念

线程池:其实就是一个容纳多个线程的容器,其中的线程可以反复使用,省去了频繁创建线程对象的操作,无需反复创建线程而消耗过多资源。

线程池的优点

  1. 降低资源消耗。减少了创建和销毁线程的次数,每个工作线程都可以被重复利用,可执行多个任务。
  2. 提高响应速度。当任务到达时,任务可以不需要的等到线程创建就能立即执行。
  3. 提高线程的可管理性。可以根据系统的承受能力,调整线程池中工作线线程的数目,防止因为消耗过多的内存,而把服务器累趴下(每个线程需要大约1MB内存,线程开的越多,消耗的内存也就越大,最后死机)。

10.2 线程池的使用

JDK1.5出现线程池的工厂类Executor用来生产线程池

Executors类的静态方法:

  • newFixedThreadPool(int nThread):创建可重用固定线程数的线程池,返回值是ExecutorService接口的实现类对象,使用ExecutorService接口接收【面向接口编程】

ExecutorService接口:

  • shutdown:关闭销毁线程池
  • submit(Runnable task):提交一个Runnable任务用于执行

使用步骤

  1. 使用工厂类Executors里面的静态方法newFixedThreadPool生产一个线程池
  2. 创建一个实现类,实现Runnable,重写run,设置线程任务
  3. 调用ExecutorService中的方法submit,传递线程任务(实现类),开启线程,执行run方法。
  4. 调用ExecutorService中的方法shutdown销毁线程池【不建议销毁线程池】
public class RunnableImpl implements Runnable {@Overridepublic void run() {System.out.println(Thread.currentThread().getName()+"创建了一个新的线程执行");}
}
public class ThreadPool {public static void main(String[] args) {ExecutorService es = Executors.newFixedThreadPool(2);es.submit(new RunnableImpl());es.submit(new RunnableImpl());es.submit(new RunnableImpl());es.submit(new RunnableImpl());}
}

pool-1-thread-2创建了一个新的线程执行
pool-1-thread-1创建了一个新的线程执行
pool-1-thread-2创建了一个新的线程执行
pool-1-thread-1创建了一个新的线程执行

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

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

相关文章

机器学习——多元线性回归分析(multiple regression)及应用

版权声明&#xff1a;本文为博主原创文章&#xff0c;未经博主允许不得转载。 https://blog.csdn.net/loveliuzz/article/details/78006493 </div><link rel"stylesheet" href"https://csdnimg.cn/release/phoenix/template/css/ck_htmledit_vi…

肖仰华 | 知识图谱研究的回顾与展望

本文转载自公众号知识工场。 本文整理自2017年10月19日肖仰华教授在知识图谱前沿技术课程&#xff08;华东师范大学站&#xff09;所做的报告&#xff0c;报告的题目为《知识图谱研究的回顾与展望》。 大家好&#xff0c;很多人在对知识图谱的研究或者落地方面都表现出了极大的…

Java应用性能调优工具介绍及实践

一、背景 &#xff08;1&#xff09;、随着微服务架构的逐渐推广&#xff0c;一个大型的单个应用程序被拆分为数个微服务系统&#xff0c;这为研发人员的本地调试跟踪带来困难 &#xff08;2&#xff09;、在微服务架构中&#xff0c;由于业务的复杂性&#xff0c;常常一个业务…

Google、MS和BAT教给我的面试真谛

大家好呀&#xff0c;我是「小鹿鹿鹿」&#xff0c;我又来啦&#xff5e;&#xff5e;趁大家还有依稀印象赶紧乘热打铁&#xff5e;&#xff5e;这次聊一聊关于面试的一些小想法&#xff0c;希望和大家交流交流&#xff5e;&#xff5e;虽然资历尚浅&#xff0c;但是也面过不少…

AAAI 2018 论文 | 蚂蚁金服公开最新基于笔画的中文词向量算法

导读&#xff1a;词向量算法是自然语言处理领域的基础算法&#xff0c;在序列标注、问答系统和机器翻译等诸多任务中都发挥了重要作用。词向量算法最早由谷歌在2013年提出的word2vec&#xff0c;在接下来的几年里&#xff0c;该算法也经历不断的改进&#xff0c;但大多是仅适用…

甲子光年 | 为什么知识图谱终于火了?

本文转载自公众号甲子光年。如果知识是人类进步的阶梯&#xff0c;知识图谱就是AI进步的阶梯。作者&#xff5c;金丝猴编辑&#xff5c;甲小姐设计&#xff5c;孙佳栋微信&#xff5c;甲子光年 (ID:jazzyear)“知识图谱”相较于AI其他分支&#xff0c;似乎是最后一个热起来的赛…

Java多线程并发编程

一、线程池 1.1、什么是线程池 线程池是一种多线程的处理方式&#xff0c;利用已有线程对象继续服务新的任务&#xff08;按照一定的执行策略&#xff09;&#xff0c;而不是频繁地创建销毁线程对象&#xff0c;由此提高服务的吞吐能力&#xff0c;减少CPU的闲置时间。具体组成…

Step-by-step to Transformer:深入解析工作原理(以Pytorch机器翻译为例)

大家好&#xff0c;我是青青山螺应如是&#xff0c;大家可以叫我青青&#xff0c;工作之余是一名独立摄影师。喜欢美食、旅行、看展&#xff0c;偶尔整理下NLP学习笔记&#xff0c;不管技术文还是生活随感&#xff0c;都会分享本人摄影作品&#xff0c;希望文艺的技术青年能够喜…

知识图谱与智能问答基础理解

什么是知识图谱&#xff1f; 知识图谱本质上是语义网络&#xff0c;是一种基于图的数据结构&#xff0c;由节点(Point)和边(Edge)组成。在知识图谱里&#xff0c;每个节点表示现实世界中存在的“实体”&#xff0c;每条边为实体与实体之间的“关系”。知识图谱是关系的最有效的…

鲍捷 | 知识图谱从 0 级到 10 级简化版

本文转载自公众号&#xff1a;文因学堂。文因学以前写过几个进阶指南&#xff0c;可能都太难&#xff0c;不接地气。这里重新写一个更实事求是、更便于工程落地的版本0级&#xff1a;掌握正则表达式、SQL、JSON和一门支持if-then-else的高级语言 —— 是的&#xff0c;这些不是…

Java并发优化思路

一、并发优化 1.1、Java高并发包所采用的几个机制 &#xff08;1&#xff09;、CAS&#xff08;乐观操作&#xff09; jdk5以前采用synchronized&#xff0c;对共享区域进行同步操作&#xff0c;synchronized是重的操作&#xff0c;在高并发情况下&#xff0c;会引起线…

他与她,一个两年前的故事

“ 有没有那个Ta&#xff0c;值得你一生去守护”1她能力出众&#xff0c;业务能力无人能出其左右&#xff1b;他资质平庸&#xff0c;扮演一个很不起眼的角色&#xff1b;她国色天香&#xff0c;是整个公司上上下下关注的焦点&#xff1b;他其貌不扬&#xff0c;甚至很多人根本…

科普 | 知识图谱相关的名词解释

知识图谱&#xff08;Knowledge Graph&#xff09;是谷歌于2012年提出。企业通常出于商业目的去设计新的概念和名词。但每一个概念的提出都有其历史渊源和本质内涵。下面列举了知识图谱相关的几个概念&#xff0c;并简要阐明了它们与知识图谱的关系和区别。Knowledge Base&…

搜索引擎核心技术与算法 —— 倒排索引初体验

今天开启一个新篇章——智能搜索与NLP。本篇章将由羸弱菜鸡小Q和大家一同学习与智能搜索相关的知识和技术&#xff0c;希望能和大家一同学习与进步&#xff0c;冲鸭&#xff01;&#xff01;这里首先区分两个概念&#xff1a;搜索和检索检索&#xff1a;数据库时代的概念&#…

微服务设计原则和解决方案

一、微服务架构演进过程 近年来我们大家都体会到了互联网、移动互联带来的好处&#xff0c;作为IT从业者&#xff0c;在生活中时刻感受互联网好处的同时&#xff0c;在工作中可能感受的却是来自自互联网的一些压力&#xff0c;那就是我们传统企业的IT建设也是迫切需要转型&…

技术动态 | 知识可视化,连接和探究知识之间的联系!

本文转载自公众号&#xff1a;东湖大数据交易中心。大数据百人会线上沙龙 第八期3月1日晚八点&#xff0c;大数据百人会沙龙第八期主讲嘉宾——北京知珠传媒科技有限公司CEO郝庆一先生&#xff0c;分享他对可视化的理解&#xff0c;以及连接知识、探究知识之间的关系。1可视化…

潜在语义分析原理以及python实现代码!!!!

在Wiki上看到的LSA的详细介绍&#xff0c;感觉挺好的&#xff0c;遂翻译过来&#xff0c;有翻译不对之处还望指教。 原文地址&#xff1a;http://en.wikipedia.org/wiki/Latent_semantic_analysis 前言 浅层语义分析&#xff08;LSA&#xff09;是一种自然语言处理中用到的方法…

史上最可爱的关系抽取指南?从一条规则到十个开源项目

正文来自订阅号&#xff1a;AINLP正文作者&#xff1a;太子長琴&#xff08;NLP算法工程师&#xff09;小夕&#xff0c;我来给你出个题&#xff0c;看看你的反应如何&#xff1f;好呀好呀&#xff0c;你说~“梁启超有一个著名的儿子&#xff0c;叫梁思成;同时还有一个著名的学…

论文浅尝 | Leveraging Knowledge Bases in LSTMs

Yang, B., Mitchell, T., 2017. Leveraging Knowledge Bases in LSTMs for Improving Machine Reading. Association for Computational Linguistics, pp. 1436–1446.链接&#xff1a;http://www.aclweb.org/anthology/P/P17/P17-1132.pdf这篇论文是今年发表在 ACL 的一篇文章…

支付系统-对账系统

在支付系统中&#xff0c;资金对账在对账中心进行&#xff0c;将系统保存的账务流水与银行返回的清算流水和清算文件进行对账&#xff0c;核对系统账务数据与银行清算数据的一致性&#xff0c;保证支付机构各备付金银行账户每日的预计发生额与实际发生额一致。 一、清算对账系…