1. javaj基础
Java字符串常量池
public class StringPools58Demo {
public static void main(String[] args) {
/*
(1).str1
str1 会有4个对象
一个StringBuilder、
一个58 ldc、
一个tongcheng ldc、
String
这个时候常量池中没有58tongcheng这个ldc在
str1.intern():在jdk7后,会看常量池中是否存在,如果没有,它不会创建一个对象,
如果堆中已经这个字符串,那么会将堆中的引用地址赋给它
所以这个时候str1.intern()是获取的堆中的
* */
String str1=new StringBuilder("58").append("tongcheng").toString();
System.out.println(str1);
System.out.println(str1.intern());
System.out.println(str1==str1.intern());//true
System.out.println();
/*
sum.misc.Version类会在JDK类库的初始化中被加载并初始化,而在初始化时它需要对静态常量字
段根据指定的常量值(ConstantValue)做默认初始化,此时sum.misc.Version.launcher静态常
量字段所引用的"java"字符串字面量就被intern到HotSpot VM的字符串常量池 - StringTable
里了
str2对象是堆中的
str.intern()是返回的是JDK出娘胎自带的,在加载sum.misc.version这个类的时候进入常量池
*/
String str2=new StringBuilder("ja").append("va").toString();
System.out.println(str2);
System.out.println(str2.intern());
System.out.println(str2==str2.intern());//false ,只有jdk自带的字符串如"java"字符串是false,其他字符串都是true
}
}
利扣算法题:两数之和
/*
题目:
给定一个整数数组 nums 和一个目标值 target,请你在该数组中找出和为目标值的那两个整数,
并返回他们的数组下标你可以假设每种输入只会对应一个答案。但是,数组中同一个元素不能使
用两遍。
* */
public class TwoSumDemo {
public static void main(String[] args) {
int[]nums ={2, 7, 11, 15};
int target = 99;
//int[]indexCollection=twoSum1(nums,target);
int[]indexCollection=twoSum2(nums,target);
if(indexCollection!=null){
for (int index : indexCollection) {
System.out.print(index+" ");
}
}
}
//1.暴力法:
//通过双重遍历数组中所有元素的两两组合,当出现符合的和时返回两个元素的下标
public static int[] twoSum1(int[] nums, int target) {
for (int i = 0; i < nums.length; i++) {
for (int j = i+1; j <nums.length ; j++) {
if(target-nums[i]==nums[j]){
return new int[]{i,j};
}
}
}
return null;
}
//2.哈希(更优解法)
public static int[] twoSum2(int[] nums, int target){
Map<Integer,Integer> map=new HashMap<>();
for (int i = 0; i < nums.length; i++) {
int param=target-nums[i];//2 7
if(map.containsKey(param)){
return new int[]{map.get(param),i};
}
map.put(nums[i],i);
//map k存数组值 v存下标
2 0
}
return null;
}
}
java并发编程(juc)
可重入锁(递归锁)
隐式(自动挡)是指:jvm自动帮我们加锁释放锁
显示(收动挡)是指:需要自己显示的lock和unLock
sychronized
隐式锁:(即synchronized关键字使用的锁)默认是可重入锁(同步块、同步方法)
原理如下:
- 每个锁对象拥有一个锁计数器和一个指向持有该锁的线程的指针
- 当执行monitorenter时,如果目标锁对象的计数器为零,那么说明它没有被其他线程持有,Java虚拟机会将该锁对象的持有线程设置为当前线程,并且将其计数器加1,否则需要等待,直至持有线程释放该锁
- 当执行monitorexit时,Java虚拟机则锁对象的计数器减1。计数器为零代表锁已经被释放
反编译底层原理
//1.同步代码块
//1.同步代码块
public class SychronizedDemo {
Object object=new Object();
public void sychronizedMethod(){
new Thread(()->{
synchronized (object){
System.out.println(Thread.currentThread().getName()+"\t"+"外层....");
synchronized (object){
System.out.println(Thread.currentThread().getName()+"\t"+"中层....");
synchronized (object){
System.out.println(Thread.currentThread().getName()+"\t"+"内层....");
}
}
}
},"A").start();
}
public static void main(String[] args) {
new SychronizedDemo().sychronizedMethod();
/*
输出结果:
A 外层....
A 中层....
A 内层....
* */
}
}
//2.同步方法
public class ReEnterLockDemo {
public synchronized void m1() {
System.out.println("=====外");
m2();
}
public synchronized void m2() {
System.out.println("=====中");
m3();
}
public synchronized void m3() {
System.out.println("=====内");
}
public static void main(String[] args) {
new ReEnterLockDemo().m1();
}
}
输出结果
=====外
=====中
=====内
ReentrantLock
显示锁:(即lock)也有ReentrantLock这样的可重入锁
(注意:有多少个lock,就有多少个unlock,他们是配对使用的;如果多一个或者少一个会使得其他线程处于等待状态)
class Phone2{
static ReentrantLock reentrantLock=new ReentrantLock();
public static void sendSms(){
reentrantLock.lock();
/*
//reentrantLock.lock();
注意有多少个lock,就有多少个unlock,他们是配对使用的
如果多了一个lock(),那么会出现线程B一直处于等待状态
* */
reentrantLock.lock();
try {
System.out.println(Thread.currentThread().getName()+"\t"+"sendSms");
sendEmails();
}catch (Exception e){
e.printStackTrace();
}finally {
reentrantLock.unlock();
}
}
private static void sendEmails() {
reentrantLock.lock();
try {
System.out.println(Thread.currentThread().getName()+"\t"+"sendEmails...");
}catch (Exception e){
e.printStackTrace();
}finally {
reentrantLock.unlock();
}
}
}
public class ReentrantLockDemo {
public static void main(String[] args) {
Phone2 phone2=new Phone2();
new Thread(()->{phone2.sendSms();},"A").start();
new Thread(()->{phone2.sendSms();},"B").start();
}
}
LockSupport
LockSupport就是对线程唤醒机制(wait/notify)的升级加强版
3种让线程等待唤醒的方法:
synchronized -->wait( )和notify( )
Object类中wait( )和notify( )实现线程的等待唤醒
wait( ):阻塞线程
notify( ):唤醒线程
1. wait和notify方法必须要在synchronized 同步块或同步方法里且成对出现使用。
2. 必须先wait后再notify(如果先notify后wait会出现另一个线程一直处于等待状态)
正常使用
- wait和notify方法必须要在synchronized 同步块或同步方法里且成对出现使用。 wait和notify方法两个都去掉同步代码块后看运行效果出现异常情况:
Exception in thread “A” Exception in thread “B”
java.lang.IllegalMonitorStateException
wait和notify方法不能脱离synchronized 同步块或同步方法使用
不是synchronized 同步块或同步方法使用
- 先notify后wai
先wait后notify才可以(如果先notify后wait会出现另一个线程一直处于等待状态)
ReentrantLock–>await()和signal()
Condition接口中的await和signal方法实现线程等待和唤醒
(出现的问题和object中wait和notify一样)
1. await()和signal()方法必须要在lock和unLock同步块或同步方法里且成对出现使用。
2. 必须先await后再signal(如果先signal后await会出现另一个线程一直处于等待状态)
public class LockDemo {
static Object object=new Object();
public static void main(String[] args) {
Lock lock=new ReentrantLock();
Condition condition = lock.newCondition();
new Thread(()->{
//如果把下行这句代码打开,先signal后await,会出现A线程一直处于等待状态
//try { TimeUnit.SECONDS.sleep(3); } catch (InterruptedException e) {e.printStackTrace();}
lock.lock();
try {
System.out.println(Thread.currentThread().getName()+"\t"+"coming....");
condition.await();
}catch (Exception e){
e.printStackTrace();
}finally {
lock.unlock();
}
System.out.println(Thread.currentThread().getName()+"\t"+"END....");
},"A").start();
new Thread(()->{
lock.lock();
try {
System.out.println(Thread.currentThread().getName()+"\t"+"唤醒A线程****");
condition.signal();
}catch (Exception e){
e.printStackTrace();
}finally {
lock.unlock();
}
},"B").start();
}
}
LockSupport–>park()和unpark()
- 不用必须写在锁代码块中
- 先unpark(),唤醒再park()阻塞,阻塞无效
/*
(1).阻塞
(permit默认是O,所以一开始调用park()方法,当前线程就会阻塞,直到别的线程将当前线程的permit设置为1时,
park方法会被唤醒,然后会将permit再次设置为O并返回)
static void park()
static void park(Object blocker)
(2).唤醒
static void unpark(Thread thread)
(调用unpark(thread)方法后,就会将thread线程的许可permit设置成1(注意多次调用unpark方法,不会累加,
permit值还是1)会自动唤醒thread线程,即之前阻塞中的LockSupport.park()方法会立即返回)
static void unpark(Thread thread)
* */
public class LockSupportDemo {
public static void main(String[] args) {
Thread t1=new Thread(()->{
System.out.println(Thread.currentThread().getName()+"\t"+"coming....");
LockSupport.park();
/*
如果这里有两个LockSupport.park(),因为permit的值为1,上一行已经使用了permit
所以下一行被注释的打开会导致程序处于一直等待的状态
* */
//LockSupport.park();
System.out.println(Thread.currentThread().getName()+"\t"+"被B唤醒了");
},"A");
t1.start();
//下面代码一行是为了A线程先执行
try { TimeUnit.SECONDS.sleep(3); } catch (InterruptedException e) {e.printStackTrace();}
Thread t2=new Thread(()->{
System.out.println(Thread.currentThread().getName()+"\t"+"唤醒A线程");
//有两个LockSupport.unpark(t1),由于permit的值最大为1,所以只能给park一个通行证
LockSupport.unpark(t1);
//LockSupport.unpark(t1);
},"B");
t2.start();
}
}
面试题
AQS
**AQS --> AbstractQueuedSynchronizer (抽象队列同步器): **
用来管理线程的:一个int状态变量+一个队列
是一个抽象的类
和AQS有关的锁
锁和AQS同步器的关系
AQS管理多线程
AQS源码解析
AQS内部架构
AQS内部架构图:
ReentrantLock和AQS的关系
ReentrantLock的内部结构
公平锁和非公平锁
多个线程抢锁AQS的执行流程
第一个哨兵节点的作用
A释放锁 --> B获取锁