多线程+注解+发射+内部类+socket基础
大家好, 我是鹿是不是鹿, 以下是关于多线程+注解+发射+内部类+socket通信相关的一些基础知识, 希望能给大家带来帮助。
1.1 进程
1.1.1 概念
就是正在运行的程序。也就是代表了程序锁占用的内存区域。
1.1.2 特点
l 独立性:进程是系统中独立存在的实体,它可以拥有自己的独立的资源,每一个进程都拥有自己私有的地址空间。在没有经过进程本身允许的情况下,一个用户进程不可以直接访问其他进程的地址空间。
l 动态性:进程与程序的区别在于,程序只是一个静态的指令集合,而进程是一个正在系统中活动的指令集合。在进程中加入了时间的概念,进程具有自己的生命周期和各种不同的状态,这些概念在程序中都是不具备的。
l 并发性:多个进程可以在单个处理器上并发执行,多个进程之间不会互相影响。
1.2 线程
1.2.1 概念
线程(thread)是操作系统能够进行运算调度的最小单位。它被包含在进程之中,是进程中的实际运作单位。一个进程可以开启多个线程。
多线程扩展了多进程的概念,使得同一个进程可以同时并发处理多个任务。
简而言之,一个程序运行后至少一个进程,一个进程里包含多个线程。
如果一个进程只有一个线程,这种程序被称为单线程。
如果一个进程中有多条执行路径被称为多线程程序。

1.2.2 进程和线程的关系

从上图中可以看出一个操作系统中可以有多个进程,一个进程中可以有多个线程,每个进程有自己独立的内存,每个线程共享一个进程中的内存,每个线程又有自己独立的内存。(记清这个关系,非常重要!)
所以想使用线程技术,得先有进程,进程的创建是OS创建的,你能实现吗?不能,一般都是c或者c++语言完成的。
1.3 多线程的特性
1.3.1 随机性

1.3.2 线程状态

线程生命周期,总共有五种状态:
1) 新建状态(New):当线程对象对创建后,即进入了新建状态,如:Thread t = new MyThread();
2) 就绪状态(Runnable):当调用线程对象的start()方法(t.start();),线程即进入就绪状态。处于就绪状态的线程,只是说明此线程已经做好了准备,随时等待CPU调度执行,并不是说执行了t.start()此线程立即就会执行;
3) 运行状态(Running):当CPU开始调度处于就绪状态的线程时,此时线程才得以真正执行,即进入到运行状态。注:就绪状态是进入到运行状态的唯一入口,也就是说,线程要想进入运行状态执行,首先必须处于就绪状态中;
4) 阻塞状态(Blocked):处于运行状态中的线程由于某种原因,暂时放弃对CPU的使用权,停止执行,此时进入阻塞状态,直到其进入到就绪状态,才有机会再次被CPU调用以进入到运行状态;
5) 根据阻塞产生的原因不同,阻塞状态又可以分为三种:
a) 等待阻塞:运行状态中的线程执行wait()方法,使本线程进入到等待阻塞状态;
b) 同步阻塞:线程在获取synchronized同步锁失败(因为锁被其它线程所占用),它会进入同步阻塞状态;
c) 其他阻塞:通过调用线程的sleep()或join()或发出了I/O请求时,线程会进入到阻塞状态。当sleep()状态超时、join()等待线程终止或者超时、或者I/O处理完毕时,线程重新转入就绪状态。
6) 死亡状态(Dead):线程执行完了或者因异常退出了run()方法,该线程结束生命周期。
1.4 多线程创建1:继承Thread
1.4.1 概述
Thread类本质上是实现了Runnable接口的一个实例,代表一个线程的实例。启动线程的唯一方法就是通过Thread类的start()实例方法。Start()方法是一个native方法,它将通知底层操作系统,最终由操作系统启动一个新线程,操作系统将执行run()方法。这种方式实现多线程很简单,通过自己的类直接extend Thread,并复写run()方法,就可以启动新线程并执行自己定义的run()方法。
模拟开启多个线程,每个线程调用run()方法
1.4.2 常用方法
String getName()
返回该线程的名称。
static Thread currentThread()
返回对当前正在执行的线程对象的引用。
void setName(String name)
改变线程名称,使之与参数 name 相同。
static void sleep(long millis)
在指定的毫秒数内让当前正在执行的线程休眠(暂停执行),此操作受到系统计时器和调度程序精度和准确性的影响。
void start()
使该线程开始执行;Java 虚拟机调用该线程的 run 方法。
Thread(String name)
分配新的 Thread 对象。
1.4.3 测试
1.5 多线程创建2:实现Runnable接口
1.5.1 概述
如果自己的类已经extends另一个类,就无法多继承,此时,可以实现一个Runnable接口。
1.5.2 常用方法
void run()
使用实现接口 Runnable 的对象创建一个线程时,启动该线程将导致在独立执行的线程中调用对象的 run 方法。
1.5.3 测试
1.5.4 比较

