一、概述
1.当多个线程并发操作同一个资源时,由于线程切换时机不确定,导致代码执行顺序出现了混乱,严重时可能会导致系统瘫痪。案例:两个人取豆子,需求取到0时,两个人都会报错不再继续取了。存在安全问题的写法:
if(beans==0){ throw new RuntimeException("没有豆子了!");} return beans--;//分析,当beans为1的时候,两人都过了throw这个代码,此时,两人陆续beans--,那么将进入到死循环当中啦。处理办法,加个synchronized,将函数变为同步函数,这时函数每次都只能用一个线程来处理了,也就是说由synchronized修饰的内容成为了一个整体,单个线程执行了这样一套内容完才会轮到另一个来处理。
2.安全问题我们用synchronized来处理的代价是效率低下。
3.有效的缩小同步范围可以保证并发安全的前提下提高并发效率,同步块可以更精确的锁定需要同步执行的代码片段。同步块的语法结构为:synchronized(同步监视器对象){需要同步的代码片段}。同步块可以更准确的指定需要同步执行的代码片段,但是需要注意的是多个线程之间若需要同步执行改段代码,用的同步监视器对象必须是同一个!
4.在方法上使用synchronized之后,同步监视器对象为当前方法所属对象,即:this。
5.Thread.yield(); 模拟线程发生切换
6.静态方法使用synchronized修饰后,该方法一定具有同步效果。不要说每个对象了,同一个类中对象调用这个方法都是同步的。
7.互斥锁,当synchronized修饰的是两段不同的代码,但是锁对象相同时,那么这两段代码就是互斥的(线程a有锁,此时代码ab都不能被其他线程执行,只有线程a将锁释放了其他线程才可执行这两处代码)
8.arrayList和linkedList都不是线程安全的。如何将给定的List集合转换为线程安全的,通过方法:list=Collections.synchronizedList(list);此时list就变成线程安全的了。
9.hashset不是线程安全的,也可将其转换为线程安全的,通过如下方法:set=Collections.synchronizedSet(set);注:通过list来快速创建set集合的方法,该参数list可以为collection的任何实例,但是这时所生成的set相对于参数list可能会丢失(重复的元素会去掉)。另外,hashmap也不是线程安全的,类似的转换为线程安全的方法为:map = Collections.synchronizedMap(map);
10.集合的元素操作方法不与迭代互斥(互斥的意思是,我在执行A的时候,你不能执行A也不能执行B)。所以若多线程操作时,并发迭代与增删元素间将出现安全问题,这可能导致迭代出错。所以需要自行维护互斥关系。
11.collection与collections的区别:collection是所有集合的顶级接口,collections是方便操作集合的工具类。
12.线程池的主要作用:1)控制线程数量;2)重用线程;
13.运行在服务端的serverSocket有两个主要的作用:1)申请服务端口,客户端就是通过该端口与服务端建立连接的;2)监听申请的服务端口,一旦有一个客户端通过该端口与服务端进行连接时,serverSocket就会主动创建一个Socket与该客户端通讯。
通过构造函数对serverSocket进行初始化时,指定要申请的服务端口,若该端口已经被其他程序占用,程序就会抛出异常。/监听端口,等待客户端连接,serverSocket的accept方法是一个阻塞方法,作用是监听服务端口,一旦有一个客户端通过该端口连接,就会返回一个socket实例,通过这个socket可以与刚刚连接我的客户端进行通讯。
14.socket封装了tcp通讯协议
实例化socket的过程就是连接远端计算机的过程,这里需要传入两个参数:参数1:服务端的ip地址信息;参数2:服务端的端口;通过ip可以找到服务端的计算机,通过端口可以连接到运行在服务端应用程序。
15.线程安全api与非线程安全api:stringbuffer、vector、hashtable是同步的,线程安全的;stringbuilder、arraylist、hashmap不是同步的,非线程安全的。
16.executorservice实现线程池
线程池的两个作用:控制线程数量;重用线程;(详述:当一个程序中创建了大量线程,并在任务结束后销毁,会给系统带来过度消耗资源,以及过度切换线程的危险,从而导致系统的奔溃)
线程池的概念:首先创建一些线程,他们的集合就称为线程池,当服务器接受到一个客户请求后,就从线程池中取出一个空闲的线程为之服务,服务完后不关闭改线程,而是将该线程还回到线程池中。
在线程池的编程模式下,任务是提交给整个线程池的,而不是直接交给某个线程,线程池在拿到任务后,它就在内部找有无空闲的线程,再把任务交给空闲的。一个线程在一个时间里只能执行一个任务,但是同一个时间里可以向线程池提交多个任务。
17.如何实现线程池:
executors.newCachesThreadPool();创建一个可根据需要创建新线程的线程池,但是以前构造的线程可用时将重用它们。有时间计,若太久没用就删掉线程。
executors.newFixedThreadPool(int nThreads);创建一个可重用固定线程集合的线程池,以共享的无届队列方式来运行这些线程。固定大小线程池,需设置线程数量(懒汉模式:一开始没有线程,来任务就创建,直到固定大小就停止了;饿汉模式:一开始就创建所有固定大小的线程)
ThreadPool.execute(runn);交给线程池,线程池中的线程都是前台线程,正常情况下这些任务执行完了,可是线程还在啊,程序就还没有结束。可以手动停止程序,方法一二;ThreadPool.shutdown();把所有任务都执行完了就停程序(不能放在for循环里,只执行一遍就行);ThreadPool.shutdownNow();这个方法是无论任务是否结束,执行到这个方法就一定会把这个程序停掉。
Executors.newScheduledThreadPool(int corePoolSize);创建一个线程池,它可安排在给定延迟后运行命令或者定期的执行。设置任务延时时间,延时时间后再创建。
Executors.newSingleThreadExecutor();创建一个使用单个worker线程的Executor,以无界队列方式来运行该线程。单线程模式,无论多少任务都是我一个人来做。
ExecutorService是java提供的用于管理线程池的类。
18.blokingQueue是双缓冲队列。在多线程并发时,若需要使用队列,我们可以使用Queue,但是要解决一个问题就是同步,但同步操作会降低并发对Queue操作的效率。BlockingQueue内部使用两条队列,可允许两个线程同时向队列一个做存储,一个取出操作。在保证并发安全的同时提高了队列的存取效率。线程安全,并发效率高。
19.socket称为“套接字”,用于描述ip地址和端口,是一个通信链的句柄。在internet上的主机一般运行了多个服务软件,同时提供几种服务。每种服务都打开一个socket,并绑定到一个端口上,不同的端口对应与不同的服务。应用服务通常通过“套接字”向网络发出请求或者应答网络请求。socket和serversocket类位于java.net包中。serversocket用于服务端,socket是建立网络连接时使用的,在连接成功时,应用服务两端都会产生一个socket实例,操作这个实例,完成所需的会话。
19.server端serversocket监听:serversocket是运行于服务端应用程序中,通常创建serversocket需要指定服务端口号,之后监听socket的连接:ServerSocket server = new ServerSocket(8088);//创建该对象并申请一下服务端口 Socket socket = server.accept();//这里会停一下,直到在某个客户端的socket连接到了返回请求连接的socket,并往下继续执行。
20.Client端Socket连接:先服务端上边两行代码写好,在等着客户端了,我们就可以在客户端应用程序中创建socket来向服务端发起请求连接,它发起连接的方式比较直接,就是在创建时就开始了,连接不成功的时候再抛出异常。
21.socket封装了tcp通讯协议
22.服务端初始化出来,指定要申请的服务端口,若该端口已经被其他的应用程序所占用就会抛出异常(别人占了或自己占了没有释放)。
accept()阻塞方法,程序卡住,直到客户端连上我时,这时返回一个socket实例(相当于服务端是被动接上电话的)。
服务端:Socket[addr=localhost/127.0.0.1,port=41357,localport=8088];
客户端:Socket[addr=localhost/127.0.0.1,port=8088,localport=41357]
二、笔记
1.安全问题。runtimeException这种,没有try/catch,抛到run之后,该线程就会报错退出了。同步函数:使得只有一个线程在做另一个被关在门外,只有它出来了另一个才能做。(默认的同步监视器对象为this)。同步代码块:相比同步函数而言,它会比较灵活,它包的不是整个函数,而是函数中某些需要包的代码。同步代码块中的小括号里是同一个对象(同步监视器对象)才行,在方法中用this,那么这个this一定是这个方法所在类的一个实例。当这里用this,但是多线程采用了不同的对象来调用这个方法咋办?怎样才能用同一个锁呢?你可以在这个对象所表类加个静态成员变量Object,反正不会变就行。相似的有static同步函数,也是仅跟类有关了。
2.同步锁:是厕所排队问题。互斥锁:是不同地点的事情(当线程A做a时,B线程不能做ab)。
3.List与Set的区别:List可以重复,有序(一定是有序的,可通过下标来取值);Set不可重复,无序(根本区别还是说是否可以重复)
4.多线程访问一个集合的时候,不能用非线程安全的:HashSet/ArrayList/LinkedList/HashMap(其中HashSet和HashMap的关系为HashSet其实就是HashMap的key部分)
5.Collections工具类,各种方法:Collections.sort(排序);Collections有个方法可以将不安全集合转为安全的。list = Collections.syncheonizedList(list);//返回的为Collections内部实现类,线程安全的list。两个线程,一个线程在做list遍历时,另一个线程在做这个list的增删是不行的,没有出现互斥是有问题的。
6.线程池:重用线程(任务小,但频繁创建销毁,考虑重用线程);控制线程数量(任务多的时候,大量创建时,考虑控制数量);高并发的危害是宕机
7.只需要创任务给线程池就行,有空闲线程就用线程,没有空闲线程就等着。
8.一开始接触的都是内存中的知识;接着学了IO流,走出了内存走进了硬盘;现在运用网络通讯,可以走去别人的电脑访问了
9.网络通讯:socket
TCP协议:可靠传输协议(数据完整,但是带宽大);UDP协议:不可靠传输协议(不管你收没收到,都不管,不再给你发了);
传输都是拆成字节包发的,因为整个发的话容易丢包,而字节包丢一部分重写发就行了。现在的通讯UDP为主导。C-S表示客户端对服务端(表两个应用程序)。
10.socket做电话看。麦克风,为outStream;听筒,为inStream;做客户端时,创建socket对象,需要传入两个参数:host(IP地址,自己的IP地址可以用localhost或127.0.0.1表示),端口号(是服务端的应用程序的一个引用吧,不是像USB接口这样的可见的而是虚拟的概念,相对于IP地址端口号类似于分机的概念,端口号是服务器这边决定的,在一个IP地址中端口号是唯一的,只能取0-65535之间的数,主流端口号都是一万以下的。但是呢,像windows系统它内部自带的应用程序分配了3000以下的端口号,所以一般我们用5千以上这样四位数的端口号自己设置用用)
11.什么时候捕获异常,什么时候抛出异常。(你调用我的时候会失败,我就抛异常给你看)