Java第三阶段学习(七、线程池、多线程)
一、线程池
1.概念:
线程池,其实就是一个容纳多个线程的容器,其中的线程可以重复使用,省去了频繁创建线程对象的过程,无需反复创建线程而消耗过多资源,是JDK1.5以后出现的。

2.使用线程池的方式----Runnable接口
线程池是由线程池工厂创建的,再调用线程池中的方法调用线程,再通过线程去执行任务方法。
构造代码:2.1 Executors:线程池创建工厂类
2.2 ExecutorService:线程池类 Future<?> submit(Runnable task):获取线程池中的某一个线程对象,并执行
2.3 Future接口:用来记录线程任务执行完毕后产生的结果。线程池创建与使用
创建线程池的步骤:
1.创建线程池对象
2.创建Runnable接口子类对象
3.提交Runnable接口子类对象
4.关闭线程池
Runnable接口实现类:
package com.oracle.Demo01;
public class MyRunnable implements Runnable{
//Runnable接口实现类,并且有线程任务
@Override
public void run() {
for(int i=0;i<100;i++){
System.out.println(Thread.currentThread().getName()+":"+i);
}
}
}
线程池代码:
package com.oracle.Demo01; import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
//Executor创建线程池方法
public class Demo01 { public static void main(String[] args) {
// 1.通过线程池工厂 获得线程池对象
ExecutorService es=Executors.newFixedThreadPool(5);
//2.获得线程对象并提交
es.submit(new MyRunnable());
es.submit(new MyRunnable());
//销毁线程池:执行完毕后,销毁线程池,线程也就进入死亡状态了
//若不销毁,线程运行完毕又回到线程池,回到新建状态,没有进入死亡状态
es.shutdown();
} }
3.使用线程池方法-----Callable接口
构造代码:3.1 Callable接口:与Runnable接口功能相似,用来指定线程的任务。其中的call()方法,用来返回线程任务执行完毕后的结果,call方法可抛出异常。
3.2 ExecutorService:线程池类
<T> Future<T> submit(Callable<T> task):获取线程池中的某一个线程对象,并执行线程中的call()方法
3.3 Future接口:用来记录线程任务执行完毕后产生的结果。线程池创建与使用
使用线程池中线程的步骤:
1.创建线程池对象
2.创建Callable接口子类对象
3.提交Callable接口子类对象
4.摧毁线程池
Callable接口实现类代码:
package com.oracle.Demo01; import java.util.concurrent.Callable;
//创建Callable接口的子类,并且有线程任务
public class MyCcallable implements Callable<String>{ @Override
public String call() throws Exception {
System.out.println("Call方法");
return "abc";
} }
线程池运行代码:
package com.oracle.Demo01; import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
//使用Callable方法使用线程池的线程
public class Demo02 { public static void main(String[] args) throws InterruptedException, ExecutionException {
// TODO Auto-generated method stub
ExecutorService es=Executors.newFixedThreadPool(3);
Future<String> f=es.submit(new MyCcallable());
String s=f.get();
System.out.println(s);
} }
二、多线程
1.线程安全:
如果有多个线程在同时运行,而这些线程可能会同时运行这段代码。程序每次运行结果和单线程运行的结果是一样的,而且其他的变量的值也和预期的是一样的,就是线程安全的。
线程安全问题都是由全局变量及静态变量引起的。若每个线程中对全局变量、静态变量只有读操作,而无写操作,一般来说,这个全局变量是线程安全的;若有多个线程同时执行写操作,一般都需要考虑线程同步,否则的话就可能影响线程安全。
比如电脑端,手机端,跟售票窗口同时卖一张票,因为是多线程所以是同时进行的,当一个线程买到票后,可能另外两个线程也进行到一半了,但是因为已经没有票了,所以就会出现异常错误,比如重复的票号或异常的票号,这就是出现了线程安全问题。
2.线程同步:
Java中提供了线程同步机制,来解决线程安全的问题。
线程同步的方式有两种:
1.同步代码块
2.同步方法
2.1同步代码块:
在代码块声明上,加上synchronized
synchronized (锁对象) {
可能会产生线程安全问题的代码
}
同步代码块中的锁对象可以是任意的对象;但多个线程时,要使用同一个锁对象才能够保证线程安全。
多线程代码:
package com.oracle.Demo02;
public class Demo {
public static void main(String[] args) {
Tickets t=new Tickets();
Thread t0=new Thread(t);
Thread t1=new Thread(t);
Thread t2=new Thread(t);
t0.start();
t1.start();
t2.start();
}
}
使用同步代码块:
package com.oracle.Demo02;
//卖票任务
//synchronized(任意对象){
// 线程要操作的共享数据
//}
//同步代码块解决线程不安全的原理:
// 线程遇到同步代码块时,线程会先判断你的同步锁有没有,如果有,就获取同步锁,进入同步去执行代码,执行完毕后,
//再把锁还回去。
// 在同步中,线程进行了休眠,此时另一个线程会执行,遇到同步代码块的时候,会判断同步锁有没有,如果没有,
//没有获取到同步锁的线程,不能进入同步去执行,被阻挡在同步代码块外面
public class Tickets implements Runnable{
private int ticket=100;
//private Object obj=new Object();
public void run() {
while(true){
synchronized (this) { //这里的this 可以为任意对象,替换成上面的obj也可以,为自身对象this也可以
if(ticket>0){
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"出售第"+(ticket--)+"张票");
}
}
} } }
2.2 同步方法
在方法声明上,加上synchronized
public synchronized void method(){
可能会产生线程安全问题的代码
}
同步方法中的锁对象是 this
多线程代码:
package com.oracle.Demo03;
public class Demo {
public static void main(String[] args) {
Tickets t=new Tickets();
Thread t0=new Thread(t);
Thread t1=new Thread(t);
Thread t2=new Thread(t);
t0.start();
t1.start();
t2.start();
}
}
使用同步代码块:
package com.oracle.Demo03;
public class Tickets implements Runnable{
private int ticket=100;
public void run() {
while(true){
method();
}
}
//同步方法:解决线程不安全问题
//问题1:同步方法有锁吗?有,锁是本类引用,this关键字
//问题2:如果你的同步方法是静态的,还有同步锁吗?是this吗?
//有,不是this,是本类自己,Tickets.class
public synchronized void method(){
if(ticket>0){
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"出售第"+(ticket--)+"张票");
}else{
return;
}
}
}
静态同步方法: 在方法声明上加上static synchronized
public static synchronized void method(){
可能会产生线程安全问题的代码
}
使用同步方法:
package com.oracle.Demo03;
public class Tickets implements Runnable{
private int ticket=100;
public void run() {
while(true){
method();
}
}
//同步方法:解决线程不安全问题
//问题1:同步方法有锁吗?有,锁是本类引用,this关键字
//问题2:如果你的同步方法是静态的,还有同步锁吗?是this吗?
//有,不是this,是本类自己,Tickets.class
public synchronized void method(){
if(ticket>0){
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"出售第"+(ticket--)+"张票");
}else{
return;
}
}
}

