java技术总结

知其然,不知其所以然 !在技术的海洋里,遨游!

做一个积极的人

编码、改bug、提升自己

我有一个乐园,面向编程,春暖花开!


本篇以一些问题开头,请先不看答案,自己思考一下,看一下你能回答上来多少! 本文内容较多,可以收藏后查看!

思考一下

1、都说String是不可变的,为什么我可以这样做呢? String a = "1"; a = "2";

2、HashMap的实现原理 ?

3、写出三种单例模式,如果能考虑线程安全最好?

4、ArrayList和LinkedList有什么区别 ?

5、什么是线程安全,为什么会出现线程安全问题?

6、实现线程的二种方式?

7、Lock与Synchronized的区别?

8、JVM的内存结构 ?

9、请解释如下jvm参数的含义:

-server -Xms512m -Xmx512m -Xss1024K -XX:PermSize=256m -XX:MaxPermSize=512m -XX:MaxTenuringThreshold=20 -XX:CMSInitiatingOccupancyFraction=80 -XX:+UseCMSInitiatingOccupancyOnly

10、数据库隔离级别有哪些,各自的含义是什么,MYSQL默认的隔离级别是是什么?


1、都说String是不可变的,为什么我可以这样做呢,String a = "1";a = "2";

先看一段代码,然后通过代码和一幅图进行讲解!

public class StringTest { public static void main(String[] args) { String s = "ABCabc"; System.out.println("s1.hashCode() = " + s.hashCode() + "--" + s); s = "123456"; System.out.println("s2.hashCode() = " + s.hashCode() + "--" + s); //运行后输出的结果不同,两个值的hascode也不一致, //说明设置的值在内存中存储在不同的位置 }}

【首先创建一个String对象s,然后让s的值为“ABCabc”, 然后又让s的值为“123456”。 从打印结果可以看出,s的值确实改变了。那么怎么还说String对象是不可变的呢?

其实这里存在一个误区: s只是一个String对象的引用,并不是对象本身。对象在内存中是一块内存区,成员变量越多,这块内存区占的空间越大。引用只是一个4字节的数据,里面存放了它所指向的对象的地址,通过这个地址可以访问对象。

也就是说,s只是一个引用,它指向了一个具体的对象,当s=“123456”; 这句代码执行过之后,又创建了一个新的对象“123456”, 而引用s重新指向了这个心的对象,原来的对象“ABCabc”还在内存中存在,并没有改变。内存结构如下图所示:

---图片摘自【Java中的String为什么是不可变的? -- String源码分析】

  • 相关参考文章

1:【知乎-胖胖 回答】如何理解 String 类型值的不可变?: https://www.zhihu.com/question/20618891

2:8 张图理解 Java :https://mp.weixin.qq.com/s/nidDtGZ9P-YJaXSxvZPv_w

一图讲解String


2、HashMap的实现原理

HashMap 我之前的专栏中也有写过,分析HashMap要注意JDK版本,jdk1.7和jdk1.8中底层的实现就有不同。

说简单点HashMap是一个集合,通过put(key,value)存储数据,然后使用get(key)获取数据。

实现原理是基于hashing原理,使用hash算法实现。 jdk1.7 数组+链表 jdk1.8 数组+链表+红黑树

  • 详情可参考下面博文:
  • java集合系列——Map之HashMap介绍(八): http://blog.csdn.net/u010648555/article/details/60324303 HashMap的工作原理 : http://www.importnew.com/7099.html Java8系列之重新认识HashMap : http://www.importnew.com/20386.html

3、写出三种单例模式,如果能考虑线程安全最好

首先总结目前实现单例模式的方式有以下五种:

  • 饿汉式,线程安全
  • 懒汉式,线程不安全(注意加synchronized,变线程安全)
  • 双重检验锁(注意将instance 变量声明成 volatile,并注意jdk版本大于等于1.5)
  • 静态内部类 ,线程安全
  • 枚举,线程安全

注:推荐使用后面三种

具体代码就不一一写了:如果想了解具体的代码如何写,点击下面:

你真的会写单例模式吗——Java实现: http://www.importnew.com/18872.html

