为什么使用多线程
1.减少系统的响应时间,因为如果是单线程的话,如果某个线程非常耗时,或者等待时间非常长的话,不会响应鼠标和键盘的,使用多线程后,将耗时的线程分配到一个单独线程去,使代码有更好的交互性。
2.更好的利用计算机的资源,因为计算机现在都是多核cpu,使用单线程的话不能更好的利用计算机资源
使用多线程应该注意什么
进程与线程的区别?
进程:操作系统中程序的一次执行周期,资源分配的最小单元
线程:进程中的一个子任务,任务分配的最小单位(一次发收信息就是两个线程)cpu调度的最小单元
线程依赖于进程,有进程才有线程
创建和销毁线程小的多
线程间通信比进程简单的多
同一个进程的线程共享整个进程的资源(寄存器,堆栈,上下文)
延伸:线程间的通信是啥,进程间的通信是啥?
进程间通信方式
进程间通信方式有很多,网上一说有十几种。面试的时候说上以下几种差不多:
①管道:半双工的通信方式,数据只能单向流动,且只能在有亲缘关系(父子进程或兄弟进程)的进程间使用;
②命名管道:FIFO,半双工的通信方式,但允许在无亲缘关系的进程间通信;
③消息队列:消息的链表,存放在内核中,并由消息队列标识符标识。消息队列克服了信号传递信息少、管道只能承载无格式字节流以及缓冲区大小受限等缺点;
④信号量:是一个计数器,用于控制多个进程间对共享资源的访问;
⑤共享内存:映射一段能被其他进程访问的内存,这段内存由一个进程创建,但多个进程都可以访问;
⑥套接字
线程间通信方式
多个线程在处理同一个资源,并且任务不同时,需要线程通信来帮助解决线程之间对同一个变量的使用或操作。就是多个线程在操作同一份数据时, 避免对同一共享变量的争夺。
①锁机制:包括互斥锁、条件变量、读写锁
互斥锁提供了以排他方式防止数据结构被并发修改的方法。
读写锁允许多个线程同时读共享数据,而对写操作是互斥的。
条件变量可以以原子的方式阻塞进程,直到某个特定条件为真为止。对条件的测试是在互斥锁的保护下进行的。条件变量始终与互斥锁一起使用。
②信号量机制(Semaphore):包括无名线程信号量和命名线程信号量
③信号机制(Signal):类似进程间的信号处理
线程间的通信目的主要是用于线程同步,所以线程没有像进程通信中的用于数据交换的通信机制
2.多线程的创建方式
继承Thread,实现Runnable、Callable、线程池
3.多线程常用操作方法
3.1sleep():线程休眠,当前线程立即交出cpu,但不会交出锁
运行–》阻塞态
3.2yield():线程让步,交出cpu由系统调度,不会释放对象锁。只会让拥有相同优先级的相同优先级的线程获取到cpu的机会
运行–》就绪态
3.3join():当前线程等待别的线程执行完毕在恢复执行。会释放锁。(对wait()方法的包装)
运行—》堵塞态
3.4interrupt():将线程状态置为中断状态。
a.当线程中有阻塞式(sleep join wait)时,调用interrupt会抛出中断异常
b.当没有这些方法,只是将isInterrupt()置ture
3.5wait/notify:线程等待和唤醒,会释放对象锁
必须在同步代码块与同步方法使用(synchronized)
3.6守护线程:也就是后台线程
生命周期:当最后一个用户线程消失的时候,一起消失
GC线程:垃圾回收线程
4.五种状态:新建,就绪,运行,阻塞,停止
5.线程的状态,wait和block的区别?
wait 是处于就绪状态,处在等待队列中(等待执行)
block 是处于阻塞状态,处在同步队列中(是好几个进程同时获取的时候,一个进去了,剩下的几个进入阻塞状态,就如同步队列中)
阻塞:当一个线程试图获取一个内部的对象锁(非java.util.concurrent库中的锁),而该锁被其他线程持有,则该线程进入阻塞状态。
等待:当一个线程等待另一个线程通知调度器一个条件时,该线程进入等待状态。例如调用:Object.wait()、Thread.join()以及等待Lock或Condition。
6、同步、异步、阻塞、非阻塞各代表什么意思?
B :阻塞 N:同步非阻塞 A:异步
同步:多个对象,必须按照顺序执行
异步:多个对象,各自执行
阻塞:遇到障碍,不能行动
非阻塞:畅通无阻
同步阻塞,相当于一个线程在等待。
同步非阻塞,相当于一个线程在正常运行。
异步阻塞,相当于多个线程都在等待。
异步非阻塞,相当于多个线程都在正常运行
Thread和Runnable区别
在这就是可以避免Java中的单继承的限制,增加程序的健壮性,代码可以被多个线程共享,代码和数据独立。线程池只能放入实现Runnable 类线程,不能直接放入继承Thread的类
僵尸进程和孤儿进程是什么,分别有什么危害(百度面试题)
一、概念
孤儿进程:
一个父进程退出,而它的一个或多个子进程还在运行,那么那些子进程将成为孤儿进程。孤儿进程将被init进程(进程号为1)所收养,并由init进程对它们完成状态收集工作。
僵尸进程:
一个进程使用fork创建子进程,如果子进程退出,而父进程并没有调用wait或waitpid获取子进程的状态信息,那么子进程的进程描述符仍然保存在系统中。这种进程称之为僵死进程。
二、危害
孤儿进程:
孤儿进程是没有父进程的进程,孤儿进程这个重任就落到了init进程身上,init进程就好像是一个民政局,专门负责处理孤儿进程的善后工作。每当出现一个孤儿进程的时候,内核就把孤 儿进程的父进程设置为init,而init进程会循环地wait()它的已经退出的子进程。这样,当一个孤儿进程凄凉地结束了其生命周期的时候,init进程就会代表党和政府出面处理它的一切善后工作。因此孤儿进程并不会有什么危害。
僵尸进程:
父进程还在运行但是子进程挂了,但是父进程却没有使用wait来清理子进程的进程信息,导致子进程虽然运行实体已经消失,但是仍然在内核的进程表中占据一条记录,这样长期下去对于系统资源是一个浪费。
三、僵尸进程处理
1、通过信号机制
子进程退出时向父进程发送SIGCHILD信号,父进程处理SIGCHILD信号。调用wait()或者waitpid(),让父进程阻塞等待僵尸进程的出现,处理完在继续运行父进程。
2、杀死父进程
当父进程陷入死循环等无法处理僵尸进程时,强制杀死父进程,那么它的子进程,即僵尸进程会变成孤儿进程,由系统来回收。
3、重启系统
当系统重启时,所有进程在系统关闭时被停止,包括僵尸进程,开启时init进程会重新加载其他进程。
7.线程池了解不,用的是哪一个线程池,为什么要选择这个线程池复习.note
8Java线程池实现原理、常用的线程池有哪几种,参数怎么设置?Executor框架介绍一下?
讲一下线程池:先讲线程池优点,然后再讲线程池的执行流程。
A.开发中使用线程池的三个优点如下:
B.线程池的实现原理,工作原理
C:Executor框架介绍一下?
在上层,Java多线程程序通常把应用分解为若干个任务,然后使用用户级的调度器(Executor框架)将这些任务映
射为固定数量的线程;在底层,操作系统内核将这些线程映射到硬件处理器上。这种两级调度模型的示意图如下图
所示
D: 线程池的选择
Executors.newFixedThreadPool();被称为可重用固定线程数的线程池。适用于满足资源管理的需求,而需要限制当前线程数量的应用场合,适用于负载比较重的服务器(我用的这个)
Executors.newCachedThreadPool();是一个会根据需要创建新线程的线程池。是大小无界的线程池,适用于执行很多的短期异步任务的小程序,或者负载较轻的服务器。
缺点:会一直创建线程,将内存写满,造成服务器崩溃
Executors.newScheduledThreadPool();它主要用来在给定的延迟之后运行任务,或者定期执行任务
Executors.newSingleThreadExecutor();使用单个工作线程的线程池,适用于需要保证顺序地执行各个任务;并且在任意时间点,不会有多个线程来同时工作的场景
9.怎么保证多线程操作的原子性?(保证线程安全,原子性,可见性,有序性)
原子性:一个或者一组元素要么同时发生,要么都不发生。
可见性:一个线程的修改对于其他线程是可知的
有序性:谁前谁先执行
如何保证原子性:使用synchronized的同步方法,或者使用Java提供的原子类:java的concurrent包下提供了一些原子类,我们可以通过阅读API来了解这些原子类的用法。比如:AtomicInteger、AtomicLong、AtomicReference等
而通过Volatile关键字,并不能解决非原子操作的线程安全性。
10.问了乐观锁,悲观锁及其实现?
悲观锁:假设每一次访问临界区都会产生冲突
乐观锁:假设每一次访问临界区都不会产生冲突,如果产生冲突,无锁操作就是使用CAS操作
https://www.cnblogs.com/qjjazry/p/6581568.html
1.2.2 处理死锁的几个常见的方法
预防死锁:
破坏死锁的四个必要条件中的一个或多个来预防死锁。
避免死锁:
和预防死锁的区别就是,在资源动态分配过程中,用某种方式防止系统进入不安全的状态。
1、加锁顺序(线程按照一定的顺序加锁)因为当多个线程需要相同的一些锁,但是按照不同的顺序加锁,死锁就很容易发生。
2、加锁时限(线程尝试获取锁的时候加上一定的时限,超过时限则放弃对该锁的请求,并释放自己占有的锁)
检测死锁:
运行时出现死锁,能及时发现死锁,把程序解脱出来
解除死锁:
发生死锁后,解脱进程,通常撤销进程,回收资源,再分配给正处于阻塞状态的进程。
11.什么是类加载器?
把类加载阶段中的"通过一个类的全限定名来获取描述此类的二进制字节流"这个动作放在Java虚拟机外部去实现,以便让应用程序自己决定如何去获取所需要的类。实现这个动作的代码模块称之为"类加载器".
12.什么是双亲委任模型?
当一个类加载器收到类加载请求的时候,他不会直接加载,而是交给他的父类去加载请求,他的父类再去寻找他的父类,直到最顶层Bootstrap。当父类处理不了请求的时候,子加载器才会尝试自己去加载。
双亲委派模式对于保证Java程序的稳定运行很重要。有一个显而易见的好处就是Java类随着它的类加载器一起具备
了一种带有优先级的层次关系。例如java.lang.Object类,它存放在rt.jar中,无论哪一个类加载器要加载这个类,
最终都是委派给处于顶端的启动类加载器进行加载。因此,Object类在程序的各种类加载器环境中都是同一个类
什么是数据源(dataSource)
JDBC2.0 提供了javax.sql.DataSource接口,它负责建立与数据库的连接,当在应用程序中访问数据库时
不必编写连接数据库的代码,直接引用DataSource获取数据库的连接对象即可。用于获取操作数据Connection对象。
数据源与数据连接池
数据源建立多个数据库连接,这些数据库连接会保存在数据库连接池中,当需要访问数据库时,只需要从数据库连接池中
获取空闲的数据库连接,当程序访问数据库结束时,数据库连接会放回数据库连接池中。
比传统的连接优势在哪里?
传统的JDBC访问数据库技术,每次访问数据库都需要通过数据库驱动器Driver和数据库名称以及密码等等资源建立数据库连接。
这样的连接存在两大问题:
什么是Sql注入?
就是通过吧Sql语句插入到web表单,或者url地址中,以此来欺骗服务器执行恶意的Sql语句。
如何预防:
1前端不行,前端即使判断了,也有可能绕过去
(1)检查用户输入的合法性;
(2)将用户的登录名、密码等数据加密保存。
(3)预处理SQL。
(4)使用存储过程实现查询,虽然不推荐,但也是一个方法
预编译可以干什么?
所谓预处理就是提前处理了一下,就是在程序执行之前处理了,这样会提高效率。也能预防sql注入。
为什么能预防sql注入呢?
因为sql语句在程序运行之前已经被数据库分析,编译,优化了,即使出现or 1=1-,他也只会把他当成一个属性来处理,而不是一个sql语句片段。
内连接和外连接:
数据模型的三要素:数据结构,数据操作,数据的完整性
栈和堆的区别
jdbc流程:
准备驱动 :
Class.forName(“com.mysql.jdbc.Driver”);
建立连接:
Connection connection = DriverManager.getConnection(“jdbc:mysql://localhost:3306/memo?
user=root&password=root&useUnicode=true&characterEncoding=UTF-8”);
创建操作命令:
Statement statement = connection.createStatement();
执行sql语句:
ResultSet resultSet= statement.executeQuery(
“select id,group_id,title,content,is_protected,
background,is_remind,remind_time,created_time,modify_time from memo_info”);
处理结果集:
while (resultSet.next()) {
int id = resultSet.getInt(“id”);
String title = resultSet.getString(“title”);
String content = resultSet.getString(“content”);
Date createTime = resultSet.getDate(“created_time”);
System.out.println(String.format(“Memo: id=%d, title=%s, content=%s,
createTime=%s”, id, title, content, createTime.toString()));
}
关闭各种命令
Statement和PreparedStatement和CallableStatement的区别?
Statement:用于执行不带参数的简单sql语句
PreparedStatement:用于执行带参数或者不带参数的sql语句
sql语句会预编译在数据库中,效率比较快
CallableStatement:用于执行数据库存储过程
Socket:
套接字,可以用来实现不同虚拟机和计算机的访问