Seul un crâne chauve peut vous rendre fort
Quand je bloguais auparavant, j'ai découvert que certains blogs bien écrits étaient collectés en silence. J'ai vérifié les omissions récemment, et certains points de connaissances sont relativement importants, mais je ne les ai pas écrits dans mon précédent blog, j'ai donc profité de mon temps libre pour les trier (recommandé : Entretien JAVA Recueil de questions).
Points de connaissance du texte :
Pool constant entier
Déballage et collage TCP
select、poll、epoll
Différence simple
Synchroniser l'optimisation du verrouillage après jdk1.6
Modèle de mémoire Java
Cet article s'efforce d'expliquer chaque point de connaissance de manière simple, j'espère que vous gagnerez quelque chose en le lisant
A. alors qu'il y a quelque temps, j'ai vu quelqu'un dans le groupe discuter du vrai ou du faux problème d'Integer, je pensais avoir déjà compris ce point de connaissance. Mais j’ai quand même fait une erreur, alors j’ai demandé conseil à un ami. Un ami m'a envoyé une autre photo :
Plus tard, j'ai découvert qu'elle provenait de "Compréhension approfondie de la machine virtuelle Java - Fonctionnalités avancées et meilleures pratiques JVM (2e édition) " )》Dans la section 10.3.2~
public class Main_1 { public static void main(String[] args) { Integer a = 1; Integer b = 2; Integer c = 3; Integer d = 3; Integer e = 321; Integer f = 321; Long g = 3L; Long h = 2L; System.out.println(c == d); System.out.println(e == f); System.out.println(c == (a + b)); System.out.println(c.equals(a + b)); System.out.println(g == (a + b)); System.out.println(g.equals(a + b)); System.out.println(g.equals(a + h)); } }
Vous pouvez d'abord y réfléchir, puis lire les réponses pour voir si vous pouvez bien faire les choses.
Avant de résoudre ce problème, je pense que beaucoup de gens savent déjà qu'il existe un pool de cache d'entiers en Java et que la taille du cache est : -128~127
La réponse est :
vrai
faux
vrai
vrai
vrai
faux
vrai
Une brève explication :
Utilisation de ==
:
Si En comparant des variables entières, la comparaison par défaut est valeur d'adresse.
Java's Integer maintient un pool de cache à partir de -128~127
S'il y a une expression d'opération d'un côté de la comparaison (comme un +b ), alors la comparaison est la valeur spécifique de
en utilisant equals()
:
Qu'il s'agisse d'un entier ou d'un long, equals()
est par défaut valeur numérique.
Méthode equals()
de Long, l'implémentation par défaut du JDK : déterminera s'il s'agit d'un type Long
Faites attention aux problèmes de déballage et d'emballage automatiques.
Décompilez-le et jetez un œil :
import java.io.PrintStream; public class Main_1 { public static void main(String[] paramArrayOfString) { Integer localInteger1 = Integer.valueOf(1); Integer localInteger2 = Integer.valueOf(2); Integer localInteger3 = Integer.valueOf(3); Integer localInteger4 = Integer.valueOf(3); Integer localInteger5 = Integer.valueOf(321); Integer localInteger6 = Integer.valueOf(321); Long localLong = Long.valueOf(3L); // 缓存池 System.out.println(localInteger3 == localInteger4); // 超出缓存池范围 System.out.println(localInteger5 == localInteger6); // 存在a+b数值表达式,比较的是数值 System.out.println(localInteger3.intValue() == localInteger1.intValue() + localInteger2.intValue()); // equals比较的是数值 System.out.println(localInteger3.equals(Integer.valueOf(localInteger1.intValue() + localInteger2.intValue()))); // 存在a+b数值表达式,比较的是数值 System.out.println(localLong.longValue() == localInteger1.intValue() + localInteger2.intValue()); // Long的equals()先判断传递进来的是不是Long类型,而a+b自动装箱的是Integer类型 System.out.println(localLong.equals(Integer.valueOf(localInteger1.intValue() + localInteger2.intValue()))); // ... 最后一句在这里漏掉了,大家应该可以推断出来 } }
L'outil de décompilation que j'utilise est jd-gui
, si vous l'avez Ce n'est pas déjà fait. Les étudiants qui ont essayé la décompilation peuvent télécharger et jouer :
https://github.com/java-decompiler/jd-gui/releases
Revue d'articles multithread :
ThreadLocal est aussi simple
Vous pouvez démarrer avec le multi-threading en trois minutes !
Analyse du code source du thread
Points de connaissance de base du multi-threading ! Lisez la suite pour apprendre le multithreading et obtenez deux fois le résultat avec la moitié de l'effort
Découvrez le mécanisme de verrouillage Java
AQS passe simplement par it
En savoir plus sur la sous-classe Lock
Vous ne voulez pas vraiment en savoir plus sur le pool de threads ?
L'impasse du multi-threading est si simple
Trois jeunes qui supportent le multi-threading Java
Lorsque j'ai écrit un article multi-thread auparavant, j'ai brièvement mentionné que les verrous synchronisés bénéficieront de diverses optimisations après jdk1.6 : adaptation aux verrous rotatifs, élimination des verrous, grossissement des verrous, verrous légers et verrous biaisés.
Je pensais que ces optimisations étaient très difficiles à comprendre, mais en fait~~~c'est facile à comprendre avec une simple compréhension.
La compétition de verrouillage est en mode noyau, et elle passera par le passage du mode utilisateur au mode noyau, oui cela prend plus de temps.
La raison pour laquelle le verrouillage rotatif apparaît est que les gens constatent que la plupart du temps, l'occupation du verrouillage ne durera que peu de temps, encore moins que le temps il faut du temps pour passer en mode noyau, alors laissez le thread attendre un temps limité avant d'entrer en mode noyau. Si le verrou peut être obtenu dans ce délai, cela évitera beaucoup de temps inutile Sinon. , puis entrez en mode noyau pour concourir pour le verrou.
Le verrouillage de rotation adaptatif a été introduit dans JDK 1.6, ce qui signifie que le temps de rotation n'est pas fixe, et il devient de plus en plus intelligent de savoir s'il faut tourner ou non .
自旋锁在JDK1.4.2中就已经引入,只不过默认是关闭的,可以使用-XX:+UseSpinning
参数来开启,在JDK1.6中就已经改为默认开启了。
如果JVM明显检测到某段代码是线程安全的(言外之意:无锁也是安全的),JVM会安全地原有的锁消除掉!
比如说:
public void vectorTest(){ Vector<String> vector = new Vector<String>(); for(int i = 0 ; i < 10 ; i++){ vector.add(i + ""); } System.out.println(vector); }
Vector是默认加锁的,但JVM如果发现vector变量仅仅在vectorTest()
方法中使用,那该vector是线程安全的。JVM会把vector内部加的锁去除,这个优化就叫做:锁消除。
默认情况下,总是推荐将同步块的作用范围限制得尽量小。
但是如果一系列的连续操作都对同一个对象反复加锁和解锁,甚至加锁操作是出现在循环体中的,频繁地进行互斥同步操作也会导致不必要的性能损耗。
JVM会将加锁的范围扩展(粗化),这就叫做锁粗化。
轻量级锁能提升程序同步性能的依据是“对于绝大部分的锁,在整个同步周期内都是不存在竞争的”,这是一个经验数据。
如果没有竞争,轻量级锁使用CAS操作避免了使用互斥量的开销
但如果存在锁竞争,除了互斥量的开销外,还额外发生了CAS操作,因此在有竞争的情况下,轻量级锁会比传统的重量级锁更慢。
简单来说:如果发现同步周期内都是不存在竞争,JVM会使用CAS操作来替代操作系统互斥量。这个优化就被叫做轻量级锁。
偏向锁就是在无竞争的情况下把整个同步都消除掉,连CAS操作都不做了!
偏向锁可以提高带有同步但无竞争的程序性能。它同样是一个带有效益权衡(Trade Off)性质的优化,也就是说,它并不一定总是对程序运行有利,如果程序中大多数的锁总是被多个不同的线程访问,那偏向模式就是多余的。在具体问题具体分析的前提下,有时候使用参数-XX:-UseBiasedLocking
来禁止偏向锁优化反而可以提升性能。
自适应偏向锁:自旋时间不固定
锁消除:如果发现代码是线程安全的,将锁去掉
锁粗化:加锁范围过小(重复加锁),将加锁的范围扩展
轻量级锁:在无竞争的情况下使用CAS操作去消除同步使用的互斥量
偏向锁:在无竞争环境下,把整个同步都消除,CAS也不做。
参考资料:
https://blog.csdn.net/chenssy/article/details/54883355
这是在看wangjingxin大佬面经的时候看到的面试题,之前对TCP粘包,拆包没什么概念,于是就简单去了解一下。
在进行Java NIO学习时,可能会发现:如果客户端连续不断的向服务端发送数据包时,服务端接收的数据会出现两个数据包粘在一起的情况。
TCP的首部格式:
TCP是基于字节流的,虽然应用层和TCP传输层之间的数据交互是大小不等的数据块,但是TCP把这些数据块仅仅看成一连串无结构的字节流,没有边界;
从TCP的帧结构也可以看出,在TCP的首部没有表示数据长度的字段
基于上面两点,在使用TCP传输数据时,才有粘包或者拆包现象发生的可能。
一个数据包中包含了发送端发送的两个数据包的信息,这种现象即为粘包
接收端收到了两个数据包,但是这两个数据包要么是不完整的,要么就是多出来一块,这种情况即发生了拆包和粘包
拆包和粘包的问题导致接收端在处理的时候会非常困难(因为无法区分一个完整的数据包)
Il existe généralement deux solutions générales au mécanisme de sous-emballage :
1, contrôle des caractères spéciaux
2, ajoutez la longueur du paquet de données dans l'en-tête
Si vous utilisez Netty, il existe des encodeurs et décodeurs spéciaux pour le résoudre. Problèmes avec déballage et collage des colis.
conseils :UDP n'a pas de problème de collage de paquets, mais il y a une perte et un désordre de paquets. Il n’y aura pas de colis incomplets et tous les colis reçus seront parfaitement corrects. Le protocole d'unité de données transmis est un message UDP ou un datagramme utilisateur, qui n'est ni fusionné ni divisé lors de l'envoi.
Revue NIO :
JDK10 est sorti, que savez-vous de nio ?
Voici comment il implémente le modèle de réutilisation des E/S sous Linux :
Appelle l'une des fonctions select/poll/epoll
et transmets plusieurs descripteurs de fichiers, renvoie si un le descripteur de fichier est prêt, sinon il se bloque jusqu'à l'expiration du délai.
Il existe quelques différences entre ces fonctions. Certains enquêteurs peuvent demander quelles sont les différences entre ces trois fonctions :
La différence est la suivante :
Résumé en deux phrases :
select和poll
doit interroger chaque descripteur de fichier, epoll
est piloté par les événements et ne nécessite pas d'interrogation
select和poll
Vous devez copier le descripteur de fichier à chaque fois, epoll
Pas besoin
select
Le nombre maximum de connexions est limité, epoll和poll
Max. Le nombre de connexions est illimité
Conseils : Implémentation d'epoll dans le noyau, utilisation d'un arbre rouge-noir pour gérer les blocs d'événements
Maintenant 3 ans Lors d'un stage en entreprise, le code que vous écrivez doit être à nouveau testé.
select/poll
Situation :
Les développeurs écrivent du code à ce moment-là, le testeur demandeà tous les développeurs un par un, avez-vous terminé. écrire le programme ? Envie de tester ?
epoll
Situation :
Le développeur a fini d'écrire le code, et dit au testeur : "J'ai écrit le code, vous allez le tester. La fonction est XXX". Le testeur a donc volontiers recherché les bugs.
Autres descriptions populaires [1] :
Un barman (un fil), avec un groupe d'ivrognes allongés devant lui, a soudainement crié "Versez le vin" (incident), vous venez au trot pour lui servir à boire, puis vous le laissez partir. Soudain, un autre veut lui servir à boire, et vous allez le servir à nouveau. Juste comme ça, un serveur sert plusieurs personnes. des boissons, et le serveur est inactif et peut travailler. Sinon, jouez avec votre téléphone portable. Quant à epoll et select, la différence entre poll et poll est que les gens ivres dans les deux dernières scènes ne parlent pas. Vous devez demander un à un si vous voulez un verre, et vous n'avez pas le temps de jouer avec votre téléphone portable. . Le multiplexage io signifie probablement que ces ivrognes partagent un serveur.
Autres descriptions populaires [2] :
Un exemple simple (peut-être pas très vivant) select/poll Le serveur de l'hôtel (noyau) dit au propriétaire de l'hôtel (programme utilisateur) : "Il y a des invités maintenant Check-out : "Mais aucun des serveurs n'a clairement indiqué au patron pour quelles tables les invités payaient. Le patron doit se rendre à chaque table et demander : Voulez-vous payer ? Le serveur de l'hôtel epoll (noyau) dit au propriétaire de l'hôtel (programme utilisateur) : "Les invités n°1, 2 et 5 partent." Le patron peut se rendre directement aux tables 1, 2 et 5 pour collecter de l'argent
Revue du billet de blog JVM :
Comment JVM est-elle passée du début à l'abandon ?
Quand j'écrivais JVM auparavant, j'ai un jour confondu la structure de mémoire JVM et le modèle de mémoire Java~~~ Heureusement, certains internautes enthousiastes m'ont fait le signaler .
Structure de la mémoire JVM :
Modèle de mémoire Java :
Règles lors de l'utilisation des variables :
Le modèle de mémoire Java stipule que toutes les variables sont stockées dans la mémoire principale
mémoire de travail du thread stocke la mémoire principale copie des variables utilisées par le thread
toutes les opérations du thread sur les variables (lire Récupération, affectation , etc.) doivent être effectués dans la mémoire de travail , et les variables de la mémoire principale ne peuvent pas être directement lues et écrites
synchronisées avec la mémoire principale à partir du mémoire de travail La mémoire est implémentée à travers les 8 opérations suivantes :
lock (lock) : Agit sur les variables de la mémoire principale, marquant une variable comme exclusive à un thread.
unlock (unlock) : Agit sur les variables de la mémoire principale pour libérer une variable qui est dans un état verrouillé. Seule la variable libérée peut être verrouillée par d'autres threads.
read (read) : Agit sur la variable de la mémoire principale, en transférant une valeur de variable de la mémoire principale à la mémoire de travail du thread, afin qu'elle puisse être utilisée par l'action de chargement ultérieure
Charger : Agit sur les variables dans la mémoire de travail. Il place la valeur de la variable obtenue de la mémoire principale par l'opération de lecture dans une copie de la variable dans la mémoire de travail.
use (use) : Agit sur une variable dans la mémoire de travail, en transmettant une valeur de variable dans la mémoire de travail au moteur d'exécution chaque fois que la machine virtuelle rencontre un mot qui doit être utilisé. la valeur de la variable. Cette opération sera effectuée lors de l'instruction du code de section.
assign (affectation) : Agit sur une variable dans la mémoire de travail. Il attribue une valeur reçue du moteur d'exécution à une variable dans la mémoire de travail. affectation Cette opération est effectuée lors de l'exécution d'instructions de bytecode.
store (stockage) : agit sur les variables de la mémoire de travail, transférant la valeur d'une variable de la mémoire de travail vers la mémoire principale pour les opérations d'écriture ultérieures.
write : Agit sur les variables de la mémoire principale. Il transfère l'opération de stockage de la valeur d'une variable de la mémoire de travail vers une variable de la mémoire principale.
Le modèle de mémoire Java est construit autour des trois caractéristiques de comment gérer l'atomicité, la visibilité et l'ordredans les processus simultanés
Opérations qui garantissent l'atomicité :
read、load、assign、use、store和write
serrure synchronisée
garantie Commandé (une nouvelle commande entraîne du désordre) opérations :
volatile
verrouillage synchronisé
Visibilité garantie :
volatile
verrouillage synchronisé
final
Comme mentionné ci-dessus, l'ordre peut être garanti grâce à des verrous volatils et synchronisés, mais lorsque nous écrivons habituellement des programmes nous ne prêtons pas toujours attention à l'ordre du code . En fait, nous avons un principe au sein de Java appelé Principe de ce qui se produit-avant(arrive-avant)
Le principe "arrive-avant" peut être transmis : plusieurs Les règles résolvent de manière globale tous les problèmes de conflits possibles entre deux opérations dans un environnement concurrent
Avec ces règles, Et nos opérations entrent dans le cadre défini par le présent règlement . Nous pouvons garantir que l'opération A aura certainement lieu avant l'opération B (il n'y aura pas de problème de réorganisation)
Le principe "arrive avant" est le suivant :
Règle d'ordre du programme : au sein d'un thread, selon l'ordre du code du programme, les opérations écrites au début se produisent avant les opérations écrites au dos. Pour être précis, il devrait s'agir de la séquence du flux de contrôle plutôt que de la séquence du code du programme, car des structures telles que des branches et des boucles doivent être prises en compte.
Règle de verrouillage du moniteur : une opération de déverrouillage se produit avant une opération de verrouillage ultérieure sur le même verrou. Ce qu'il faut souligner ici, c'est le même verrou, et « plus tard » fait référence à la séquence dans le temps.
Règle de variable volatile : une opération d'écriture sur une variable volatile se produit d'abord avant une opération de lecture ultérieure sur la variable. Le "plus tard" fait ici également référence au temps. Règle de démarrage du thread : la méthode start() de l'objet Thread précède chaque action de ce thread.
Règle de terminaison de thread : toutes les opérations dans un thread se produisent en premier lors de la détection de terminaison de ce thread. Nous pouvons y mettre fin via la méthode Thread.join(), Thread. isAlive() et d'autres moyens détectent que le thread a terminé son exécution.
Règle d'interruption de thread : L'appel à la méthode thread interrompu() se produit en premier avant que le code du thread interrompu ne détecte l'occurrence de l'événement d'interruption. Vous pouvez transmettre Thread.interrupted(). La méthode détecte si une interruption se produit.
Règle de finalisation d'objet (Finalizer Rule) : L'initialisation d'un objet (la fin de l'exécution du constructeur) se produit en premier au début de sa méthode finalize().
Transitivité : Si l'opération A se produit avant l'opération B, et que l'opération B se produit avant l'opération C, alors nous pouvons conclure que l'opération A se produit avant l'opération C.
Articles connexes :
Partage d'un résumé des questions d'entretien Java
10 principaux Java classiques questions d'entretien de méthode
Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!