1200字范文,内容丰富有趣,写作的好帮手!
1200字范文 > 面试准备——Java回顾:高级编程(多线程 常用类 集合 泛型 IO流 反射 动态代理 新特性)

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

时间:2018-12-17 21:32:41

相关推荐

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

多线程

程序、进程、线程

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

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

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

进程作为资源分配的单位,线程作为调度和执行的单位,有独立的运行站和程序计数器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

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。