网络、io和多线程

说明:io部分来自李刚的《疯狂Java讲义》

1、网络

OSI参考模型:
应用层:所有能够产生网络流量的程序
表示层:在传输之前是否进行加密 或 压缩处理
会话层:可以查木马 netstat -n
传输层:可靠传输 流量控制 不可靠传输
网络层:负责选择最佳路径 规划IP地址
数据链路层:帧的开始和结束 透明传输(中间加入转义符) 差错检验
物理层:接口标准 电器标准 如何在物理链路上传输更快的速度

1580702783(1).png
1580904402(1).png

TCP:需要将要传输的文件分段 传输 建立会话 可靠传输 流量控制 比如qq传文件
UDP:一个数据包就能够完成数据通信 不分段 不需要建立会话 不需要流量控制 不可靠传输 比如 qq聊天


1580905970(1).png

1580906108(1).png

如何查看服务侦听的端口:
netstat -an
netstat -n 查看建立的会话
netstat -nb 查看建立会话的进程
telnet 192.168.80.100 3389 测试远程计算机某个端口是否打开

传输层(TCP/UDP协议):提供应用进程间的逻辑通信
网络层(IP协议):提供主机之间的逻辑通信

TCP:
面向连接的传输层协议
每一条TCP连接只能有两个端点(endpoint),每一条TCP连接只能是点对点的(一对一)
TCP提供可靠交付的服务
TCP提供全双工通信
面向字节流
TCP把连接作为最基本的抽象
TCP连接的端点不是主机,不是主机的IP地址,不是应用程序,也不是传输层协议端口,TCP连接的端点叫做套接字(socket).
端口号拼接到IP地址即构成了套接字。


1581043964(1).png

服务使用 TCP或UDP的端口侦听客户端请求
客服端使用IP地址定位服务器 使用目标端口定位服务

DNS服务作用:
负责解析域名 将域名解析成IP
域名解析测试:
ping www.baidu.com
nslookup www.baidu.com

使用web代理服务器访问网站:
1、节省内网访问Internet的带宽
2、通过Web代理绕过防火墙

恶意程序:
1、计算机病毒——会“传染”其他程序的程序
2、计算机蠕虫——通过网络的通信功能将自身从一个结点发送到另一个结点并启动运行的程序。(吃CPU和内存)
3、特洛伊木马——它执行的功能超出所声称的功能
4、逻辑炸弹——当运行环境满足某种特定条件时执行其他特殊功能的程序

ARP协议:将IP地址通过广播(目标MAC地址是FF-FF-FF-FF-FF-FF)解析目标IP地址的MAC地址,扫描本网段MAC地址。

tracert baidu.com.cn 可以跟踪路由器
网络通的关键,数据有去有回
静态路由:需要管理员告诉路由器所有没有直连的网络下一跳给谁。
动态路由:RIP协议 周期性广播路由表 跳数 30秒更新一次路由信息 最大跳数15跳

FTP(文件传输)协议:
连接方式:
控制连接:标准端口为21,用于发送FTP命令信息
数据连接:标准端口为20,用于上传、下载数据
数据连接的建立类型:
主动模式 服务端从20端口主动向客户端发起连接
被动模式 服务端在指定范围内的某个端口被动等待客户端发起连接
FTP服务器端如果有防火墙 需要在防火墙开20和21端口 使用主动模式进行数据连接

因特网提供的音频/视频服务大体上可分为三种类型:
1、流式存储音频/视频 ——边加载边播放
节省客户端硬盘空间 不用下载 保护视频版权
2、流式 实况音频/视频 ——边录制边发送
通过网络现场直播
3、交互式音频/视频 ——实时交互式通信

在Internet上传输音频视频面临哪些问题:
1、延迟 对于非交互式的音频视频影响不大
2、带宽不稳定

2、NIO(New IO)

将文件或文件的一段区域映射到内存中,这样就可以像访问内存一样来访问文件了(模拟了操作系统上的虚拟内存的概念)。

Channel(通道)和Buffer(缓冲)是新IO中的两个核心对象。
Channel是对传统的输入/输出系统的模拟,在新IO中所有的数据都需要通过通道传输。
Buffer可以理解为一个容器,本质是一个数组,发送到Channel中的所有对象都必须先放到Buffer中,而从Channel中读取的数据也必须先放到Buffer中。
实际使用较多的是ByteBuffer和CharBuffer。其中ByteBuffer类还有一个子类:MappedByteBuffer,它用于表示Channel将磁盘文件的部分或全部内容映射到内存中后得到的结果,通常MappedByteBuffer对象由Channel的map()方法返回。

