java基础(反射,注解,多线程,juc)
Posted rd-yyx
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了java基础(反射,注解,多线程,juc)相关的知识,希望对你有一定的参考价值。
JAVA基础
java反射
class对象
三种方式获取class加载时对象
1.class.forName("全类名"):将字节码文件加载进内存,返回class对象
2.类名.class:通过类名的属性class获取
3.对象.getClass():getClass()方法是object中的静态方法
同一个字节码文件(*.class)在运行过程中只会被加载一次
class对象功能
1.获取成员变量
2.获取构造方法
3.获取成员方法
4.获取类名
Field成员变量
一般的get方法获取public修饰符下的,getDeclared无论是什么修饰符都能获取但要获取或改变值需要设置忽略权限修饰符的安全检查setAccessible称为暴力反射
获取值和改变值都需要给一个相应的对象
访问修饰符作用范围 | 所在类 | 同一包内其他类 | 其他包内子类 | 其他包内非子类 |
---|---|---|---|---|
private | 可以访问 | 不可以 | 不可以 | 不可以 |
缺省 | 可以 | 可以 | 不可以 | 不可以 |
protected | 可以 | 可以 | 可以 | 不可以 |
public | 可以 | 可以 | 可以 | 可以 |
Constructor构造方法
一样拥有get,可以根据构造器中的参数类型获取
newInstance可以创建对象,如果是空参构造可以直接使用class对象newInstance方法
Method成员方法
一样拥有get,可以根据方法名,参数获取,获取的时候会获取所有的方法包括父类
invoke方法是执行相应方法,需传一个对象如果有参数需要传相应参数
getName获取方法名
propertis配置文件
propertis对象.load可以加载一个配置文件并获取配置文件中的值
可以通过类名.class.getClassLoader可以获取类加载器,然后通过类加载器的方法获取资源路径下的资源路径或相应的字节流
JAVA注解
JDK中自定义的注解
@Override:检测该注解标注的方法是否是继承自父类或父接口的
@Deprecated:将该注解标注的内容,表示已过时
@SupperessWarnings:压制警告
自定义注解
格式:
? 元注解
? public @interface 注解名称{}
本质:注解本质上就是接口
? public interface 注解名称 extends java.lang.annotation.Annotation{}
属性:接口中的抽象方法
? 要求:
? 属性的返回值类型
? 基本数据类型、String、枚举、注解,及上面的数组类型
? 定义了属性就要给属性赋值,也可以使用default给默认值,如果只有一个value要赋值就可以省略
元注解:用于描述注解的注解
? @Target:描述注解能够作用的位置
? ElementType取值:
? TYPE:可以作用于类上
? METHOD:可以作用于方法上
? FIELD:可以作用在成员变量上
? @Retention:描述注解被保留的阶段
? RetentionPolicy.SOURCE/CLASS/RUNTIME表示java 的三个阶段,源代码,字节码,运行时被jvm读到
? @Documented:描述注解是否被抽取到api文档中
? @Inherited:描述注解是否被子类继承
解析注解
获取标有注解的类的字节码文件对象Class<类> 名称 = 类.class
获取该类上面标注的注解。 getAnnotation(注解类)
调用注解对象中定义的抽象方法,获取返回值
JAVA多线程
线程的生命周期
1、新生态:一个线程被实例化完成,但是没有做人任何事
2、就绪态:一个线程已经开启,已经开始争抢cpu时间片
3、运行态:一个线程已经抢到了时间片,开始执行这个线程中的逻辑
4、阻塞态:一个线程在运行过程中,受到某些操作的影响,放弃了已经获取的cpu时间片,并且不再参与cpu时间片的争抢
5、死亡态:一个线程对象被销毁
线程开辟方式
线程实例化
1、继承Thread类,做一个线程子类,重写run方法。使用start方法开启一个新线程,执行run方法
2、传一个Runable接口的实现类参数给Thread,然后执行start
3、继承callable接口有返回值,效率比runable高
线程常用方法
线程命名
实例化后使用setName方法或者在实例化的同时使用构造方法命名
线程休眠
Thread.sleep参数以毫秒为单位
线程优先级
默认优先级是五,setPriority方法[0,10]的整数
线程礼让
Thread.yield方法进行礼让
线程插队
Thread.join
临街资源问题解决
同步代码段
synchronized(对象锁或者是类锁但所有访问这个临界资源的锁需要一样){需要加锁的代码段}
同步方法
public synchronized static void name(){执行体} 静态方法同步锁是类锁,非静态方法同步锁是this
锁
ReenTrantLock对象,lock方法上锁,unlock方法解锁
死锁
多个线程之间持有对方的锁但需要另外的锁标记不释放自己的锁而导致相互卡锁产生死锁
wait:等待,object类中的方法,让当前线程释放自己的锁标记让出cpu资源,使当前线程进入等待队列
notify:通知,object类中的方法,唤醒等待队列中的一个线程,使这个线程进入锁池
notifyAll:通知,object类中的方法,唤醒等待队列中等待相应锁的所有的线程,并使这些线程进入锁池
懒汉式单例模式的多线程解决
class Single(){
private static Single Instance = null;
public static synchronized Single getSingle(){
if(Instance == null){
Instance = new Single();
}
return Instance
}
}
生产者消费者问题
package Thread;
import java.util.LinkedList;
import java.util.List;
public class ProductPool {
private List<Product> productList = null;
private int MaxSize = 0;
ProductPool(int maxSize){
productList = new LinkedList<Product>();
this.MaxSize = maxSize;
}
public synchronized Product push(Product product){
if(productList.size() == MaxSize){
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
productList.add(product);
this.notifyAll();
return product;
}
public synchronized Product pop(){
if(productList.size() == 0){
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
Product product = productList.remove(0);
this.notifyAll();
return product;
}
}
class Product {
private String name;
Product(String name){
this.name = name;
}
public String getName(){
return name;
}
}
class Productor extends Thread {
private ProductPool productPool;
Productor(ProductPool productPool){
this.productPool = productPool;
}
@Override
public void run(){
while(true){
String name = (int)(Math.random() * 100) + "号产品";
System.out.println("生产了"+name);
Product product= new Product(name);
productPool.push(product);
}
}
}
class Consumer extends Thread{
private ProductPool productPool;
Consumer(ProductPool productPool){
this.productPool = productPool;
}
@Override
public void run(){
while(true){
Product pop = productPool.pop();
System.out.println("消费了"+pop.getName());
}
}
}
class MainProduct{
public static void main(String[] args) {
ProductPool productPool = new ProductPool(10);
Productor productor = new Productor(productPool);
Consumer consumer = new Consumer(productPool);
productor.start();
consumer.start();
}
}
Lamda表达式
函数式接口:任何接口只包含了唯一一个抽象方法,那么它就是函数式接口
(参数)->{方法}
JUC
LOCK锁
public ReentrantLock(boolean fair) {
sync = fair ? new FairSync() : new NonfairSync();
}
公平锁:先来后到。非公平锁:可以插队(默认)
Synchronized 内置java关键字,Lock是一个java类
Synchronized 无法判断获取锁的状态,Lock可以判断是否获取到了锁
Synchronized 会自动释放锁,lock必须手动,如果不释放会产生死锁
Synchronized 会死死的等待,Lock不会一直等待下去
Synchronized 可重入锁,不可以中断的 非公平,Lock,,可重入锁,可以判断锁,是否公平可以设置
Lock替换Synchronized生产者消费者模式
Lock lock = new ReentrantLock();
Condition condition = lock.newCondition();
condition.await;//等待
condition.singnal();//唤醒当前
conditon.signalAll();//唤醒
lock.lock();//锁
lock.unlock();//开锁
八锁问题
1.使用Synchronized作为锁的话,锁的对象是方法的调用者,两个方法是用的同一个的锁的时候会,谁先拿到谁执行
2.普通方法不受锁的影响,看速度不看同步锁方法的先来后到
3.不同对象调用锁的同步方法,如果不是静态的就是不同的锁,静态的是同一个类锁
4.用的静态同步方法,锁的是静态同方法的类,全局唯一
主要看锁是同一个还是不同,就能确定线程是否存在顺序关系!
集合类不安全
List不安全
普通的List集合在线程中会产生java.util.ConcurrentModificationException并发修改异常
如果用new Vector()内部的方法自动加了Sychronized不会产生异常
Collections.sychronized(new 集合);集合的工具类方法转成同步的
JUC中可以new CopyOnWriteArrayList解决 写入时复制 前面的同步代码效率比较低而这个是防止覆盖,先复制然后写入 使用lock锁来控制
Set不安全
同样会产生并法修改异常 工具类可以解决
JUC中可以 new CopyOnWriteArrarySet
Map不安全
JUC中使用 new ConcurrentHashmap
Callable
callable类似于Runable接口,但是Runable不返回结果,也不能 抛出检查的异常
public class CallableTest {
public static void main(String[] args) throws ExecutionException, InterruptedException {
MyThread myThread = new MyThread();
FutureTask futureTask = new FutureTask<String>(myThread);//适配类
new Thread(futureTask,"线程一").start();
System.out.println(futureTask.get());
}
}
class MyThread implements Callable<String>{
@Override
public String call() {
System.out.println(Thread.currentThread().getName()+"正在运行");
return "hello world";
}
}
因为Thread的构造方法只接收Runable,为了能在Thread里使用Callable需要用FutureTask将Callble进行适配
常用辅助类
CountDownLatch
一种减法计数器。在创建对象的传一个数量, countDownLatch.countDown计数减1,countDownLatch.await()等待计数器归零唤醒,然后再向下执行
CyclicBarrier
类似于加法计数器,创建cyclicbarrier对象时需要传一个数量参数和在集齐数量的同时执行的线程Runable接口
cyclibarrier.await()等待集齐后的操作,也相当于计数器加一
Semaphore
信号量。相当于操作系统里面进行同步操作的时候,因为能提供的资源不够多制造一种信号量。
创建对象时给资源数量。
Semaphore.acquire()获得,假设如果满了就等待
Semaphore.release();释放,会将当前信号量加一,然后唤醒等待的线程
读写锁
ReadWriteLock可以多个线程进行读,但是只能有一个线程对其进行写操作
readWriteLock.writeLock().lock();增加写锁。unlock解锁。
readWriteLock.readLock().lock(); 增加读锁。unlock解锁
共享锁(读锁)独占锁(写锁)
阻塞队列
BlockingQueue阻塞队列
所有已知实现类:
ArrayBlockingQueue , DelayQueue , LinkedBlockingDeque , LinkedBlockingQueue , LinkedTransferQueue , PriorityBlockingQueue , SynchronousQueue
四组api
方式 | 抛出异常 | 有返回值 | 阻塞等待 | 超时等待 |
---|---|---|---|---|
添加 | add | offer | put | offer(带参数) |
移除 | remove | poll | take | poll(带参数) |
判断队列首 | element | peek |
SynchronouseQueue同步队列,没有容量
进去一个元素,必须等待取出来之后才能再往里面放一个元素
线程池
Executors
3大方法
Executors.newFixedThreadPool();创建一个固定的线程池大小
Executors.newSingleThreadExecutor();单个线程
Executors.newCachedThreadPool();可伸展的
7大参数
源码分析
public static ExecutorService newSingleThreadExecutor() {
return new FinalizableDelegatedExecutorService
(new ThreadPoolExecutor(1, 1,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>()));
}
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
}
public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>());
}
//本质:ThreadPoolExecutor
public ThreadPoolExecutor(int corePoolSize,//核心线程池大小
int maximumPoolSize,//最大核心线程池大小,当阻塞队列满的时候触发
long keepAliveTime,//超时没有人调用就会释放
TimeUnit unit,//超时单位
BlockingQueue<Runnable> workQueue,//阻塞队列
ThreadFactory threadFactory,//线程工厂,创建线程
RejectedExecutionHandler handler//拒绝策略) {
if (corePoolSize < 0 ||
maximumPoolSize <= 0 ||
maximumPoolSize < corePoolSize ||
keepAliveTime < 0)
throw new IllegalArgumentException();
if (workQueue == null || threadFactory == null || handler == null)
throw new NullPointerException();
this.acc = System.getSecurityManager() == null ?
null :
AccessController.getContext();
this.corePoolSize = corePoolSize;
this.maximumPoolSize = maximumPoolSize;
this.workQueue = workQueue;
this.keepAliveTime = unit.toNanos(keepAliveTime);
this.threadFactory = threadFactory;
this.handler = handler;
}
四种拒绝策略
通过源码可以找到默认的拒绝策略:
private static final RejectedExecutionHandler defaultHandler =
new AbortPolicy();//如果都满了就不处理新进来的并抛出异常
第二种策略:
CallerRunsPolicy//哪来的去哪里
第三种策略:
DiscardPolicy//队列满了不会抛出异常
第四种策略:
DiscardOldestPolicy//队列满了,尝试和最早的竞争
最大线程到底该如何定义
1、cpu密集型,几核cpu就定义为几可以保持cpu最高
Runtime.getRuntime().availableProcessors()//获取cpu的核数
2、io密集型, 判断程序中十分耗io的线程,设置大于这个的数量
四大函数接口
lambda表达式,链式编程,函数式接口,Stream流式计算
函数式接口:只有一个方法的接口
比如:
@FunctionalInterface
public interface Runnable {
public abstract void run();
}
//foreach(参数也是函数式接口 )
函数式接口:
Function function = (str)->{return str;};//使用lambda表达式可以简化
断定型接口:
Predicate<String> predicate = (str)->{return str.isEmpty();};//返回值为布尔值
消费型接口:
Consumer<String> consumer = (str) -> { };//只有参数没有返回
供给型接口:
Supplier supplier = () -> { return "123";};//只有返回值没有参数
ForkJoin
特点:map reduce 、 工作窃取
public class Fork extends RecursiveTask<Long> {
Long start;
Long end;
Long temp = 10000L;
Fork(Long start , Long end){
this.start = start;
this.end = end;
}
@Override
protected Long compute() {
Long sum = 0l;
if( (end - start) >temp ){
for(Long i = start ; i < end ; i++){
sum = sum + i ;
}
return sum;
}else{
Long middle = (end + start) / 2 ;
Fork fork = new Fork(start , middle);
fork.fork();//拆分任务把任务压入线程
Fork fork2 = new Fork(middle + 1 , end);
fork2.fork();
return fork.join() + fork2.join();
}
}
}
class Test{
static Long start = 1l;
static Long end = 10_0000_0000l;
public static void test1(){
Long sum = 0l;
Long startTime = System.currentTimeMillis();
for (Long i = start ; i < end ; i++){
sum = sum + i;
}
Long endTime = System.currentTimeMillis() - startTime;
System.out.println("sum=" + sum + " time=" + endTime);
};
public static void test2() throws ExecutionException, InterruptedException {
Long startTime = System.currentTimeMillis();
ForkJoinPool forkJoinPool = new ForkJoinPool();
ForkJoinTask task = new Fork(start , end);
ForkJoinTask submit = forkJoinPool.submit(task);
Long sum = (Long) submit.get();
Long endTime = System.currentTimeMillis() - startTime;
System.out.println("sum=" + sum + " time=" + endTime);
}
public static void test3(){
Long startTime = System.currentTimeMillis();
Long sum = LongStream.rangeClosed(start, end).parallel().reduce(0, Long::sum);
Long endTime = System.currentTimeMillis() - startTime;
System.out.println("sum=" + sum + " time=" + endTime);
}
public static void main(String[] args) throws ExecutionException, InterruptedException {
test1();
test2();
test3();
}
}
//sum=499999999500000000 time=6160
sum=499999999500000000 time=4980
sum=500000000500000000 time=179
在大数据下的运算速度比较
CompletableFuture//可以实现异步回调runAsync无返回值,supplyAsync有返回值
JMM
JMM是java内存模型,不存在的东西,是一种概念,约定
关于JMM的同步约定:
1、线程解锁前,必须把共享变量立刻刷新回主存
2、线程甲锁前,必须读取主存中的最新值到工作内存中
3、加锁和解锁是同一把锁
内存交互操作有8种,虚拟机实现必须保证每一个操作都是原子的,不可在分的(对于double和long类型的变量来说,load、store、read和write操作在某些平台上允许例外)
-
- lock (锁定):作用于主内存的变量,把一个变量标识为线程独占状态
- unlock (解锁):作用于主内存的变量,它把一个处于锁定状态的变量释放出来,释放后的变量才可以被其他线程锁定
- read (读取):作用于主内存变量,它把一个变量的值从主内存传输到线程的工作内存中,以便随后的load动作使用
- load (载入):作用于工作内存的变量,它把read操作从主存中变量放入工作内存中
- use (使用):作用于工作内存中的变量,它把工作内存中的变量传输给执行引擎,每当虚拟机遇到一个需要使用到变量的值,就会使用到这个指令
- assign (赋值):作用于工作内存中的变量,它把一个从执行引擎中接受到的值放入工作内存的变量副本中
- store (存储):作用于主内存中的变量,它把一个从工作内存中一个变量的值传送到主内存中,以便后续的write使用
- write (写入):作用于主内存中的变量,它把store操作从工作内存中得到的变量的值放入主内存的变量中
JMM对这八种指令的使用,制定了如下规则:
-
- 不允许read和load、store和write操作之一单独出现。即使用了read必须load,使用了store必须write
- 不允许线程丢弃他最近的assign操作,即工作变量的数据改变了之后,必须告知主存
- 不允许一个线程将没有assign的数据从工作内存同步回主内存
- 一个新的变量必须在主内存中诞生,不允许工作内存直接使用一个未被初始化的变量。就是怼变量实施use、store操作之前,必须经过assign和load操作
- 一个变量同一时间只有一个线程能对其进行lock。多次lock后,必须执行相同次数的unlock才能解锁
- 如果对一个变量进行lock操作,会清空所有工作内存中此变量的值,在执行引擎使用这个变量前,必须重新load或assign操作初始化变量的值
- 如果一个变量没有被lock,就不能对其进行unlock操作。也不能unlock一个被其他线程锁住的变量
- 对一个变量进行unlock操作之前,必须把此变量同步回主内存
Volatile
Volatile是java虚拟机提供轻量级的同步机制
1、保证可见性。2、不保证原子性。3、禁止指令重排
CAS
compareandset,期望一个值,如果当前值是期望的值就设置为某个值如果不是就一直循环
循环会耗时,一次性只能保证一个共享变量的原子性,有ABA问题
Unsafe类
为了保证原子性,有AtomicInteger类
public class AtomicInteger extends Number implements java.io.Serializable {
private static final long serialVersionUID = 6214790243416807050L;
// setup to use Unsafe.compareAndSwapInt for updates
private static final Unsafe unsafe = Unsafe.getUnsafe();
private static final long valueOffset;
static {
try {
valueOffset = unsafe.objectFieldOffset
(AtomicInteger.class.getDeclaredField("value"));
} catch (Exception ex) { throw new Error(ex); }
}
private volatile int value;
public final boolean compareAndSet(int expect, int update) {
return unsafe.compareAndSwapInt(this, valueOffset, expect, update);
}
public final int getAndAddInt(Object var1, long var2, int var4) {
int var5;
do {
var5 = this.getIntVolatile(var1, var2);
} while(!this.compareAndSwapInt(var1, var2, var5, var5 + var4));
return var5;
}
java是不能直接操作内存,但是c++可以,java可以调用c++,这里有个后门,java通过内存地址偏移量直接操作内存,因为在多线程里面,使用简单的int型的i++,因为java在操作数据的时候有自己内存模型,可能多线程在操作这个i的时候得到的值还是没有更新的值而导致,无法正确的进行i++,所以我们要用这个直接对内存的值进行操作
CAS:ABA问题
AtomicStampedReference 可以记录版本号,当期望的版本号不对就会修改失败
锁的理解
1.公平锁与非公平锁
公平锁:不能够插队,先到先得
非公平锁:可以插队(默认非公平)
2.可重入锁
也可称为递归锁
可重入就是说某个线程已经获得某个锁,可以再次获取锁而不会出现死锁。
可重入锁有
- synchronized
- ReentrantLock(注意锁和释放锁的次数一定要相对应)
3.自旋锁
spinlock
使用while循环,cas操作,当第一次进去的时候将期望值改变从而第二次进去的时候会进入循环,只有解锁让期望值恢复才能使后面进去的循环结束。
和这个操作是一样的
public final int getAndAddInt(Object var1, long var2, int var4) {
int var5;
do {
var5 = this.getIntVolatile(var1, var2);
} while(!this.compareAndSwapInt(var1, var2, var5, var5 + var4));
return var5;
}
以上是关于java基础(反射,注解,多线程,juc)的主要内容,如果未能解决你的问题,请参考以下文章