2.3死锁
同步锁使用的弊端:当线程任务中出现了多个同步锁(多个锁)时,如果同步中嵌套了其他的同步。这时容易引发一种现象:程序出现无限等待,这种现象我们称为死锁。
synchronzied(A锁){
synchronized(B锁){
}
}
所以,不能在一个同步中再嵌套另一个同步

2.4 Lock接口
Lock 实现类提供了比使用 synchronized 方法和语句可获得的更广泛的锁定操作。
常用方法:

Lock提供了一个更加面对对象的锁,在该锁中提供了更多的操作锁的功能。
package com.oracle.Demo04; import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
//Lock接口
public class Tickets implements Runnable{
private int ticket=100;
private Lock lock=new ReentrantLock(); //创建一个锁的对象
public void run() {
while(true){
lock.lock(); //提供锁
if(ticket>0){
try {
Thread.sleep(10);
System.out.println(Thread.currentThread().getName()+"出售第"+(ticket--)+"张票");
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}finally{
lock.unlock(); //执行完毕释放锁
}
}
} } }
2.5等待唤醒机制
线程之间的通信:多个线程在处理同一个资源,但是处理的动作(线程的任务)却不相同。通过一定的手段使各个线程能有效的利用资源。而这种手段即—— 等待唤醒机制。
等待唤醒机制的方法:
wait():等待。将正在执行的线程释放其执行资格 和 执行权,并存储到线程池中。
notify():唤醒。唤醒线程池中被wait()的线程,一次只能唤醒一个,而且是随机的任意线程。
notifyAll():唤醒全部。可以将线程池中所有被wait()的线程唤醒。
所谓唤醒的意思就是让线程池中的线程具备执行资格。必须注意的是,这些方法都是在 同步中才有效。同时这些方法在使用时必须标明所属锁,
这样才可以明确出这些方法操作的到底是哪个锁上的线程。