在Buffer中有3个重要的概念:容量(capacity)、界限(limit)和位置(position)。

  • 容量(capacity):缓冲区的容量(capacity)表示该Buffer的最大数据容量,即最多可以存储多少数据。缓冲区的容量不可能为负值,创建后不能改变。
  • 界限(limit):第一个不应该被读出或者写入的缓冲区位置索引。也就是说,位于limit后的数据既不可被读,也不可被写。
  • 位置(position):用于指明下一个可以被读出的或者写入的缓冲区位置索引(类似于IO流中的记录指针)。当使用Buffer从Channel中读取数据时,position的值恰好等于已经读到了多少数据。当刚刚新建一个Buffer对象时,其position为0;如果从Channel中读取了2个数据到该Buffer中,则position为2,指向Buffer中第3个(第1个位置的索引为0)位置。

当Buffer装入数据结束后,调用Buffer的flip()方法,该方法将limit设置为position所在位置,并将position设为0,这就使得Buffer的读写指针又移到了开始位置。也就是说,Buffer调用flip()方法之后,Buffer为输出数据做好准备;
当Buffer输出数据结束后,Buffer调用clear()方法,clear()方法不是清空Buffer的数据,它仅仅将position置为0,将limit置为capacity,这样为再次向Buffer中装入数据做好准备。

所有的Channel都不应该通过构造器来直接创建,而是通过传统的节点InputStream、OutputStream的getChannel()方法来返回对应的Channel,不同的节点流获得的Channel不一样。

Channel中最常用的3类方法是map()read()write(),其中map()方法用于将Channel对应的部分或全部数据映射成ByteBuffer;而read()或write()方法都有一系列重载形式,这些方法用于从Buffer中读取数据或向Buffer中写入数据。

下面程序示范了直接将FileChannel的全部数据映射成ByteBuffer的效果。

public class FileChannelTest
        {
            public static void main(String[] args)
            {
                  File f=new File("FileChannelTest.java");
                  try(
                        // 创建FileInputStream,以该文件输入流创建FileChannel
                        FileChannel inChannel=new FileInputStream(f).getChannel();
                        // 以文件输出流创建FileChannel,用以控制输出
                        FileChannel outChannel=new FileOutputStream("a.txt")
                            .getChannel())
                  {
                        // 将FileChannel里的全部数据映射成ByteBuffer
                        MappedByteBuffer buffer=inChannel.map(FileChannel
                            .MapMode.READ_ONLY , 0 , f.length());   // ①
                        // 使用GBK的字符集来创建解码器
                        Charset charset=Charset.forName("GBK");
                        // 直接将buffer里的数据全部输出
                        outChannel.write(buffer);    // ②
                        // 再次调用buffer的clear()方法,复原limit、position的位置
                        buffer.clear();
                        // 创建解码器(CharsetDecoder)对象
                        CharsetDecoder decoder=charset.newDecoder();
                        // 使用解码器将ByteBuffer转换成CharBuffer
                        CharBuffer charBuffer=decoder.decode(buffer);
                        // CharBuffer的toString方法可以获取对应的字符串
                        System.out.println(charBuffer);
                  }
                  catch (IOException ex)
                  {
                        ex.printStackTrace();
                  }
            }
        }

虽然FileChannel既可以读取也可以写入,但FileInputStream获取的FileChannel只能读,而FileOutputStream获取的FileChannel只能写。

3、多线程(并发编程)

互斥同步(Mutual Exclusion & Synchronization)是一种最常见也是最主要的并发正确性保障手段。同步是指在多个线程并发访问共享数据时,保证共享数据在同一个时刻只被一条(或者是一些,当使用信号量的时候)线程使用。而互斥是实现同步的一种手段,临界区(Critical Section)、互斥量(Mutex)和信号量(Semaphore)都是常见的互斥实现方式。因此在“互斥同步”这四个字里面,互斥是因,同步是果;互斥是方法,同步是目的。

3.1 Semaphore和Exchanger

类Semaphore的主要作用是限制并发执行的线程个数。

在类A中使用Semaphore作为全局变量,类A是单例模式的,就可以实现控制线程并发数的功能。semaphore.acquire()和semaphore.release()之间的代码只有有限线程可以访问,其他处于阻塞状态。