1.6 售票案例
设计4个售票窗口,总计售票100张。
用多线程的程序设计并写出代码。
1.6.1 方案1:继承Thread
1.6.2 方案2:实现Runnable
1.6.3 问题
1、 每次创建线程对象,都会生成一个tickets变量值是100,创建4次对象就生成了400张票了。不符合需求,怎么解决呢?能不能把tickets变量在每个对象间共享,就保证多少个对象都是卖这100张票。-- 用静态修饰
2、 产生超卖,-1张、-2张。
3、 产生重卖,同一张票卖给多人。
4、 多线程安全问题是如何出现的?常见情况是由于线程的随机性+访问延迟。
5、 以后如何判断程序有没有线程安全问题?在多线程程序中+有共享数据+多条语句操作共享数据。
2.1 同步锁
把有可能出现问题的代码包起来,一次只让一个线程执行。通过sychronized关键字实现同步。
当多个对象操作共享数据时,可以使用同步锁解决线程安全问题。
2.1.1 synchronized
synchronized(对象){
需要同步的代码;
}
2.1.1 特点
1、 前提1,同步需要两个或者两个以上的线程。
2、 前提2,多个线程间必须使用同一个锁。
3、 同步的缺点是会降低程序的执行效率, 为了保证线程安全,必须牺牲性能。
4、 可以修饰方法称为同步方法,使用的锁对象是this。
5、 可以修饰代码块称为同步代码块,锁对象可以任意。
2.1.2 改造
2.2 单例设计模式
2.2.1 概念
单例模式可以说是大多数开发人员在实际中使用最多的,常见的Spring默认创建的bean就是单例模式的。
单例模式有很多好处,比如可节约系统内存空间,控制资源的使用。
其中单例模式最重要的是确保对象只有一个。
简单来说,保证一个类在内存中的对象就一个。
RunTime就是典型的单例设计,我们通过对RunTime类的分析,一窥究竟。
2.2.2 源码剖析
2.2.3 饿汉式
2.2.4 懒汉式
2.3 注解
2.3.1 概念
注解很厉害,它可以增强我们的java代码,同时利用反射技术可以扩充实现很多功能。它们被广泛应用于三大框架底层。传统我们通过xml文本文件声明方式,而现在最主流的开发都是基于注解方式,代码量少,框架可以根据注解去自动生成很多代码,从而减少代码量,程序更易读。例如最火爆的SpringBoot就完全基于注解技术实现。
注解设计非常精巧,初学时觉得很另类甚至多余,甚至垃圾。有了java代码干嘛还要有@注解呢?但熟练之后你会赞叹,它竟然可以超越java代码的功能,让java代码瞬间变得强大。大家慢慢体会吧。
常见的元注解:@Target、@Retention,jdk提供将来描述我们自定义的注解的注解。听起来好绕,别着急,做两个例子,立刻清晰。现在现有“元注解”这个概念。
2.3.2 分类
l JDK自带注解
l 元注解
l 自定义注解
2.3.3 JDK注解
JDK注解的注解,就5个:
l @Override
l @Deprecated标记就表明这个方法已经过时了,但我就要用,别提示我过期
l @SuppressWarnings(“deprecation”) 忽略警告
l @SafeVarargs jdk1.7出现,堆污染,不常用
l @FunctionallInterface jdk1.8出现,配合函数式编程拉姆达表达式,不常用
2.3.4 元注解
描述注解的注解,就5个:
l @Target 注解用在哪里:类上、方法上、属性上
l @Retention 注解的生命周期:源文件中、class文件中、运行中
l @Inherited 允许子注解继承
l @Documented 生成javadoc时会包含注解,不常用
l @Repeatable注解为可重复类型注解,可以在同一个地方多次使用,不常用
2.4 元注解
2.4.1 @Target ElementType.class
描述注解的使用范围:
l ElementType.ANNOTATION_TYPE 应用于注释类型
l ElementType.CONSTRUCTOR 应用于构造函数
l ElementType.FIELD 应用于字段或属性
l ElementType.LOCAL_VARIABLE 应用于局部变量
l ElementType.METHOD 应用于方法级
l ElementType.PACKAGE 应用于包声明
l ElementType.PARAMETER 应用于方法的参数
l ElementType.TYPE 应用于类的元素
2.4.2 @Retention RetentionPolicy.class
定义了该注解被保留的时间长短,某些注解仅出现在源代码中,而被编译器丢弃;
而另一些却被编译在class文件中; 编译在class文件中的注解可能会被虚拟机忽略,而另一些在class被装载时将被读取。
为何要分有没有呢?没有时,反射就拿不到,从而就无法去识别处理。
l SOURCE 在源文件中有效(即源文件保留)
l CLASS 在class文件中有效(即class保留)
l RUNTIME 在运行时有效(即运行时保留)
2.5 自定义注解
2.5.1 定义注解
//1,定义注解
//1.1,设置注解的使用范围@Target,啥都不写,哪儿都能用
//@Target({ElementType.METHOD})//作用于方法上
//@Target({ElementType.FIELD})//作用于属性上
@Target({ElementType.METHOD , ElementType.PACKAGE})//作用范围
@Retention(RetentionPolicy.SOURCE)//生命周期
@Target({ElementType.TYPE})//作用于类上
@interface Test{
//3,定义属性
int age() default 0;//使用时,必须给age属性赋值,如:age=X。除非设置好默认值。
//()不是参数,也不能写参数,只是特殊语法
//4,特殊属性value
String value() default "";//使用时,必须给value属性赋值,如:X | value=X。除非设置好默认值
}
注意:注解的语法写法和常规java的语法写法不同
2.5.2 使用注解
//2,使用注解
//@Test
//5,注解的组合属性
@Test(value="",age=0)
class HelloTest{
// @Test(value="",age=0)
String name;
}
2.5.3 解析注解
判断注解是否存在
3.1 反射
3.1.1 概念
Reflection(反射) 是 Java 程序开发语言的特征之一,它允许运行中的 Java 程序对自身进行检查,或者说“自审”,也有称作“自省”。反射非常强大,它甚至能直接操作程序的私有属性。我们前面学习都有一个概念,private的只能类内部访问,外部是不行的,但这个规定被反射赤裸裸的打破了。
反射就像一面镜子,它可以在运行时获取一个类的所有信息,可以获取到任何定义的信息(包括成员变量,成员方法,构造器等),并且可以操纵类的字段、方法、构造器等部分。
3.1.2 为什么需要反射
好好的我们new User(); 不是很好,为什么要去通过反射创建对象呢?
那我要问你个问题了,你为什么要去餐馆吃饭呢?
例如:我们要吃个牛排大餐,如果我们自己创建,就什么都得管理。
好处是,每一步做什么我都很清晰,坏处是什么都得自己实现,那不是累死了。牛接生你管,吃什么你管,屠宰你管,运输你管,冷藏你管,烹饪你管,上桌你管。就拿做菜来说,你能有特级厨师做的好?
那怎么办呢?有句话说的好,专业的事情交给专业的人做,饲养交给农场主,屠宰交给刽子手,烹饪交给特级厨师。那我们干嘛呢?
我们翘起二郎腿直接拿过来吃就好了。
再者,饭店把东西做好,不能扔到地上,我们去捡着吃吧,那不是都成原始人了。那怎么办呢?很简单,把做好的东西放在一个容器中吧,如把牛排放在盘子里。
在开发的世界里,spring就是专业的组织,它来帮我们创建对象,管理对象。我们不在new对象,而直接从spring提供的容器中beans获取即可。Beans底层其实就是一个Map<String,Object>,最终通过getBean(“user”)来获取。而这其中最核心的实现就是利用反射技术。
总结一句,类不是你创建的,是你同事或者直接是第三方公司,此时你要或得这个类的底层功能调用,就需要反射技术实现。有点抽象,别着急,我们做个案例,你就立马清晰。
3.1.3 反射Class类对象
Class.forName(“类的全路径”);
类名.class
对象.getClass();
3.1.4 常用方法
获得包名、类名
clazz.getPackage().getName()//包名
clazz.getSimpleName()//类名
clazz.getName()//完整类名
!!成员变量定义信息
getFields()//获得所有公开的成员变量,包括继承的变量
getDeclaredFields()//获得本类定义的成员变量,包括私有,不包括继承的变量
getField(变量名)
getDeclaredField(变量名)
!!构造方法定义信息
getConstructor(参数类型列表)//获得公开的构造方法
getConstructors()//获得所有公开的构造方法
getDeclaredConstructors()//获得所有构造方法,包括私有
getDeclaredConstructor(int.class, String.class)
方法定义信息
getMethods()//获得所有可见的方法,包括继承的方法
getMethod(方法名,参数类型列表)
getDeclaredMethods()//获得本类定义的方法,包括私有,不包括继承的方法
getDeclaredMethod(方法名, int.class, String.class)
反射新建实例
c.newInstance();//执行无参构造
c.newInstance(6, "abc");//执行有参构造
c.getConstructor(int.class, String.class); //执行含参构造,获取构造方法
反射调用成员变量
c.getDeclaredField(变量名); //获取变量
c.setAccessible(true); //使私有成员允许访问
f.set(实例, 值); //为指定实例的变量赋值,静态变量,第一参数给 null
f.get(实例); //访问指定实例的变量的值,静态变量,第一参数给 null
反射调用成员方法
获取方法
Method m = c.getDeclaredMethod(方法名, 参数类型列表);
m.setAccessible(true) ;//使私有方法允许被调用
m.invoke(实例, 参数数据) ;//让指定的实例来执行该方法
3.2 反射的应用
3.2.1 获取类对象
private static void method() throws Exception {
Class clazz = Student.class;
Class<?> clazz2 = Class.forName("seday15.Student");
Class clazz3 = new Student().getClass();
System.out.println(clazz.getName());
System.out.println(clazz2.getName());
System.out.println(clazz3.getName());
}
3.2.2 获取构造方法
private static void method3(Class clazz) {
Constructor[] cs = clazz.getDeclaredConstructors();
for (Constructor c : cs) {
String name = clazz.getSimpleName();
System.out.println(name);
Class[] cs2 = c.getParameterTypes();//参数
System.out.println(Arrays.toString(cs2));
}
}
3.2.3 获取成员方法
private static void method4(Class clazz) {
Method[] ms = clazz.getMethods();
for (Method m : ms) {
String name = m.getName();
System.out.println(name);
Class<?>[] cs = m.getParameterTypes();
System.out.println(Arrays.toString(cs));
}
}
3.2.4 获取成员变量
private static void method2(Class clazz) {
Field[] fs = clazz.getFields();//获取public的属性
for (Field f : fs) {
String name = f.getName();
String tname = f.getType().getSimpleName();
System.out.println(name);
System.out.println(tname);
}
}
3.2.5 创建对象
3.3 暴力反射
指可以将程序中的私有的属性或者方法通过反射技术,暴力的获取到资源。需要使用的常见方法如下:

