A股上市公司传智教育(股票代码 003032)旗下技术交流社区北京昌平校区

 找回密码
 加入黑马

QQ登录

只需一步,快速开始

本帖最后由 小石姐姐 于 2018-4-24 16:19 编辑

                               Java第二阶段day11-第三阶段01

#多线程:
- 进程:当前正在运行的程序。确切的来说,当一个程序进入内存运行,即变成一个进程,进程是处于运行过程中的程序,并且具有一定独立功能。
- 线程:线程是进程中的一个执行单元,负责当前进程中程序的执行,一个进程中至少有一个线程。一个进程中是可以有多个线程的,这个应用程序也可以称之为多线程程序。
- 运行程序:通过进程中的线程来运行。edg:公司接收项目,开发部门开发

##线程的实现:
###方式一:定义Thread的子类,是线程类
    创建线程的步骤:
    1.定义一个类继承Thread。
    2.重写run方法。run方法里是线程执行的任务
    3.创建子类对象,就是创建线程对象。
    4.调用start方法,开启线程并让线程执行,同时还会告诉jvm去调用run方法
     Thread
     *         final String getName()      返回该线程的名称。 不能被重写
     *         void   setName(String name) 改变线程名称,使之与参数 name 相同。
     *         
            MyThread mt1 = new MyThread();
            MyThread mt2 = new MyThread();
            mt1.setName("老王");
            mt2.setName("老宋");
            mt1.start();
            mt2.start();
   
        //线程子类中重写run方法
        @Override
        public void run() {
            //任务
            for (int i = 0; i < 100; i++) {
                //此getName()认为父类对象在子类对象中,两者对外表现为同一对象,名字代表这同一对象的名字
                System.out.println(getName() + ":" + i);
            }
        }


###方式二:实现Runnable接口,是任务类,Thread也实现了Runnable接口
*Runnable接口用来指定每个线程要执行的任务。只包含了一个 run 的无参数抽象方法,需要由接口实现类重写该方法。*
    创建线程的步骤:
    1. 定义类实现Runnable接口。
    2. 覆盖接口中的run方法。
    3. 创建Thread类的对象
    4. 将Runnable接口的子类对象作为参数传递给Thread类的构造函数。
    5. 调用Thread类的start方法开启线程。
        //新建一个任务
        MyThread mt = new MyThread();
        //两个线程共用一个mt,将任务放入线程,任务和线程分离开
        Thread t = new Thread(mt);
        t.setName("老王");
        t.start();
        
        Thread t1 = new Thread(mt);
        t1.setName("张三");
        t1.start();
   
        //任务类中重写run方法
        @Override
        public void run() {
            for (int i = 0; i < 100; i++) {
                //获取当前线程对象:任务在哪个线程执行,就返回哪个线程对象。
                //Thread t = Thread.currentThread();
                //System.out.println(t.getName() + ":" + i);
               
                //链式编程
                System.out.println(Thread.currentThread().getName() + ":" + i + num);
            }
        }

##两种方式的不同:
    - 继承Thread类创建线程,在子类中固定了线程的任务
    - 实现Runnable接口创建的是任务类,Thread类创建不同线程,将任务对象传入线程对象中(构造方法实现)
    - 方式二更灵活,用的更多
---
##多线程安全问题产生和解决
产生:不同线程在各自run()方法中操作共享数据时,由于CPU的高速切换导致某一线程的run()方法会对其他线程操作共享数据产生影响
解决:解决不同线程操作共享资源,没有共享资源不需要安全机制
几种情况下的共享资源(售票为例):
- 三个线程操作同一对象,因此里面ticket是共享的。
- 三个线程操作同一类不同对象,Static ticket 属于类 因此所有对象共用一个ticket
- 定义在run方法中的不共享:线程t.start()执行任务对象中run方法,三个线程调用了三个run方法,i是局部变量超出方法外就死亡,因此是分别定义声明了三个i。
- ticket定义在一个对象中,在调用方法时,run方法操作ticket并没有自己定义