import java.util.concurrent.Semaphore;
        public class Service {
            private Semaphore semaphore = new Semaphore(1);
            public void testMethod() {
                try {
                    semaphore.acquire();
                    System.out.println(Thread.currentThread().getName()
                                  + " begin timer=" + System.currentTimeMillis());
                          Thread.sleep(5000);
                          System.out.println(Thread.currentThread().getName()
                                  + "    end timer=" + System.currentTimeMillis());
                          semaphore.release();
                      } catch (InterruptedException e) {
                          e.printStackTrace();
                      }
                  }
            }
类Exchanger可以使2个线程之间传输数据,比生产者/消费者模式使用的wait/notify更加方便。

创建ThreadB.java类代码如下:

package extthread;
     import java.util.concurrent.Exchanger;
     public class ThreadA extends Thread {
        private Exchanger<String> exchanger;
        public ThreadA(Exchanger<String> exchanger) {
            super();
            this.exchanger = exchanger;
        }
        @Override
        public void run() {
            try {
                System.out.println("在线程A中得到线程B的值=" + exchanger.exchange("中国人A"));
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

创建ThreadB.java类代码如下:

    package extthread;
    import java.util.concurrent.Exchanger;
    public class ThreadB extends Thread {
        private Exchanger<String> exchanger;
        public ThreadB(Exchanger<String> exchanger) {
            super();
            this.exchanger = exchanger;
        }
        @Override
        public void run() {
            try {
                System.out.println("在线程B中得到线程A的值=" + exchanger.exchange("中国人B"));
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

运行类Run.java代码如下:

    package test.run;
    import java.util.concurrent.Exchanger;
    import extthread.ThreadA;
    import extthread.ThreadB;
    public class Run {
        public static void main(String[] args) {
                  Exchanger<String> exchanger = new Exchanger<String>();
                  ThreadA a = new ThreadA(exchanger);
                  ThreadB b = new ThreadB(exchanger);
                  a.start();
                  b.start();
              }
          }

程序运行后输出:
在线程B中得到线程A的值=中国人A
在线程A中得到线程B的值=中国人B

3.2 CountDownLatch的使用

类CountDownLatch所提供的功能是判断count计数不为0时则当前线程呈wait状态,也就是在屏障处等待。

实现等待与继续运行的效果分别需要使用await()和countDown()方法来进行。调用await()方法时判断计数是否为0,如果不为0则呈等待状态。其他线程可以调用count-Down()方法将计数减1,当计数减到为0时,呈等待的线程继续运行。

3.3 Excutor

没有灵魂的executor类图

接口Executor仅仅是一种规范,并没有实现任何的功能,所以大多数的情况下需要使用接口的实现类来完成指定的功能,比如ThreadPoolExecutor类就是Executor的实现类,但ThreadPoolExecutor在使用上并不是那么方便,在实例化时需要传入很多个参数,还要考虑线程的并发数等与线程池运行效率有关的参数,所以官方建议使用Executors工厂类来创建线程池对象
使用线程池实现线程的复用示例:
类MyRunnable.java代码如下:

        package myrunnable;
        class MyRunnable implements Runnable {
            private String username;
            public MyRunnable(String username) {
                super();
                this.username = username;
            }
            @Override
            void run() {
                try {
                    System.out.println(Thread.currentThread().getName() + " username="
                              + username + " begin " + System.currentTimeMillis());
                    Thread.sleep(2000);
                    System.out.println(Thread.currentThread().getName() + " username="
                              + username + "    end " + System.currentTimeMillis());
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }

运行类Run.java代码如下:

        package test.run;
        import java.util.concurrent.ExecutorService;
        import java.util.concurrent.Executors;
        import myrunnable.MyRunnable;
        Public class Run {
            Public static void main(String[] args) {
                ExecutorService executorService = Executors.newCachedThreadPool();
                for (int i = 0; i < 10; i++) {
                    executorService.execute(new MyRunnable(("" + (i + 1))));
                }
            }
        }

程序运行结果如下图所示。


线程池复用线程

3.4 Callable和Future

接口Callable与线程功能密不可分,但和Runnable的主要区别为:
1)Callable接口的call()方法可以有返回值,而Runnable接口的run()方法没有返回值。
2)Callable接口的call()方法可以声明抛出异常,而Runnable接口的run()方法不可以声明抛出异常。
执行完Callable接口中的任务后,返回值是通过Future接口进行获得的。但是有阻塞性。
future使用示例:

import java.util.concurrent.Callable;
    import java.util.concurrent.ExecutionException;
    import java.util.concurrent.ExecutorService;
    import java.util.concurrent.Future;
    import java.util.concurrent.LinkedBlockingDeque;
    import java.util.concurrent.ThreadPoolExecutor;
    import java.util.concurrent.TimeUnit;
    public class Test3 {
        public static void main(String[] args) {
            try {
                ExecutorService executor = new ThreadPoolExecutor(50,
                          Integer.MAX_VALUE, 5, TimeUnit.SECONDS,
                          new LinkedBlockingDeque<Runnable>());
                Future future = executor.submit(new Callable<String>() {
                    @Override
                    public String call() throws Exception {
                          Integer.parseInt("a");
                          return "我是返回值";
                    }
                });
                System.out.println(future.get());
            } catch (InterruptedException e) {
                e.printStackTrace();
            } catch (ExecutionException e) {
                e.printStackTrace();
                System.out.println("能捕获异常");
            }
        }
    }

3.5 ScheduledExecutorService

类ScheduledExecutorService的主要作用就是可以将定时任务与线程池功能结合使用。

3.6 集合

3.6.1 List

接口List最常用的非并发实现类就是ArrayList,它是非线程安全的,是有序的。
类ArrayList并不是线程安全的,如果想使用线程安全的链表则可以使用Vector类

3.6.2 Set

接口Set也是对Collection接口进行了扩展,它具有的默认特点是内容不允许重复,排序方式为自然排序,防止元素重复的原理是元素需要重写hashCode()和equals()方法。
接口Set最常用的不支持并发的实现类就是HashSet。HashSet默认以无序的方式组织元素,而LinkedHashSet类可以有序的组织元素。另一个实现类TreeSet,不仅实现了Set接口,还实现了SortedSet和NavigableSet接口,所以有序。

3.6.3 非阻塞队列
非阻塞队列的特色就是队列里面没有数据时,操作队列出现异常或返回null,不具有等待/阻塞的特色。和普通的容器使用方式相同。

常见的非阻塞队列有:
1)ConcurrentHashMap;
2)ConcurrentSkipListMap;
3)ConcurrentSkipListSet;(有序)
4)ConcurrentLinkedQueue;
5)ConcurrentLinkedDeque;
6)CopyOnWriteArrayList;(ArrayList升级版)
7)CopyOnWriteArraySet。(解决多线程环境下的HashSet不安全的问题)

