1.基础
1.1数据类型
存储范围
Boolean 和 double
1.2 object 类
方法
常用的11中方法
方法 | 说明 |
public string tostring () | 返回该对象的字符串表示 |
hashcode() | native 方法用于返回对象的哈希码 |
equal方法 | 默认地址比较相当于==,重写比较内容 |
class getClass() | getClass()方法是获得调用该方法的对象的类;getClass().getName()可以得到该类的路径; (2)通过getClass()方法得到该对象类Class后,可以通过Class获取这个类中的相关属性和方法; |
object clone() | 深克隆:复制对象复制一份权限的,属性内容一致的对象 但是内存中的地址不一样 浅克隆:复制引用 |
void finalize() | gc垃圾回收机制 |
wait(long timeout) | 不能重写 释放了锁,timeout是等待wait |
wait(long timeout,int nanos) | |
wait() | 一直等待没有超时概念 |
notify() | 不能重写 唤醒一个等待的线程 |
notifyall() | 不能重写 唤醒所有等待的线程 |
hashcode
计算对象的散列值 返回int类型数据
对有散列表(hashmap,hashtable。。)的类有作用,比如hash set 插入数据时 可以直接通过hashcode去插入数据 不用一个个比较是否有重复
值相等 hashcode 一定相同 ,反之则未必
与equals的关系是 equals需要重写时 hashcode也许呀重写,否则会出现值相同但是hashcode 不相同的情况
equals 和==
==:引用数据类型 比较的是地址,基本数据类型比较的是值
equals没有重写等同于==,string 重写了equals 所以比较的是值
1.3 string stringBuilder stringbuffer
string 是不可变的 ,因为string类中使用了final关键字修饰不能被继承,避免子类破坏string不可变,并且保存的char数组也被private final修饰为私有切不可变
stringbuilder 是可变的 继承自abstractstringbuilder 非线程安全的没有对方法加同步锁
stringbuffer是线程安全的,可变的,abstractstringbuilder提供了一些append,expandcapacity,insert ,indexof等方法
string适合操作少量的数据
string buffer适合操作多线程的大量数据
string builder 适合读操作单线程大量数据
string的intern方法是一个native 方法 作用是加那个指定的字符串对象的引用保存在字串常量池中
如果字符串常量池中保存了对应的字符串对象的引用,就直接返回该引用,
如果字符串常量池中保存了对应的字符串对象的引用,那就在常量池中创建一个指向该字符串对象的引用并返回
2.jdk源码
2.1 hashmap
阅读源码,了解hashmap数据结构
存放键值对,基于hash表map的接口试下 非线程安全
可以存储null 的key 和value ,key只有一个null, 但是value 可以存多个null
jdk1.8之前由数组链表组成 数组是hashmap的主体,链表是为了解决hash冲突的,
1.8以后 在解决hash冲突是有了较大的变化 ,当链表长度大于等于阈值(默认为8),将链表转为红黑树,以减少搜索时间,在转红黑树之前会判断,如果当前的数组长度小于64,会选择对数组进行扩容
默认初始化大小为16,每次扩为2倍,hashmap总是使用2 的幂作为哈希表的大小
底层结构
1.8之前是数据加链表的组合,hashmap通过key的hashcode 经过扰动函数得到hash值 ,然后通过(n-1)&hash 判断当前元素的存放位置,如果当前位置存在的化,就判断是key和值相同,相同则覆盖 ,不相同则通过拉链法解决冲突
拉链法就是:链表数组组合,数组中每一格就是一个链表,遇到哈希冲突,将冲突的值加到链表中
1.8 的hash方法比1.7的简便
1.8 以后的在解决哈希冲突上有了较大的变化,当链表长度大于阈值8,首先会调用treeifbin方法,这个方法会根据hashmap数组来决定是否转为红黑树,只有当数组长度大于或者等与64的情况下,才会执行红黑树的操作,以减少搜素时间,否则就是执行resize方法来对数组扩容
扩容方式
hashmap底层有数组+链表(红黑树),数组大小可以在构造方法时设置,默认大小为16,,数组中每一个元素是一个链表,1.7采用头插法,1.8采用尾插法,插入的元素多了查找的效率就变低,所以满足一定条件链表就会转为红黑树,随着元素的增加,hashmap的数组会频繁扩容,如果构造时没有给加载因子设值,默认是0.75:
当添加某个元素后,数组的总添加元素树大于了数组长度×0.75,数组长度扩容为两倍,元素重新进入hashmap
如果没有红黑树,数组已经到64,链表也到8,再添加元素会转为红黑树
2.2 线程池
由于创建对象、连接、线程等操作时是比较“重”的,耗时的,高成本的。针对与这种场景,我们都会采用能复用就复用,而不是每次都要重新的去创建一遍
阅读源码
2.3Reentrantlook
源码
和synchornized的区别
2.4 concurrent 并发包下源码
3.多线程
3.1多线程实现方式
继承thread类
package com.yjm.javalearning.practiceschedule.thread.createthread;
/**
* @author77918
* @namepractice-schedule
* @date20232023/6/1723:56
*/
public class MyThread extends Thread {
public void run (){
for(int i=0;i<100;i++){
System.out.println(getName()+"hello");
}
}
}
package com.yjm.javalearning.practiceschedule.thread.createthread;
/**
* @author77918
* @namepractice-schedule
* @date20232023/6/180:16
*/
public class ThreadDemo1 {
public static void main(String[] args) {
MyThread thread1= new MyThread();
MyThread thread2= new MyThread();
thread1.setName("first");
thread2.setName("second");
thread1.start();
thread2.start();
}
}
实现runnable接口
package com.yjm.javalearning.practiceschedule.thread.createthread;
/**
* @author77918
* @namepractice-schedule
* @date20232023/6/182:08
*/
public class MyRun implements Runnable {
@Override
public void run() {
for (int i= 0;i<100;i++){
Thread t= Thread.currentThread();
System.out.println(t.getName()+"hello");
}
}
}
public class ThreadDemo1 {
public static void main(String[] args) {
/* MyThread thread1= new MyThread();
MyThread thread2= new MyThread();
*/
MyRun mr=new MyRun();
Thread thread1=new Thread(mr);
Thread thread2=new Thread(mr);
thread1.setName("first");
thread2.setName("second");
thread1.start();
thread2.start();
}
}
利用callable接口 和future接口实现方式
重写call方法
package com.yjm.javalearning.practiceschedule.thread.createthread;
import java.util.concurrent.Callable;
/**
* @author77918
* @namepractice-schedule
* @date20232023/6/182:37
*/
public class MyCallable implements Callable<Integer> {
@Override
public Integer call() throws Exception {
int sum=0;
for(int i=0;i<100;i++){
sum=sum+i;
}
return sum;
}
}
package com.yjm.javalearning.practiceschedule.thread.createthread;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;
/**
* @author77918
* @namepractice-schedule
* @date20232023/6/180:16
*/
public class ThreadDemo1 {
public static void main(String[] args) throws ExecutionException, InterruptedException {
/* MyThread thread1= new MyThread();
MyThread thread2= new MyThread();
*/
/* MyRun mr=new MyRun();
Thread thread1=new Thread(mr);
Thread thread2=new Thread(mr);*/
MyCallable mc= new MyCallable();
FutureTask<Integer> futureTask= new FutureTask<>(mc);
Thread thread1=new Thread(futureTask);
thread1.setName("first");
thread1.start();
Integer result = futureTask.get();
System.out.println(result);
}
}
小结
实现类的优点 编程简单可以可以直接使用thread 类中方法 但是不能在去继承其他类,扩展性差
接口不可以直接使用thread类中的方法 但是扩展性好
常见的成员方法
方法名称 | 说明 |
String getName() | 返回此线程的名称 |
void setName(String name) | 设置线程的名字 |
static Thread currentThread() | 获取当前线程的对象 |
static void sleep(long time) | 设置线程休眠指定时间 单位毫秒 |
setPriority(int newPriority) | 设置线程的优先级 |
final int getPriority() | 获取线程的优先级 |
final void setDaemon(boolean on) | 设置守护线程 |
public static void yield() | 出让线程 |
public static void join() | 插队线程 |
线程优先级1-10 默认是5
守护线程:当其他非守护线程执行结束 守护线程也会陆续结束
保证线程安全
同步代码块
锁对象一定是唯一的
synchronized 加在循环里面
package com.yjm.javalearning.practiceschedule.thread.createthread;
/**
* @author77918
* @namepractice-schedule
* @date20232023/6/1723:56
*/
public class MyThread extends Thread {
//这个类的所有对象都共享ticket数据
static int ticket =0;
//锁对象 必须是唯一的
static Object obj= new Object();
@Override
public void run (){
while (true){
synchronized (obj){
if(ticket<100){
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
} ticket++;
System.out.println(getName()+"第"+ticket);
}else {
break;
}
}
}
}
}
同步方法
同步方法是所著方法里的所有代码 且锁对象不能自己指定
就是把之前方法块里 的抽出来做方法
package com.yjm.javalearning.practiceschedule.thread.createthread;
/**
* @author77918
* @namepractice-schedule
* @date20232023/6/182:08
*/
public class MyRun implements Runnable {
// ticket 只创建一次 所以不需要加上static
int ticket =0;
@Override
public void run (){
while (true){
synchronized (MyThread.class) {
if (method()) break;
}
}
}
private boolean method(){
if (ticket < 100) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
ticket++;
System.out.println(Thread.currentThread().getName() + "第" + ticket);
}
return false;
}
}
Lock锁
lock锁实现提供比使用synchronized方法和语句可以获得更广泛的锁定操作
提供了获得锁和释放锁的方法:
获得锁 void lock(),释放锁 void unlock()
lock是不能直接实例化,采用lock的实现类 ReentrantLock来实例化
Reentrant Lock():构造方法 创建一个reentranlock实例
要保证锁一定会被释放
package com.yjm.javalearning.practiceschedule.thread.createthread;
import ch.qos.logback.core.joran.conditional.ElseAction;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
/**
* @author77918
* @namepractice-schedule
* @date20232023/6/182:08
*/
public class MyRun implements Runnable {
// ticket 只创建一次 所以不需要加上static
int ticket =0;
static Lock lock=new ReentrantLock();
@Override
public void run (){
while (true){
lock.lock();
try {
if (ticket == 100) {
break;
} else {
Thread.sleep(100);
ticket++;
System.out.println(Thread.currentThread().getName() + "第" + ticket);
}
} catch (InterruptedException e) {
e.printStackTrace();
}finally{
lock.unlock();
}
}
}
}
生产者和消费者问题
多线程同步的经典问题 ,生产者生产一定的数据放到缓冲池区,消费者也在缓冲区消耗这些数据 ,生产者和消费者之间必须保证同步 ,保证缓冲区满了的时候 生产者不会在缓冲区放入数据,缓冲区空的时候消费者不会去消费数据
notify()/wait()
常见方法 :void waite(),void notify(),void notifyall()
思路:当缓冲区满了,生产者线程停止执行,放弃锁,生产者处于等待,当缓冲区已空,消费者停止执行,放弃锁,进入等待状态
仓库:
package com.yjm.javalearning.practiceschedule.thread.createthread.ProducerConsumer;
import java.util.LinkedList;
/**
* @author77918
* @namepractice-schedule
* @date20232023/6/1813:59
*/
public class Storage {
private final int MAX_SIZE=10;
private LinkedList<Object> list =new LinkedList<>();
public void produce(){
synchronized (list){
while (list.size()+1>MAX_SIZE){
System.out.println("【producer"+Thread.currentThread().getName()+"】仓库已满");
try {
list.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
list.add(new Object());
System.out.println("【producer"+Thread.currentThread().getName()+"】生产一个产品,当下库存为::"+list.size());
list.notifyAll();
}
}
public void consume(){
synchronized (list){
while (list.size()==0){
System.out.println("【consume"+Thread.currentThread().getName()+"】库存为空");
try {
list.wait(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
list.remove();
System.out.println("【consume"+Thread.currentThread().getName()+"】消费一个产品,现在库存“:"+list.size());
list.notifyAll();
}
}
}
生产者
package com.yjm.javalearning.practiceschedule.thread.createthread.ProducerConsumer;
/**
* @author77918
* @namepractice-schedule
* @date20232023/6/1813:58
*/
public class Producer implements Runnable {
private Storage storage;
public Producer() {
}
public Producer(Storage storage) {
this.storage=storage;
}
@Override
public void run() {
while (true){
try {
Thread.sleep(1000);
storage.produce();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
消费者
package com.yjm.javalearning.practiceschedule.thread.createthread.ProducerConsumer;
/**
* @author77918
* @namepractice-schedule
* @date20232023/6/1814:02
*/
public class Consumer implements Runnable {
private Storage storage;
public Consumer() {
}
public Consumer(Storage storage) {
this.storage=storage;
}
@Override
public void run() {
while (true){
try {
Thread.sleep(3000);
storage.consume();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
测试类
package com.YJM.love.com.YJM.test;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Timer;
import java.util.TimerTask;
/**
* @author77918
* @namelove_take_out
* @date20232023/6/151:22
*/
public class Task1 {
public static void main(String[] args) {
TimerTask timerTask = new TimerTask() {
@Override
public void run() {
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("HH:MM:ss.SSS");
String datestr= simpleDateFormat.format(new Date());
System.out.println("定时任务"+datestr);
}
};
//计时器
Timer timer = new Timer();
//添加执行任务 延迟1s执行,每3s执行一次
timer.schedule(timerTask,1000,3000);
}
}
await()/signal()
在jdk5中 用reentrantlock 和condition可以实现等待/通知模型 ,更灵活,通过在lock对象上调用new condition方法,将变量和锁对象进行帮电工,进而控制并发程序访问竞争资源的安全
package com.yjm.javalearning.practiceschedule.thread.createthread.ProducerConsumer;
import java.util.LinkedList;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
/**
* @author77918
* @namepractice-schedule
* @date20232023/6/1813:59
*/
public class Storage {
private final int MAX_SIZE=10;
private LinkedList<Object> list =new LinkedList<>();
//锁
private final Lock lock=new ReentrantLock();
//仓库满的条件变量
private final Condition full =lock.newCondition();
//仓库空的条件变量
private final Condition empty=lock.newCondition();
public void produce(){
lock.lock();
while (list.size()+1>MAX_SIZE){
System.out.println("【producer"+Thread.currentThread().getName()+"】仓库已满");
try {
full.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
list.add(new Object());
System.out.println("【producer"+Thread.currentThread().getName()+"】生产一个产品,当下库存为::"+list.size());
empty.signalAll();
lock.unlock();
}
public void consume(){
lock.lock();
while (list.size()==0){
System.out.println("【consume"+Thread.currentThread().getName()+"】库存为空");
try {
empty.await(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
list.remove();
System.out.println("【consume"+Thread.currentThread().getName()+"】消费一个产品,现在库存“:"+list.size());
full.signalAll();
lock.unlock();
}
}
blockingqueue阻塞队列
在内部实现同步的队列
实现方式采用 await() signal()
可以在生成对象时指定容量大小 用于阻塞操作的是put 和take方法
put():生产者容量达到最大时 阻塞
take():消费者容量到达最大时阻塞
package com.yjm.javalearning.practiceschedule.thread.createthread.ProducerConsumer;
import java.util.LinkedList;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
/**
* @author77918
* @namepractice-schedule
* @date20232023/6/1813:59
*/
public class Storage {
private LinkedBlockingQueue<Object> list =new LinkedBlockingQueue<>(10);
public void produce(){
try {
list.put(new Object());
System.out.println("【producer"+Thread.currentThread().getName()+"】生产一个产品,当下库存为::"+list.size());
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public void consume(){
try {
list.take();
System.out.println("【consume"+Thread.currentThread().getName()+"】消费一个产品,现在库存“:"+list.size());
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
信号量
semaphore是一种基于计数的信号量,可以设定一个阈值,基于此 多个线程竞争获取许可信号,实现后归还信号,超过阈值 线程申请许可信号将会被阻塞 ,可以创建计数为1的,将其作为一种类似互斥锁的机制, 也叫二元信号量,表示两种互斥状态,计数为0 表示基于release,然后就可以acquire
package com.yjm.javalearning.practiceschedule.thread.createthread.ProducerConsumer;
import java.util.LinkedList;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.Semaphore;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
/**
* @author77918
* @namepractice-schedule
* @date20232023/6/1813:59
*/
public class Storage {
private LinkedList<Object> list =new LinkedList<>();
//仓库最大容量
final Semaphore notfull= new Semaphore(10);
//将线程挂起,等待其他线程来触发
final Semaphore notempty=new Semaphore(0);
//互斥锁
final Semaphore mutex=new Semaphore(1);
public void produce(){
try {
notfull.acquire();
mutex.acquire();
list.add(new Object());
System.out.println("【producer"+Thread.currentThread().getName()+"】生产一个产品,当下库存为::"+list.size());
} catch (InterruptedException e) {
e.printStackTrace();
}finally {
mutex.release();
notempty.release();
}
}
public void consume(){
try {
notempty.acquire();
mutex.acquire();
list.remove();
System.out.println("【consume"+Thread.currentThread().getName()+"】消费一个产品,现在库存“:"+list.size());
} catch (InterruptedException e) {
e.printStackTrace();
}finally {
mutex.release();
notfull.release();
}
}
}