##解决方法:
synchronized(同一对象){}
###同步代码块:
1. 线程接收同一对象
        //同一对象
        TicketeThread tt  = new TicketeThread();
        //不同线程接收
        Thread t1 = new Thread(tt);
        t1.setName("窗口1");
        Thread t2 = new Thread(tt);
        t2.setName("窗口2");
        Thread t3 = new Thread(tt);
        t3.setName("窗口3");
        
   
         // 定义共享的票,同一对象,run方法中没有自己定义,访问的时同一对象的tickete,所以tickete共享
        int tickete = 100;
        //同一对象中的obj为同一个对象
        Object obj = new Object();
   
        @Override
        public void run() {
            while (true) {
                //同一个对象,同一把锁
                synchronized (obj) {
                    if (tickete > 0) {
                        try {
                            // 明确观察出本程序的问题:售票出现重复和负数
                            Thread.sleep(100);
                        } catch (InterruptedException e) {
                            // TODO 自动生成的 catch 块
                            e.printStackTrace();
                        }
                        System.out.println(Thread.currentThread().getName() + ":" + tickete--);
                    }
                }
2. 线程接收不同对象
         TicketeThread tt  = new TicketeThread();
        TicketeThread tt2  = new TicketeThread();
        TicketeThread tt3  = new TicketeThread();
        //
        Thread t1 = new Thread(tt);
        t1.setName("窗口1");
        Thread t2 = new Thread(tt2);
        t2.setName("窗口2");
        Thread t3 = new Thread(tt3);
        t3.setName("窗口3");
    // 定义共享的票 ,静态时不同对象共享票
    static int tickete = 100;
    //但是不同对象中obj对象不同,解决obj也定义为static,类不同对象共享此对象
    Object obj = new Object();

    @Override
    public void run() {
        while (true) {
            //不同对象中,obj对象不同,锁不同,所以依旧会错误
            synchronized (obj) {
                if (tickete > 0) {
                    try {
                        // 明确观察出本程序的问题:售票出现重复和负数
                        Thread.sleep(100);
                    } catch (InterruptedException e) {
                        // TODO 自动生成的 catch 块
                        e.printStackTrace();
                    }
                    System.out.println(Thread.currentThread().getName() + ":" + tickete--);
                }
            }

        }
    }
###同步方法:
###同步代码块
*和同步代码块安全机制一样,一般将具有共享数据的代码锁起来*
    -非静态方法:this作为锁
    //非静态方法:this作为锁
    public synchronized void method2() {
        if (tickete > 0) {
            try {
                // 明确观察出本程序的问题:售票出现重复和负数
                Thread.sleep(100);
            } catch (InterruptedException e) {
                // TODO 自动生成的 catch 块
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName() + ":" + tickete--);
        }
    }
    -静态方法:本类的class字节码对象作为锁
    //对于静态方法,锁为本类的class字节码对象
    public synchronized static void method() {
        if(num>10) {
            System.out.println(Thread.currentThread().getName()+"送第"+num+"份礼物");
            num--;
        }
    }
##如何在使用同步方法是让`while(true)...mathod()`停止循环:
        while (true) {
            boolean flag = method(box);
            //老师利用同步代码块,能够不出错结束循环
            //同步方法如何让循环结束 ??
            if(flag){
                break ;
            }
            
        }
        //同步方法
        private synchronized boolean method(List<Integer> box) {
        if (!bonusPool.isEmpty()) {
            //抽奖
            ...
        }else {
            //奖池为空时再输出奖项
            ...
            //返回标记,代表可以结束循环了
            return true;
        }
            //不可以结束循环
            return false;

        }
##让没执行完的run方法提前终止的方法如下:   
1.  使用退出标志,使线程正常退出,也就是当run方法完成后线程终止。

2.  使用stop方法强行终止线程(这个方法不推荐使用,因为stop和suspend、resume一样,也可能发生不可预料的结果)。     
3.  使用interrupt方法中断线程。     当run方法执行完后,线程就会退出。
4.  但有时run方法是永远不会结束的。如在服务端程序中使用线程进行监听客户端请求,或是其他的需要循环处理的任务。在这种情况下,一般是将这些任务放在一个循环中,如while循环。如果想让循环永远运行下去,可以使用while(true){……}来处理。`但要想使while循环在某一特定条件下退出,`最直接的方法就是`设一个boolean类型的标志`,并通过设置这个标志为true或false来控制while循环是否退出。     
5.  在下面代码中定义了一个退出标志exit,当exit为true时,while循环退出,exit的默认值为false.在定义exit时,使用了一个Java关键字volatile,这个关键字的目的是使exit同步,也就是说在同一时刻只能由一个线程来修改exit的值.
    package chapter2;
     
    public class ThreadFlag extends Thread
    {
        public volatile boolean exit = false;
     
        public void run()
        {
            while (!exit);
        }
        public static void main(String[] args) throws Exception
        {
            ThreadFlag thread = new ThreadFlag();
            thread.start();
            sleep(5000); // 主线程延迟5秒
            thread.exit = true;  // 终止线程thread
            thread.join();
            System.out.println("线程退出!");
        }
    }


-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

* Socket
    * 套接字
    * 用于描述IP地址和端口, 是一种网络编程机制, 通信的两端都会有Socket
* 网络通信3要素
    * 传输协议    传送接收方式
    * IP地址        具体设备     
    * 端口号        具体设备的某个具体服务(进程)
* 常见通信协议
    * UDP
        * 它是一种无连接的不可靠协议(发送端通过Socket发送包,接收端通过Socket接收包,操作包:查看代码后再次修正)
        * 数据传输大小限制为64K(一个包)
        * 不需要建立连接即可传输
        * 数据发送速度快, 发送方只发送数据, 不检查接收方是否真正接收到数据, 所以数据有丢包的情况
        * 这种方式适合实时性要求强的场合, 比如网络电话, 网络游戏等环境, 这种协议延迟很小
    * TCP
        * 它是一种需要建立连接的可靠协议(由于连接,具有了“通道”,可以使用流传输,服务端监听“通道”,获取Socket对象,操作Socket对象:查看代码后再次修正)
        * 没有数据传输大小的限制
        * 在传输前需要先建立连接(三次握手)
        * 它的重发机制保证了数据传输的准确性, 但因为需要接收方发回验证信息, 所以数据发送时间长, 数据流量大
        * 这种方式适合准确性要求强的场合, 比如金融系统, 视频点播, 用户可以等待数据传输但是不能忍受错误


## InetAddress概述和测试

* `java.net.InetAddress`类: 用于表示IP地址对象 (InetAddress的实例包含 IP 地址,还可能包含相应的主机名)
  * 静态方法
    * `static InetAddress getLocalHost()`: 获取本机的InetAddress对象
    * `static InetAddress getByName(String host)`: 根据**主机名****IP的字符串**获取主机的InetAddress对象
    * `static InetAddress getLoopbackAddress()`: 获取回环地址的InetAddress对象. 即`127.0.0.1`` localhost`
    * `static InetAddress getByAddress(byte[] addr)`: 根据IP获取InetAddress对象
        * 如: `InetAddress.getByAddress(new byte[]{(byte)192, (byte)168, (byte)1, (byte)1});`
  * 成员方法
    * `String getHostAddress()`: 返回主机的IP地址
    * `String getHostName()`: 返回主机名



## UDP协议发送数据

* `java.net.DatagramSocket`类: 基于UDP协议的Socket
  * 构造方法
      * `DatagramSocket()`: 创建DatagramSocket对象, 随机分配自己的端口号
      * `DatagramSocket(int port)`: 创建DatagramSocket对象, 指定自己的端口号
  * 成员方法
      * `void send(DatagramPacket p)`: 发送数据包
      * `void receive(DatagramPacket p)`: 接收数据, 数据保存在DatagramPacket对象中
      * `void close()`: 关闭通信, 释放资源
* `java.net.DatagramPacket`类: UDP数据包
    * 构造方法
        * `DatagramPacket(byte[] msg, int msgLength, InetAddress host, int port)`: 创建数据包对象, 指定数据, 目标主机对象, 端口
                * 用于发送端,不仅指定了封装数据的字节数组和数据的大小,还指定了数据包的目标IP地址(addr)和端口号(port)。
                * 该对象通常用于发送端,因为在发送数据时必须指定接收端的IP地址和端口号,就好像发送货物的集装箱上面必须标明接收人的地址一样。
               
        * `DatagramPacket(byte[] buf, int length)`: 创建数据包对象, 接收数据为length的数据, 存入byte数组中
                * 这样的对象只能用于接收端,不能用于发送端。因为发送端一定要明确指出数据的目的地(ip地址和端口号),而接收端不需要明确知道数据的来源,只需要接收到数据即可。
    * 成员方法
        * `InetAddress getAddress()`: 获取数据包发送方的InetAddress对象   
        * `byte[] getData()`: 获取包中的数据, 以byte数组形式
        * `int getLength()`: 获取数据包中数据的长度, 即byte数组的长度
        * `int getPort()`: 获取发送方端口号


###大体原理:
    DatagramPacket数据包的作用就如同是“集装箱”,可以将发送端或者接收端的数据封装起来。然而运输货物只有“集装箱”是不够的,还需要有码头。在程序中需要实现通信只有DatagramPacket数据包也同样不行,为此JDK中提供的一个DatagramSocket类。DatagramSocket类的作用就类似于码头,使用这个类的实例对象就可以发送和接收DatagramPacket数据包。

## UDP收发数据注意事项

* 端口绑定异常:
    * `java.net.BindException: Address already in use: Cannot bind`: 端口已经被占用
    * 如何解决: 要更改为其他端口号, 或将占用端口的应用关闭



## TCP协议发送数据:

*TCP通信是严格区分客户端与服务器端的,在通信时,必须先由客户端去连接服务器端才能实现通信,服务器端不可以主动连接客户端,并且服务器端程序需要事先启动,等待客户端的连接。*
*服务端和客户端通过套接字进行交互,套接字相当于文件因此内容具有格式,需要刷新,读取同理*

###客户端:
* `java.net.Socket`类: 基于TCP协议的Socket, 作为客户端
    * 构造方法
        * `Socket(InetAddress add, int port)`: 创建TCP客户端对象
    * 成员方法
        * `OutputStream getOutputStream()`: 获取输出流对象, **用于发送数据**
        * `InputStream getInputStream()`: 获取输入流, **用于接收数据**
        * `void close()`: 释放资源
* TCP发送数据步骤

```java
// 1. 创建客户端Socket对象(建立连接)
Socket socket = new Socket(InetAddress地址, 端口号);
// 2. 获取输出流对象,返回将字节写入此套接字的输出流。字节在套接字上缓冲。目的地:Socekt
OutputStream outputStream = socket.getOutputStream();
// 3. 发送数据
outputStream.write(byte数组);
// 4. 释放资源
socket.close();
```

* 连接失败异常:
    * `ConnectException: Connection refused: connect`: 连接被拒绝, 无法创建连接. 一般是因为网络不通, IP地址不存在等导致无法连接
    * 解决办法:
        * 确认必须有接收端, 且可以连接


## TCP协议接收数据
###服务端:
* `java.net.ServerSocket`: TCP服务端
    * 构造方法
        * `ServerSocket(int port)`: 创建一个TCP服务端, 并监听指定端口
    * 成员方法
        * `Socket accept()`: 监听数据, 会阻塞. 收到数据后返回Socket对象
        * `void close()`: 关闭Socket
* TCP接收数据步骤

```java
// 1. 创建服务端ServerSocket对象:
ServerSocket serverSocket = new ServerSocket(端口号);  // 指定要监听的端口号

// 2. 监听数据,侦听并接受到此服务器套接字的连接。返回新的Socket套接字(实现客户端和服务端的连接)
Socket socket = serverSocket.accept();                // 该方法会阻塞, 直到有客户端连接

// 3. 获取输入流对象,返回从此套接字读取字节的输入流。数据源:Socket
InputStream inputStream = socket.getInputStream();
// 4. 获取数据
int dataLength = inputStream.read(装数据的byte[]数组);
String s = new String(byte[], 0, dataLength);
// 5. 输出数据
System.out.println(s);
// 6. 释放资源:
socket.close();
serverSocket.close();
```
----------------------------------------------------------------------------------------------------------------------------------------------------------------------


#第1章    反射机制概述、字节码对象的获取方式、反射操作构造方法、成员方法、成员属性
##反射机制的概述  字节码对象的获取方式:
1. Reflect。Java反射机制是在 `运行` 状态中:对于任意一个:
    *类:都能知道这个类的所有属性和方法(包括私有)。
    *对象:都能够调用它的任意一个方法(包括私有)。
这种`动态获取`的以及`动态调用对象的方法`的功能称为Java的反射机制
2. 反射目的:
    *反射可以在不改变源代码的情况下...
3. 反射的前提:
    * 要获取类的字节码对象(Class对象)
    - 字节码对象的获取方式(三种):


---


##反射操作构造方法:
`字节码对象` -->`构造方法对象`-->`通过构造方法创建对象`
通过反射获取构造方法并使用:
*Class
    `Constructor<?>[] getConstructors()` :得到`无参的构造方法对象`数组
    `Constructor<T> getConstructor(Class<?>... parameterTypes)`:
        - 根据不同参数列表区分不同构造方法,返回有参的构造方法对象
        - `本方法参数列表` 为 `构造方法参数列表` 的class对象(主要是为了区分)
    `T newInstance()` 只能用于无参,一步创建具体类的对象
    *构造方法以对象形式进行返回(属性、方法也是)
*Constructor:
    `T newInstance(Object... initargs)`:
        根据具体参数返回具体的JavaBean对象,参数类型和得到构造方法对象时的参数列表一致(主要是为了初始化)

---
##反射操作成员变量
`字节码对象` -->`获取JavaBean对象`-->`成员变量对象`-->公共:`非静态变量值跟随具体对象,使用时需要传入具体对象`
                |                                  |_私有:多一步:setAccessible让jvm不检查权限
                |_`构造方法对象`-->`通过构造方法创建对象`
                |_ `class一步获取`

通过反射获取成员变量并使用  
* Class:
*      `只能获取public修饰的,其他三种均不可,可以直接操作`
*        Field[] getFields()  :返回一个存储`公共成员变量对象`的数组
*        Field getField(String name)  :返回`指定名称``公共成员变量`对象
*        `能获取所有权限的,私有成员操作需要多一步`
*        Field[] getDeclaredFields()  :返回一个存储`所有权限的成员变量对象`的数组
*        Field getDeclaredField(String name) :...返回`指定名称`的成员变量对象
*
* Field:
* 公共(直接使用以下下方法):
* 私有(通过setAccessible(boolean b)让jvm不检查权限,再使用以下方法):
*
*         Object get(Object obj)  :返回`成员变量对象`跟随的`JavaBean对象(具体)`的值
*         void set(Object obj, Object value) :设置`成员变量对象`跟随的`JavaBean对象(具体)`的值

------------------------------------------------------------------------------------------------------------------------------------------

#HTML概述:
概念:
HTML:Hyper Text Markup Language  --- 超文本标记语言.
    * 标记语言:指的是通过一组标签的形式描述事物的一门语言.
    * 超文本:比普通的文本更强大.
作用:
HTML是用来制作页面(静态页面).
    *静态页面:假的数据,不是从数据库动态取得的
应用场景:
在设计网站的页面的时候都要使用到HTML.

小结:
    - HTML学习的就是标签,具有结构性(标签表现)
    - html文件被浏览器解析运行(java文件被JVM解析运行)
    - 标签的使用:
        * <标签 属性 = "" 属性 = "">
            标签元素:<元素标签></元素标签>
          </标签>
        * 不需要包围文字的标签可以:<标签/>使用
        * 信息被标签包围,属性在标签内,用来设置信息

##HTML的字体标签:
<font>标签:HTML中的字体标签.使用:<font>文字</font>
    *三个属性 color颜色 size字体大小 face字体类型
    *字体颜色:
        英文单词
        配色方案(Java程序员不推荐) :红绿蓝 16进制 :#FFFFFF两位一个颜色,组合配色
    *字体大小:1-7 超过按最值算
##HTML的排版标签:
*一类标签*
标题标签:h标签<h1>...<h6> 从h1-h6分为六个等级的标题
段落标签:<p>内容</p> 包围的为一段
B I U :字体加粗 字体斜体 字体下划线
居中标签:<center>内容</center>
#HTML的图片标签:
重点:图片标签`<img/>` 属性多
* 属性:
         * src    :图片的来源.
             src 相对路径,相对位置:先找自己在哪,再找目标
            ../  上一级
         * width    :图片的宽度.
         * height:图片的高度.
         * alt    :图片找不到显示的内容.
##【HTML的列表标签】:
有序列表
    <ol>
        <li>列表</li>
            ...
        <li>列表项</li>
    </ol>
有序列表的属性:
    * type属性:
        * 1        :数字类型
        * a        :英文类型
        * i        :罗马字符
    * start属性:从哪开始

无序列表(unlist?)
    <ul>
        <li>列表项</li>
            ...
        <li>列表项</li>
    </ul>
无序列表的属性:
    * type属性
        * disc    :实心点.
        * circle    :空心圆
        * square :方块.


##【HTML的超链接标签】
HTML的超链接标签:<a>
属性:
    * href    :链接的路径(跳转的地方)
    * target    :打开的方式
        * _self    :在自身页面打开
        * _blank    :新打开一个窗口
        * _parent    :在父元素页面打开(需要框架支撑)

#【HTML的表格标签】
重点:
    - 表格:很规整(牵一发而动全身)
    - 创建时根据最大生成总表格大小
    - 不能单独只把一个单元格改变大小,会改变牵扯到的其他单元格
    - 在单元格中操作,不能操作行
    -
    - <td></td> 相当于一个单元格:属性:colspan||rowspan 自己占几格
    - 百分比:相对于父类标签

表格的嵌套(在单元格中),表格单元格的合并
表格:
<table>
<tr>
    <td></td>
    <td></td>
</tr>
</table>

属性:
    width    :表格宽度
    height    :表格高度
    border    :边框
    align    :表格水平位置:
        * left
        * center
        * right

<td>的属性:
    * colspan=”列数”
    * rowspan=”行数”


#【HTML的表单标签(*****)】:

表单元素提交需要 :
    name 属性
    type
    value值

HTML的表单标签:<form>
     * 常用属性:
          * action属性:提交的路径.默认提交到当前页面
          * method属性:请求的方式.GET和POST.默认是GET.
              ***** GET方式和POST方式的区别?
                   * GET    :数据会显示到地址栏中.GET方式提交是有大小的限制.
                   * POST    :数据不会显示到地址栏中.POST方式提交的是没有大小限制.
HTML中表单元素:
* <input type=”text”>        :文本框.
    * 常用属性:
        * name    :表单元素的名称.必须有name属性,然后后台才可以接收数据.
        * value    :文本框的默认值.
        * size    :文本框的长度.
        * maxlength:文本框输入的最大长度.
        * readonly:只读文本框.
* <input type=”password”>    :密码框.
    * 常用属性:
        * name    :表单元素的名称.必须有name属性,然后后台才可以接收数据.
        * value    :密码框的默认值.
        * size    :密码框的长度.
        * maxlength:密码框输入的最大长度.
        

* <input type=”radio”>        :单选按钮.
    * 常用的属性:
        * name    :表单元素的名称.必须有name属性,然后后台才可以接收数据.
        * value    :单选按钮的默认值.
        * checked:单选按钮默认被选中.

* <input type=”checkbox”>    :复选按钮.
* 常用的属性:
        * name    :表单元素的名称.必须有name属性,然后后台才可以接收数据.
        * value    :单选按钮的默认值.
        * checked:单选按钮默认被选中.

* <input type=”button”>        :普通按钮.没有任何功能的按钮.
* <input type=”submit”>        :提交按钮.
* <input type=”reset”>        :重置按钮.
* <input type=”file”>        :文件上传的表单项.
* <input type=”hidden”>        :隐藏字段.
* <input type=”image”>        :图片按钮

* <select>                    :下拉列表.
* <textarea>                :文本域.


0 个回复

您需要登录后才可以回帖 登录 | 加入黑马