ConcurrentLinkedQueue
poll():如果有数据时则移除表头,并将表头进行返回。
peek():如果有数据时则不移除表头,并将表头进行返回。

3.6.4 阻塞队列

如果BlockQueue空,从BlockingQueue取东西的操作将会被阻塞进入等待状态,直到BlockingQueue添加进了元素才会被唤醒。同样,如果BlockingQueue是满的,试图往队列中存放元素的操作也会被阻塞进入等待状态,直到BlockingQueue里有剩余空间才会被唤醒继续操作。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 220,295评论 6 512
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,928评论 3 396
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 166,682评论 0 357
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 59,209评论 1 295
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 68,237评论 6 397
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,965评论 1 308
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,586评论 3 420
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,487评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 46,016评论 1 319
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 38,136评论 3 340
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,271评论 1 352
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,948评论 5 347
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,619评论 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 32,139评论 0 23
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,252评论 1 272
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,598评论 3 375
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 45,267评论 2 358

推荐阅读更多精彩内容

  • 13.服务器图片改了,url没有变,需求,服务器的图片变了,就从服务器加载图片,如果服务器的没变,就从本地加载 1...
    AlanGe阅读 1,157评论 0 1
  • 计算机网络分层结构 两个系统中实体间的通信是一个很复杂的过程 ,为了降低协议设计和调试过程的复杂性,也为了便于对网...
    CodeKing2017阅读 2,736评论 0 4
  • OSI是一个开放性的通信系统互连参考模型,他是一个定义的非常好的协议规范。OSI模型有7层结构,每层都可以有几个子...
    一直在努力hard阅读 894评论 0 7
  • OSI七层网络模型与TCP/IP四层网络模型 引用自:http://www.cnblogs.com/music00...
    Reinelili阅读 1,889评论 0 2
  • 引言 先来讲讲我自身的感受,偶然看到知乎上一位刚入学的学生问如何学习计算机网络这门课,我毕业多年,依稀还能记得这门...
    OzanShareing阅读 1,634评论 0 3