  • 双重检验锁 : jdk1.5以后才能正确工作
public class Singleton { private static volatile Singleton singleton = null; private Singleton(){} public static Singleton getSingleton(){ if(singleton == null){ synchronized (Singleton.class){ if(singleton == null){ singleton = new Singleton(); } } } return singleton; } }
  • 静态内部类 : 延时加载,线程安装
public class Singleton { private static class Holder { private static Singleton singleton = new Singleton(); } private Singleton(){} public static Singleton getSingleton(){ return Holder.singleton; }}
  • 枚举 : [Effective Java] 推荐尽可能地使用枚举来实现单例
public enum Singleton { INSTANCE; private String name; public String getName(){ return name; } public void setName(String name){ this.name = name; }}

4 、ArrayList和LinkedList有什么区别

我之前博客也有写过 , java集合系列——List集合总结 :http://blog.csdn.net/u010648555/article/details/59708627

这里在说明一下: 简单介绍 1 ArrayList是基于数组实现的,是一个数组队列。可以动态的增加容量!

  1. LinkedList是基于链表实现的,是一个双向循环列表。可以被当做堆栈使用!

使用场景

  1. 当集合中对插入元素数据的速度要求不高,但是要求快速访问元素数据,则使用ArrayList!
  2. 当集合中对访问元素数据速度不做要求不高,但是对插入和删除元素数据速度要求高的情况,则使用LinkedList!

具体分析 1.ArrayList随机读取的时候采用的是get(index),根据指定位置读取元素,而LinkedList则采用size/2 ,二分法去加速一次读取元素,效率低于ArrayList! 2.ArrayList插入时候要判断容量,删除时候要将数组移位,有一个复制操作,效率低于LinkList!而LinkedList直接插入,不用判断容量,删除的时候也是直接删除跳转指针节点,没有复制的操作!


5、什么是线程安全,为什么会出现线程安全问题?

【参考书 :Java并发编程实战】:https://book.douban.com/subject/10484692/

线程安全就是多线程访问时,采用了加锁机制,当一个线程访问该类的某个数据时,进行保护,其他线程不能进行访问直到该线程读取完,其他线程才可使用。不会出现数据不一致或者数据污染。[百度百科:线程安全]

线程安全 = 线程(多个线程) + 数据一致!

思考1:为什么会出现线程安全问题?

从百度百科的概念可以知道,发送线程安全问题的两个条件:

  • 多线程访问
  • 访问某个数据,(这里强调一下,某个数据是实例变量即对线程是共享的)
  • 这两个条件都必须满足,缺一不可,否则不会出现线程安全问题。

思考2:怎么解决线程安全问题?

通过加锁机制,可以使用关键字synchronized,或者java并发包中的Lock。还有在使用集合中的类如ArrayList或者HashMap时要考虑是否存在线程安全问题,如果存在最好使用ConcurrentHashMap替代hashMap,或者使用Collections.synchronizedXXX进行封装!