3.3.1 创建Person类
class Person{
private String name="jack";
private int age = 30;
private void show(int[] a) {
System.out.println("show()..."+Arrays.toString(a));
}
private void test() {
System.out.println("test()...");
}
}
3.3.2 测试
1、 获取私有属性值并修改
2、 获取私有方法并执行
3.4 内部类
3.4.1 概述
如果一个类存在的意义就是为指定的另一个类,可以把这个类放入另一个类的内部。就是把类定义在类的内部的情况就可以形成内部类的形式。
A类中又定义了B类,B类就是内部类。B类可以当做A类的一个成员看待。
3.4.2 特点
1、 内部类可以直接访问外部类中的成员,包括私有成员
2、 外部类要访问内部类的成员,必须要建立内部类的对象
3、 在成员位置的内部类是成员内部类
4、 在局部位置的内部类是局部内部类
3.4.3 成员内部类
被private修饰
被static修饰
3.4.4 匿名内部类
匿名内部类属于局部内部类,并且是没有名字的内部类。
4.1 网络
查看本机ip地址


4.2 Socket
4.2.1 概述
也叫套接字编程,是一个抽象层。
应用程序可以通过它发送或接收数据,可对其像对文件一样的打开、读写和关闭等操作。套接字允许应用程序将I/O插入到网络中,并与网络中的其他应用程序进行通信。网络套接字是IP地址与端口与协议的组合。
Socket就是为网络编程提供的一种机制 / 通信的两端都有Socket
网络通信其实就是Socket间的通信 / 数据在两个Socket间通过IO传输

4.3 服务器端-ServerSocket
在服务器端,选择一个端口号,在指定端口上等待客户端发起连接。
启动服务:ServerSocket ss = new ServerSocket(端口);
等待客户端发起连接,并建立连接通道:Sokcet socket = ss.accept();
4.4 客户端-Socket
新建Socket对象,连接指定ip的服务器的指定端口
Socket s = new Socket(ip, port);
从Socket获取双向的流
InputStream in = s.getInputStream();
OutputStream out = s.getOutputStream();
4.5 入门案例
服务器端接收客户端发来的hello,并给客户端响应hello。
4.5.1 服务器端
说明其中,server端的accept()是阻塞的,客户端不连接,服务器不执行后面流程。
in.read()也是阻塞的,读不到就死等。
4.5.2 客户端