等待唤醒示例图:

自定义实体类:
package com.oracle.Demo06;
//等待唤醒机制 自定义实体类
public class Resource {
public String name;
public String sex;
public boolean flag=false;
}
线程类:
package com.oracle.Demo06;
public class Input implements Runnable{
private Resource r;
public Input(Resource r){
this.r=r;
}
public void run() {
int i=0;
while(true){
synchronized(r){
if(r.flag){
try {
r.wait();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
if(i%2==0){
r.name="张三";
r.sex="男";
}else{
r.name="lisi";
r.sex="nv";
}
i++;
}
r.flag=true;
r.notify();
}
}
}
}
package com.oracle.Demo06;
public class Output implements Runnable{
private Resource r;
public Output(Resource r){
this.r=r;
}
//flag:当flag为true的时候,代表赋值完成
//为false,获取值完成
public void run() {
while(true){
synchronized (r) {
if(!r.flag){
try {
r.wait();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
System.out.println(r.name+"..."+r.sex);
r.flag=false;
r.notify();
}
}
}
}
测试类:
package com.oracle.Demo06;
public class Output implements Runnable{
private Resource r;
public Output(Resource r){
this.r=r;
}
//flag:当flag为true的时候,代表赋值完成
//为false,获取值完成
public void run() {
while(true){
synchronized (r) {
if(!r.flag){
try {
r.wait();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
System.out.println(r.name+"..."+r.sex);
r.flag=false;
r.notify();
}
}
}
}
Java第三阶段学习(七、线程池、多线程)的更多相关文章
- Java第三阶段学习(六、多线程)
一.进程和线程的区别: 进程:指正在运行的程序,当一个程序进入内存运行,就变成一个进程. 线程:线程是进程的一个执行单元. 总结:一个程序运行后至少会有一个进程,一个进程可以有多个线程. 多线程:多线 ...
- JAVA多线程学习七-线程池
为什么用线程池 1.创建/销毁线程伴随着系统开销,过于频繁的创建/销毁线程,会很大程度上影响处理效率 例如: 记创建线程消耗时间T1,执行任务消耗时间T2,销毁线程消耗时间T3 如果T1+T3> ...
- Java第三阶段学习(十、XML学习)
一.XML学习 1.模拟Servlet执行 在学习完前端及java与数据库后,将进行WEB编程阶段的学习.在WEB编程中,可以通过浏览器访问WEB服务器上的数据.这时WEB服务器就相当于另一台计算机. ...
- Java第三阶段学习(九、类加载器、反射)
一.类加载器 1.类的加载: 当程序要使用某个类时,如果该类还未被加载到内存中,则系统会通过加载,连接,初始化三步来实现对这个类进行初始化. 1.1 加载: 就是指将class文件读入内存,并为之自动 ...
- Java第三阶段学习(八:网络通信协议、UDP与TCP协议)
一.网络通信协议 1.概念: 通过计算机网络可以使多台计算机实现连接,位于同一个网络中的计算机在进行连接和通信时需要遵守一定的规则,在计算机网络中,这些连接和通信的规则被称为网络通信协议,它对数据的传 ...
- Java第三阶段学习(三、字符流、转换流)
一.字节流读取中文时出现的问题: 文件中有中文时,用字节流读取会出现乱码的问题,因为一个中文为两个字节. 二.字符编码表 编码表:其实就是生活中字符和计算机二进制的对应关系表. 1.ascii: 一个 ...
- Java第三阶段学习(十四、JSP动态页面、EL表达式、JSTL标签库)
一.JSP技术 1.jsp脚本和注释 jap脚本: 1)<%java代码%> ----- 内部的java代码翻译到service方法的内部,比如写在doget.dopost 内的代码 2) ...
- Java第三阶段学习(十三、会话技术、Cookie技术与Session技术)
一.会话技术 1. 存储客户端状态 会话技术是帮助服务器记住客户端状态(区分客户端)的. 2. 会话技术 从打开一个浏览器访问某个站点,到关闭这个浏览器的整个过程,称为一次会话.会话技术就是记录这 ...
- Java第三阶段学习(十二、HttpServletRequest与HttpServletResponse)
一.HttpServletRequest 1.概述: 我们在创建Servlet时会覆盖service()方法,或doGet()/doPost(),这些方法都有两个参数,一个为代表请求的request和 ...
随机推荐
- Webpack2 中的 NamedModulesPlugin 与 HashedModuleIdsPlugin
要讨论Webpack 2中新增的这两个plugin的功能,还要先从使用Webpack打包的项目的前端资源缓存方案说起. 通常在使用了Webpack的项目中我们会使用CommonsChunkPlugin ...
- csrfguard3.1 部署笔记
1:git clone 导入csrfguard 2:点击菜单栏View->Tool Windows->Maven projects 3:Lifecycle clean build 4:t ...
- Fiddler功能介绍
1.对话框:添加备注,添加完了会在控制面板中的comments显示2.Replay:选中会话后点击,会重新发送请求3.Go:是打断点后,想要继续执行,就点击GO 4.Stream:模式切换. 默认是缓 ...
- Ubuntu18 输入法fcitx安装
默认iBus非常难用 1.安装fcitx终端输入fcitx提示程序尚未安装.使用命令安装sudo apt-get install fcitx-bin相关的依赖库和框架都会自动安装上.2.安装输入法su ...
- web@css普通布局 , 高级布局 , 布局坑
1.高级布局<文档流概念>:页面从上至下,块式标签一行一行排列,内联式一行中从左至右排列<BFC规则>:左右位置(左右margin)垂直位置(上下margin)容器内外(互不影 ...
- LabVIEW将字符串转化为十进制
(1)作用:将ASCII当做成十六进制来表示,并计算这个十六进制数对应的十进制大小 例如:008A本身对应的ASCII码用十六进制表示为为30 30 38 41,但有些协议中将008A当成0x008A ...
- 数据库之删除表数据drop、truncate和delete的用法
数据库中删除表数据的关键字,最常用的可能就是delete了,另外其实还有drop和truncate两个关键字. 老大:drop 命令格式:drop table tb ---tb表示数据表的名字,下 ...
- [PHP]session的一些要点
一.session_start([array $options=array()]) 1.只能在输出http头前启动此函数,因为如果需要改写sessid的键和值,需要在http报文头发出前就开始定义了: ...
- python时间日期格式化和反格式化
strftime()和strptime()行为 date,datetime和time对象都支持一种 strftime(format)方法,以创建一个表示显式格式字符串控制下的时间的字符串.从广义上讲, ...
- 图解Metrics, tracing, and logging
Logging,Metrics 和 Tracing 最近在看Gophercon大会PPT的时候无意中看到了关于Metrics,Tracing和Logging相关的一篇文章,凑巧这些我基本都接触过, ...