  • 实例:通过一段代码演示线程安全和非线程安全
/** * 线程安全和线程不安全---简单实例 * * @author:dufy * @version:1.0.0 * @date 2017/10/13 */public class ThreadSafey { private int countUnSafe = 0;//实例变量 private int countSafe = 0; //线程不安全的方法 public void addUnSafe(){ try { Thread.sleep(100);//为了更好的测试。休眠100ms } catch (InterruptedException e) { e.printStackTrace(); } countUnSafe ++; System.out.println("countUnSafe = " + countUnSafe); } //线程安全的方法 //这里也可以使用使用同步代码块的方式,建议在实际开发使用同步代码块,相对比同步方法好很多, 也可以使用Lock进行加锁! public synchronized void addSafe(){ try { Thread.sleep(100);//为了更好的测试。休眠100ms } catch (InterruptedException e) { e.printStackTrace(); } countSafe ++; System.out.println("countSafe = " + countSafe); } public static void main(String[] args) { ThreadSafey ts = new ThreadSafey(); UnSafeT unSafeT = new UnSafeT(ts); SafeT safeT = new SafeT(ts); //启动10个线程 for (int i = 0; i < 10; i++) { Thread thread = new Thread(unSafeT); thread.start(); } //启动10个线程 for (int i = 0; i < 10; i++) { Thread thread = new Thread(safeT); thread.start(); } }}//不安全线程测试类class UnSafeT implements Runnable{ private ThreadSafey threadSafey; public UnSafeT(ThreadSafey threadSafey){ this.threadSafey = threadSafey; } @Override public void run() { threadSafey.addUnSafe(); }}//安全线程测试类class SafeT implements Runnable{ private ThreadSafey threadSafey; public SafeT(ThreadSafey threadSafey){ this.threadSafey = threadSafey; } @Override public void run() { threadSafey.addSafe(); }}

运行结果如下,多次运行后,发现countUnSafe总是有重复的值,并且不按照顺序输出,最后的结果也不是10; countSafe 按照顺序打印,最后的结果也是10。如果你运行了上面的代码,可能和我执行下面打印的不一样,但是结论是一样的。

countUnSafe = 3countUnSafe = 3countUnSafe = 3countUnSafe = 3countUnSafe = 3countUnSafe = 5countUnSafe = 8countUnSafe = 8countUnSafe = 6countUnSafe = 5countSafe = 1countSafe = 2countSafe = 3countSafe = 4countSafe = 5countSafe = 6countSafe = 7countSafe = 8countSafe = 9countSafe = 10

线程安全说简单了就上面这些内容,如何深入需要知道线程的工作原理,JVM下线程是如何进行工作,为什么实例变量会存在线程安全问题,而私有变量不会出现,这就和变量在内存中创建和存储的位置有关。下面进行简单的说明,不会一一展开了。

在程序运行后JVM中有一个主内存,线程在创建后也会有一个自己的内存(工作内存),会拷贝主内存的一些数据,每个线程之间能够共享主内存,而不能访问其他线程的工作内存,那么一个变量是实例变量的时候,如果没有加锁机制,就会出现线程安全问题。

比如:系统有线程A和线程B,这两个线程同时访问了addUnSafe方法,并将countUnsafe变量拷贝在自己的内存中(countUnsafe = 0),然后进行操作,那么这两个线程 都执行countUnsafe++,这两个线程的工作内存中countUnsafe = 1;然后写回主内存,此时主内存countUnsafe = 1,当另一个线程C访问时候,C工作内存操作的countUnsafe的值就是1,此时发生了线程安全问题。

【图片来自--java并发编程艺术-第二章 java内存模型抽象结构】

JMM模型

先就讲这么多了,这些在面试中是有定的深度了。后面有时间专门在深入总结。


6、实现线程的二种方式

Java中有两种方式实现多线程,一种是继承Thread类,一种是实现Runnable接口。具

  • 实现Runnable接口
  • 继承Thread类

注意:线程的启动是调用start()方法,而不是run()方法!

举例并进行解释

1.直接调用run方法实例:

public class TestTheadDemo { public static void main(String[] args) { for (int i = 0; i < 10; i++) { ThreadTest thread = new ThreadTest(); thread.run();// thread.start(); } }}class ThreadTest extends Thread{ @Override public void run() { System.out.println("当前线程 : " + Thread.currentThread().getName()); }}

运行结果 :当前线程全部是main线程,相当于ThreadTest类的thread对象直接调用了run()方法。(直接调用run方法只是一个普通的单线程程式)

当前线程 : main当前线程 : main当前线程 : main当前线程 : main当前线程 : main

2.调用start()方法 将上面的代码 注释的thread.start();打开, thread.run();注释!

运行结果 :发现启动了不同的线程进行执行。

当前线程 : Thread-0当前线程 : Thread-5当前线程 : Thread-3当前线程 : Thread-4当前线程 : Thread-2当前线程 : Thread-1当前线程 : Thread-7当前线程 : Thread-6当前线程 : Thread-9

查看start()方法的源码中,发现有个地方

 public synchronized void start() { //代码省略.... try { start0();//注意这个地方,调用了native本地方法 started = true; } finally { //代码省略.... } } //本地方法 private native void start0();

总结: 调用start()方法,虚拟机JVM通过执行本地native方法start0和操作系统cup进行交互,此时线程并没有正在立即执行,而是等待cup的调度,当cpu分配给线程时间,线程才执行run()方法!

更多相关内容可以参考 [java多线程编程核心技术: 高洪岩 ] :https://book.douban.com/subject/26555197/


7、Lock与Synchronized的区别

在Java中Lock与Synchronized都可以进行同步操作,保证线程的安全,就如上面第一问提到的,线程安全性问题。下面进行简单的额介绍!

(1)、Synchronized的简单介绍

synchronized同步的原理

关键字synchronized可以修饰方法或者以同步块的形式来使用,它主要确保多个线程在同一时刻,只能有一个线程处于方法或者同步块 中,保证了线程对变量访问的可见性和排他性。

synchronized同步实现的基础

  1. 普通同步方法,锁是当前实例对象
  2. 静态同步方法,锁是当前类的class对象
  3. 同步方法块,锁是括号里面的对象

[一张图讲解对象锁和关键字synchronized修饰方法(代码块] :http://blog.csdn.net/u010648555/article/details/78138225

【死磕Java并发】-----深入分析synchronized的实现原理]http://blog.csdn.net/chenssy/article/details/54883355

注:有兴趣也可以看看 volatile关键字!

关键字volatile可以用来修饰字段(成员变量),就是告知程序任何对该变量的访问均需要从共享内存中获取,而对它的改变必须同步刷新回共享内存中,保证所有线程对变量访问的可见性。

关键字volatile和关键字synchronized均可以实现线程间通信!

(2)、Lock的简单介绍

首先明确Lock是Java 5之后,在java.util.concurrent.locks包下提供了另外一种方式来实现同步访问。 Lock是一个接口,其由三个具体的实现:ReentrantLock、ReetrantReadWriteLock.ReadLock 和 ReetrantReadWriteLock.WriteLock,即重入锁、读锁和写锁。增加Lock机制主要是因为内置锁存在一些功能上局限性。

Java并发编程系列之十六:Lock锁 : http://blog.csdn.net/u011116672/article/details/51064186

区别总结: 1.synchronized是Java语言的关键字,Lock是一个类,通过这个类可以实现同步访问;

2.synchronized是在JVM层面上实现的,不但可以通过一些监控工具监控synchronized的锁定,而且在代码执行时出现异常,JVM会自动释放锁定,但是使用Lock则不行,Lock是通过代码实现的,要保证锁定一定会被释放,就必须将unLock(),最好放到finally{}中。 3.Lock有ReetrantLock(可重入锁)实现类,可选的方法比synchronized多,使用更灵活。 4..并不是Lock就比synchronized一定好,因为synchronized在jdk后面的版本也在不断优化。在资源竞争不是很激烈的情况下,synchronized的性能要优于ReetrantLock,但是在资源竞争很激烈的情况下,synchronized的性能会下降很多,性能不如Lock。

Lock和synchronized的区别和使用:http://www.cnblogs.com/baizhanshi/p/6419268.html

8、JVM的内存结构

下图:【图片版本-深入理解Java虚拟机:JVM高级特性与最佳实践:周志明】

Jvm内存模型

​ 还有这个博文: JVM内存结构图解 :http://blog.csdn.net/coffeelifelau/article/details/52534672

9、请解释如下jvm参数的含义

-server -Xms512m -Xmx512m -Xss1024K -Xmn256m-XX:PermSize=256m -XX:MaxPermSize=512m -XX:MaxTenuringThreshold=20 -XX:CMSInitiatingOccupancyFraction=80 -XX:+UseCMSInitiatingOccupancyOnly

-server :服务器模式 注:JVM client模式和server模式,生产环境请使用-server,性能更好! [JVM client模式和Server模式的区别]:https://zhidao.baidu.com/question/1703232626938481420.html

-Xms512m :JVM初始分配的堆内存,一般和Xmx配置成一样以避免每次gc后JVM重新分配内存。

-Xmx512m :JVM最大允许分配的堆内存,按需分配

-Xss1024K :设置每个线程的堆栈大小

-Xmn256m :年轻代内存大小,整个JVM内存=年轻代 + 年老代 + 持久代

-XX:PermSize=256m :设置持久代(perm gen)初始值,默认物理内存的1/64(注意:jdk 8 已经没有此参数)

-XX:MaxPermSize=512m : 设置持久代最大值(注意:jdk 8 已经没有此参数)

**-XX:MaxTenuringThreshold=20 ** :垃圾最大年龄

**-XX:CMSInitiatingOccupancyFraction=80 ** :使用cms作为垃圾回收 使用80%后开始CMS收集

-XX:+UseCMSInitiatingOccupancyOnly :使用手动定义初始化定义开始CMS收集

还有很多很多参数。。。。

[一个性能较好的JVM参数配置] :http://developer.51cto.com/art/201507/486162.htm

10 、数据库隔离级别有哪些,各自的含义是什么,MYSQL默认的隔离级别是是什么?

事务指定了4种隔离级别(从弱到强分别是):

  1. Read Uncommitted:读未提交
  2. Read Committed:读提交
  3. Repeatable Read:重复读
  4. Serializable:序列化

1:Read Uncommitted(读未提交):一个事务可以读取另一个未提交事务的数据。

2:Read Committed(读提交):一个事务要等另一个事务提交后才能读取数据。

3:Repeatable Read(重复读):在开始读取数据(事务开启)时,不再允许修改操作。

4:Serializable(序列化):Serializable 是最高的事务隔离级别,在该级别下,事务串行化顺序执行,可以避免脏读、不可重复读与幻读。

大多数数据库默认的事务隔离级别是Read committed,比如Sql Server , Oracle。MySQL的默认隔离级别是Repeatable read。

在事务的并发操作中可能会出现脏读(dirty read),不可重复读(repeatable read),幻读(phantom read)。可参考:[理解事务的4种隔离级别]:http://blog.csdn.net/qq_33290787/article/details/51924963

注:Mysql查询事务隔离级别: 查询命令总结:

查看当前会话隔离级别:select @@tx_isolation;查看系统当前隔离级别:select @@global.tx_isolation;设置当前会话隔离级别:set session transaction isolation level repeatable read;设置当前会话隔离级别:set global transaction isolation level repeatable read;

谢谢你的阅读,如果您觉得这篇博文对你有帮助,请点赞或者喜欢,让更多的人看到!祝你每天开心愉快!


不管做什么,只要坚持下去就会看到不一样!在路上,不卑不亢!

博客首页 : http://blog.csdn.net/u010648555

愿你我在人生的路上能都变成最好的自己,能够成为一个独挡一面的人

© 每天都在变得更好的阿飞云

arraylist线程安全吗_Java的线程安全、单例模式、JVM内存结构等知识梳理相关推荐

  1. java多线程构造函数_java线程基础巩固---多线程与JVM内存结构的关系及Thread构造函数StackSize的理解...

    多线程与JVM内存结构的关系[了解]: 对于最后一个有疑问的构造中stackSize参数,其实学过编程滴人从参数字面就比较容易理解,栈大小嘛,这里从官方文档上来了解一下这个参数: 而之前在学习java ...

  2. 线程基础知识_线程生命周期_从JVM内存结构看多线程下的共享资源

    线程生命周期 线程状态 New: 线程创建(new Thread()) Runnable: 线程可运行(thread.start()), 注: 调用start并不一定是运行状态, 可能在等待CPU调度 ...

  3. java 编写线程公共类_Java实现线程间通信方式

    线程间通信的模型: 共享内存 消息传递 我们来做道题理解一下 题目: 有两个线程A.B,A线程向一个集合里面依次添加元素"abc"字符串,一共添加十次,当添加到第五次的时候,希望B ...

  4. JAVA线程六种状态_Java:线程的六种状态及转化

    多线程概述及创建方式 Java:线程的六种状态及转化 关于线程的生命周期,网上书上说法不一,难以统一,本篇做一个总结: java.lang.Thread.State枚举类中定义了六种线程的状态,可以调 ...

  5. java线程中等待_Java:线程中的Thread.sleep():没有等待

    线程没有睡觉我有问题. 我不能把我的整个代码放在这里.所以,为了重现,这里是一个等待5秒的基本代码. try { int millisec = 5000; System.out.println(new ...

  6. java daemon线程的作用_JAVA DAEMON线程的理解

    java线程分两种:用户线程和daemon线程.daemon线程或进程就是守护线程或者进程,但是java中所说的daemon线程和linux中的daemon是有一点区别的. linux中的daemon ...

  7. java线程交替执行_Java synchronized线程交替运行实现过程详解

    背景 用两个线程交替输出A-Z和1-26,即一个线程输出A-Z,另一个线程输出1-26 而且是交替形式 线程1输出A--线程二输出1 线程1输出B--线程二输出2 线程1输出C--线程二输出3 以此类 ...

  8. java线程池面试题_java之线程池面试题

    面试官:线程池有哪些?分别的作用是什么? 常用的线程池有: newSingleThreadExecutor newFixedThreadExecutor newCacheThreadExecutor ...

  9. java对象怎么创建_Java对象是怎么创建的(通过对象的创建,了解JVM内存结构)...

    在代码层面,我们通过new关键字创建一个对象: Object obj=new Object(); 而虚拟机中,创建一个对象,则经过了许多环节,JVM的内存结构可以通过另一篇文章了解:一个"H ...

最新文章

  1. FPGA之道(44)HDL中的隐患写法
  2. Spring Cloud Netflix Eureka 配置参数说明
  3. WebMagic功能——XPath、CSS选择器、正则表达式 || 抽取元素API、获取结果API || ​​​​​​​获取链接||​​​​​​​使用Pipeline保存结果
  4. 【风控场景】互利网上数字金融典型场景: 网购运费险
  5. java:能否得到一个对象的内存地址?
  6. Python技巧:不要在for与while循环后写else块
  7. css :after :berfor
  8. 红外线摄像机的选择与使用及原理
  9. Django之models
  10. WEB测试总结 (架构,设计)精华部分(转)
  11. 使用spring session+redis技术,解决负载均衡下的session共享问题
  12. python描述符魔术方法_Python类型转换的魔术方法详解
  13. jQuery Mobile中图标icon样式大全ui-icon-*
  14. java pkcs8_java – 如何在python中创建PKCS8 RSA签名
  15. 打工人必备的20款软件,让办公更高效
  16. 为知笔记Linux一键安装版
  17. vmware 虚拟机安装成功,但没有虚拟网卡的解决方法
  18. IDEMIA被选中提供其下一代指纹匹配引擎,成为英国内政部生物识别计划的一部分
  19. 【 MES】 MES的另一视角
  20. Distractor-aware Siamese Networks for Visual Object Tracking 论文学习

热门文章

  1. Relu神经网络输出预测全为0或1,对所有输入样本的预测概率也相同
  2. C++预处理指令#define,#if,#endif等
  3. Python中class的简单介绍
  4. C++中operator的两种用法
  5. Scrapy源码阅读分析_3_核心组件
  6. Windows 10 使用 Kali Linux子系统
  7. Java消息服务~@JmsListener集成
  8. html里table的scope,HTML表格的运用
  9. esxi root 密码规则_陌陌风控系统静态规则引擎aswan
  10. MySQL自动建立集合自动分片_1.mongodb初步使用总结