多线程

程序、进程、线程

程序:完成特定任务、用某种语言编写的一组指令的结合,一段静态的代码,静态对象;

进程:程序的一次执行过程,即正在执行的程序;

线程:进程可细化为线程,是一个程序内部的一条执行路径;

进程作为资源分配的单位,线程作为调度和执行的单位,有独立的运行站和程序计数器PC,线程开销小;

方法区和堆是一个进程一份,线程共享;

单核CPU和多核CPU:

并发:一个CPU在一段时间内执行多个任务;
并行:多个CPU同时执行多个任务;

多线程:
①提高应用程序响应,增强用户体验;
②提高CPU利用率
③改善程序结构,将长、复杂的进程分为多个线程;

java.lang.Thread创建多线程
每个线程都是通过某个特定Thread对象的 run() 方法来完成操作的;
该Thread对象的start()方法来启动这个线程,而非直接调用run();

多线程的创建(方式一):
①创建一个继承于Thread类的子类
②重写Thread类的run()
③创建Thread类的子类的对象
④通过对象调用start() => 启动线程并执行run

Thread.currentThread().getName()获取当前运行线程名字

Thread线程测试常用方法

创建线程的方式二:实现runnable接口

两种方式比较:

实现runnable接口从而创建线程比较好,像购票系统,只需要new一个实现了接口的对象,就可以将此参数传递到Thread类的构造器中从而创建多个Thread类的对象;


线程的几种状态:

线程同步

why?
解决像买票这种线程安全问题;

线程同步、线程安全问题
两种同步机制:
①同步代码块 synchronized (锁){ 操作共享数据(多个线程共同操作的数据)的代码 }
任何一个类的对象(类也可以,真正的面向对象,反射会将)都可以充当锁,但多个线程必须共用同一个锁;
②同步方法:在方法修饰前加上synchronized,将同步代码放方法体内;同步方法仍然涉及到同步监视器,只是不需要显示声明,非静态同步方法,同步监视器是this,静态同步方法,同步监视器是当前类本身;
③lock锁;

实现runnable接口,同步监视器可以用this来充当;
如果用同步方法,且是继承Thread类的形式,那么同步类就要声明为

同步的优点及局限性:

  • 解决了线程安全问题
  • 操作同步代码时,只有一个线程操作,其它线程等待,相当于一个单线程问题,效率低;

使用同步机制将单例模式中的懒汉式改写为线程安全的

class Bank
{private Bank(){}private static Bank instance=null;/* 方法一public static synchronized Bnak getInstance(){if(instance == null){instance=new Bank();}return instance;}*/// 方法2.1public static Bnak getInstance(){// 效率稍差synchronized(Bank.class){if(instance == null){instance=new Bank();}return instance;}}// 方法2.2public static Bnak getInstance(){if(instance == null){synchronized(Bank.class){if(instance == null){instance=new Bank();}}}return instance;}}

死锁问题

死锁:不同的线程相互占用同步资源,都在等待对方放弃自己需要的同步资源,形成死锁;

死锁不会有异常提示,所有线程处于阻塞状态,无法继续,要避免死锁;

lock锁
解决线程安全方式三:lock锁
private ReentrantLock lock = new ReentrantLock(fair=false) 若 fair = true,则先来先得,公平方式;若为false,则多人排队时下一个进入者随机;

private ReentrantLock lock = new ReentrantLock(fair=false);
lock.lock();
同步代码块
lock.unlock();

面试题:synchronized 与 lock 的异同

  • 都可以解决线程安全的问题;
  • synchronized在执行完相应的同步代码后,自动释放同步监视器;
  • lock需要手动开启同步和手动结束同步;

使用的优先顺序:lock -> synchronized同步代码块 -> 同步方法;

面试题:同步与异步
同步涉及到线程通信,多个线程访问临界区,只允许一个线程进去使用,其它的等待;
异步就是大家该走走,都并行地执行;

线程通信

wait、notify
wait进入阻塞状态时会释放锁;
notifyAll 会唤醒所有 wait 的线程

注意:

  • 这三个方法必须在同步方法or同步代码块中使用;
  • 三个方法的调用者必须是同步代码块or同步方法中的同步监视器;否则出现非法监视器状态异常;

面试题:sleep 和 wait 异同

  • 都能让线程进入阻塞;
  • 二者声明位置不同:Thread类中声明sleep(),object类中声明wait();
  • 调用范围不一样:wait要在同步中用,sleep可以在任何需要的场景下调用;
  • wait释放同步监视器,而sleep不释放;

练习:生产者消费者模型

import java.util.Arrays;
import java.util.Scanner;public class test {public static void main(String[] args) {Goods goods = new Goods();Producer producer = new Producer(goods);Comsumer comsumer = new Comsumer(goods);producer.setName("生产者1");comsumer.setName("消费者1");producer.start();comsumer.start();}
}class Goods {private int num = 0;public synchronized void produceGoods() {if (num < 20) {num++;System.out.println(Thread.currentThread().getName() + ":开始生产第" + num + "个产品");notify();} else {try {wait();} catch (InterruptedException e) {e.printStackTrace();}}}public synchronized void consumeGoods() {if (num > 0) {System.out.println(Thread.currentThread().getName() + ":开始消费第" + num + "个产品");num--;notify();} else {try {wait();} catch (InterruptedException e) {e.printStackTrace();}}}}class Producer extends Thread {private Goods good = new Goods();public Producer(Goods good) {this.good = good;}@Overridepublic void run() {System.out.println(getName() + "开始生成产品...");while (true) {try {sleep(10);// 如果要生产快些,那就让生产者睡得短些} catch (InterruptedException e) {e.printStackTrace();}good.produceGoods();}}
}class Comsumer extends Thread {private Goods good = new Goods();public Comsumer(Goods good) {this.good = good;}@Overridepublic void run() {System.out.println(getName() + "开始消费产品...");while (true) {try {sleep(10);} catch (InterruptedException e) {e.printStackTrace();}good.consumeGoods();}}
}

JDK5.0 定增创建线程方式:Callable接口

步骤:
①创建一个实现Callable接口的类
②实现call方法,将此线程需要执行的操作声明在call()中;
③创建Callable接口实现类的对象
④将此对象作为参数传递到FutureTask构造器中,创建FutureTash对象;
⑤将FutureTash对象作为参数传递到 Thread 类的构造器,创建Thread类的对象并 start() ;
⑥获取Callable中call()的返回值;

比实现runnable接口创建线程要强大;

创建线程的方式四:线程池
开发中不会一个个去创建线程,都会有线程池;

比如手机看新闻下滑过程,网路不好时,你下滑可能出现了文字但没出现图像,这几乎是主线程加载文字,用多个分线程去加载图片,不影响用户观看;



开发有框架,不需要掌握具体实现,但要记住创建多线程的四种方式;

常用类

字符串相关类

String

①JDK9之后是 byte[] 字节数组,主要是为了节省空间,还增加了一个coder字节,表示是否包含utf-16编码;
②String实现了Serializable接口:表示字符串支持序列化,序列化就是在这边是一个对象,通过网络传输字节过去后,在对面仍能够还原成这个对象;
③String内部定义了 final char[] value用于存储字符串数据;
④不可变性;
⑤当对字符串进行连接操作时,需要重写指定区域赋值,不能使用原有value进行赋值;

String str1=“abc” 和 String str2=new String(“abc”)的区别

注意含有字符串属性的类,字面对字符串的初始化也放常量池;

面试题:String str2 = new String(“abc”);在内存中创建了几个对象?

String str1 = "abc";  // 在常量池中
String str2 = new String("abc"); // 在堆上

当直接赋值时,字符串“abc”会被存储在常量池中,只有1份,此时的赋值操作等于是创建0个或1个对象。如果常量池中已经存在了“abc”,那么不会再创建对象,直接将引用赋值给str1;如果常量池中没有“abc”,那么创建一个对象,并将引用赋值给str1。

那么,通过new String(“abc”);的形式又是如何呢?答案是1个或2个。

当JVM遇到上述代码时,会先检索常量池中是否存在“abc”,如果不存在“abc”这个字符串,则会先在常量池中创建这个一个字符串。然后再执行new操作,会在堆内存中创建一个存储“abc”的String对象,对象的引用赋值给str2。此过程创建了2个对象。

当然,如果检索常量池时发现已经存在了对应的字符串,那么只会在堆内创建一个新的String对象,此过程只创建了1个对象。


图中两个String对象的value值的引用均为{char[3]@1355},也就是说,虽然是两个对象,但它们的value值均指向常量池中的同一个地址。当然,大家还可以拿一个复杂对象(Person)的字符串属性(name)相同时的debug结果进行比对,结果是一样的;

面试题:String str = “abc” + “def”;会创建几个对象
String str = "abc" + "def";
上面的问题涉及到字符串常量重载“+”的问题,当一个字符串由多个字符串常量拼接成一个字符串时,它自己也肯定是字符串常量。字符串常量的“+”号连接Java虚拟机会在程序编译期将其优化为连接后的值。

就上面的示例而言,在编译时已经被合并成“abcdef”字符串,因此,只会创建1个对象。并没有创建临时字符串对象abc和def,这样减轻了垃圾收集器的压力。

更深层的内容,参考文章:面试题系列第2篇:new String()创建几个对象?有你不知道的
字符串拼接

①常量与常量的拼接结果还在常量池,即常量池不会存在相同内容的常量;
②只要其中有一个是变量,结果就在堆中;
③如果拼接结果调用intern()方法,返回值就在常量池中;
④如果价格final 那么字符串就在常量池,那么拼接后还在常量池,比如final String s8="javaEE",String s9=s8+"hadoop"那么s3==s9 为true

面试题:字符串的不可变性

为什么ex.str输出为good,因为String的不可变性,当String对象作为参数传递给函数时,的确传的地址,但函数内部试图修改该地址所指向的内容,由于String的不可变性,使得它会在常量区中再创建"test ok",使形参str等于它的地址,但并未改变实参str的地址;

还是记住那句话:基本数据类型,传递值;引用数据类型,传递地址

字符串常量区的演变
1、 java7之前,方法区位于永久代(PermGen),永久代和堆相互隔离,永久代的大小在启动JVM时可以设置一个固定值,不可变;
2、 java7中,static变量从永久代移到堆中;
3、 java8中,取消永久代,方法存放于元空间(Metaspace),元空间仍然与堆不相连,但与堆共享物理内存,逻辑上可以认为在堆中,但是实际上我们说的堆指的是用于存放java对象的那些空间;




String 与 基本数据类型、包装类的转换:

String 与 char[] 数组的转换
String -> char[] String.toCharArray()
char[] -> String 构造器;
String 与 byte[] 之间的转换:调用String的getBytes(可指定编码类型);

StringBuffer

面试准备——Java回顾:高级编程(多线程、常用类、集合、泛型、IO流、反射、动态代理、新特性)相关推荐

  1. Java高级编程之常用类

    一.String类 java.lang.String类的使用 (一)概述 String: 字符串,使用一对""引起来表示. String声明为final的,不可被继承 String ...

  2. 浅谈Java锁,与JUC的常用类,集合安全类,常用辅助类,读写锁,阻塞队列,线程池,ForkJoin,volatile,单例模式不安全,CAS,各种锁

    浅谈JUC的常用类 JUC就是java.util.concurrent-包下的类 回顾多线程 Java默认有几个线程? 2 个 mian.GC Java 真的可以开启线程吗? 开不了,点击源码得知:本 ...

  3. 第11-15章枚举|异常|常用类|集合|泛型

    文章目录 第11章 枚举和注解 11.1举例 11.2枚举的二种实现方式 11.3enum 实现接口 11.4注解的理解 11.4基本的 Annotation 介绍 第12章 异常-Exception ...

  4. Day 7 2021.3.8Final-权限修饰符-部分常用类-集合-泛型

    Day 7 2020.3.8 Final关键字 package com.hong.Day007.Demo01; /* final关键字代表最终的.不可改变的 常见四种用法: 1.可以用来修饰一个类 格 ...

  5. 带你了解Java高级编程-----多线程

    带你了解Java高级编程-----多线程 对于Java的学习,基本的步骤是Java基础编程,掌握了Java语言的基本语法.数组.面向对象编程.异常处理这四部分之后,就要开始对Java高级编程进一步学习 ...

  6. 《Java Web高级编程——涵盖WebSockets、Spring Framework、JPA H

    2019独角兽企业重金招聘Python工程师标准>>> <Java Web高级编程--涵盖WebSockets.Spring Framework.JPA Hibernate和S ...

  7. 深入理解Java反射+动态代理,java开发面试笔试题

    我总结出了很多互联网公司的面试题及答案,并整理成了文档,以及各种学习的进阶学习资料,免费分享给大家. 扫描二维码加VX好友,拉你进[程序员面试学习交流群]免费领取.也欢迎各位一起在群里探讨技术. 答: ...

  8. Java基础巩固(二)异常,多线程,线程池,IO流,Properties集合,IO工具类,字符流,对象流,Stream,Lambda表达式

    一.异常,多线程 学习目标 : 异常的概述 异常的分类 异常的处理方式 自定义异常 多线程入门 1 异常的概述 1.1 什么是异常? 异常就是程序出现了不正常情况 , 程序在执行过程中 , 数据导致程 ...

  9. Java Review - 并发编程_原子操作类LongAdder LongAccumulator剖析

    文章目录 概述 小Demo 源码分析 重要的方法 long sum() reset sumThenReset longValue() add(long x) longAccumulate(long x ...

  10. 笔记整理2----Java语言基础(二)06 断点调试与数据加密+07 面向对象-类与对象+08 java常用API-基础+09 java集合+10 IO流-基础

    06 断点调试与数据加密+07 面向对象-类与对象+08 java常用API-基础+09 java集合+10 IO流-基础 第06天 java基础语法 今日内容介绍  Eclipse断点调试  基 ...

最新文章

  1. 双11特刊|一站式在线数据管理平台DMS技术再升级,高效护航双11
  2. DATEDIFF() 函数返回两个日期之间的天数
  3. 4.2第一个窗口程序
  4. 世界上最成熟、功能最全的加密库HELib
  5. 大型网站的架构设计问题--大型高并发高负载网站的系统架构
  6. 解读 JVM 类加载器-一篇文章简单易懂
  7. 渗透测试专业术语——防守篇
  8. java mail 踩坑 >>> 批量读取附件,附件重复
  9. echarts 地图自定义图标_echarts 地图自定义图标Symbol 及其颜色
  10. python期货基本面分析_Python股票期货交易利器,砖型图详细绘制教程!
  11. parse_depend_manifests Could not find dependent assembly LMicrosoft.Windows.Common-Controls
  12. xp系统迁移到固态硬盘_通过网络轻松传输,将XP迁移到Windows 7
  13. Js出库入库数量变化
  14. 如何让虚拟角色自然融入现实?
  15. 数字化时代的新形式下,如何提升客户满意度,实现客户成功?
  16. Word2019很卡的解决办法
  17. JZOJ5454. 【NOIP2017提高A组冲刺11.5】仔细的检查 树hash
  18. 2017.01.31 看别人的博客,自己也重新kankan
  19. 环境配置:前端程序员快速进行开发
  20. CTFSHOW新手杯MISC部分WriteUp

热门文章

  1. (转载)测试理论面试题
  2. 【MVC 过滤器的应用】ASP.NET MVC 如何统计 Action 方法的执行时间
  3. 简单混淆密码加密和解密
  4. 高质量C /C编程指南---第1章 文件机关
  5. APIO 2014 回文串(Manacher+后缀自动机+倍增)
  6. 日期格式化方法封装,对外暴露使用
  7. Eclipse打JAR包的使用
  8. hdu 4415 Assassin’s Creed 贪心
  9. vs2015打开慢的解决方法
  10. win10安装ubuntu系统出现的一些问题以及解决方案