欢迎光临散文网 会员登陆 & 注册

多线程+注解+发射+内部类+socket基础

2023-02-12 00:19 作者:鹿是不是鹿  | 我要投稿

大家好, 我是鹿是不是鹿, 以下是关于多线程+注解+发射+内部类+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        客户端


多线程+注解+发射+内部类+socket基础的评论 (共 条)

分享到微博请遵守国家法律