一、线程池

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接口实现类:

  1. package com.oracle.Demo01;
  2.  
  3. public class MyRunnable implements Runnable{
  4. //Runnable接口实现类,并且有线程任务
  5. @Override
  6. public void run() {
  7. for(int i=0;i<100;i++){
  8. System.out.println(Thread.currentThread().getName()+":"+i);
  9. }
  10.  
  11. }
  12.  
  13. }

线程池代码:

  1. package com.oracle.Demo01;
  2.  
  3. import java.util.concurrent.ExecutorService;
  4. import java.util.concurrent.Executors;
  5. //Executor创建线程池方法
  6. public class Demo01 {
  7.  
  8. public static void main(String[] args) {
  9. // 1.通过线程池工厂 获得线程池对象
  10. ExecutorService es=Executors.newFixedThreadPool(5);
  11. //2.获得线程对象并提交
  12. es.submit(new MyRunnable());
  13. es.submit(new MyRunnable());
  14. //销毁线程池:执行完毕后,销毁线程池,线程也就进入死亡状态了
  15. //若不销毁,线程运行完毕又回到线程池,回到新建状态,没有进入死亡状态
  16. es.shutdown();
  17. }
  18.  
  19. }

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接口实现类代码:

  1. package com.oracle.Demo01;
  2.  
  3. import java.util.concurrent.Callable;
  4. //创建Callable接口的子类,并且有线程任务
  5. public class MyCcallable implements Callable<String>{
  6.  
  7. @Override
  8. public String call() throws Exception {
  9. System.out.println("Call方法");
  10. return "abc";
  11. }
  12.  
  13. }

线程池运行代码:

  1. package com.oracle.Demo01;
  2.  
  3. import java.util.concurrent.ExecutionException;
  4. import java.util.concurrent.ExecutorService;
  5. import java.util.concurrent.Executors;
  6. import java.util.concurrent.Future;
  7. //使用Callable方法使用线程池的线程
  8. public class Demo02 {
  9.  
  10. public static void main(String[] args) throws InterruptedException, ExecutionException {
  11. // TODO Auto-generated method stub
  12. ExecutorService es=Executors.newFixedThreadPool(3);
  13. Future<String> f=es.submit(new MyCcallable());
  14. String s=f.get();
  15. System.out.println(s);
  16. }
  17.  
  18. }

二、多线程

1.线程安全:

如果有多个线程在同时运行,而这些线程可能会同时运行这段代码。程序每次运行结果和单线程运行的结果是一样的,而且其他的变量的值也和预期的是一样的,就是线程安全的。

线程安全问题都是由全局变量及静态变量引起的。若每个线程中对全局变量、静态变量只有读操作,而无写操作,一般来说,这个全局变量是线程安全的;若有多个线程同时执行写操作,一般都需要考虑线程同步,否则的话就可能影响线程安全。

比如电脑端,手机端,跟售票窗口同时卖一张票,因为是多线程所以是同时进行的,当一个线程买到票后,可能另外两个线程也进行到一半了,但是因为已经没有票了,所以就会出现异常错误,比如重复的票号或异常的票号,这就是出现了线程安全问题。

2.线程同步:

Java中提供了线程同步机制,来解决线程安全的问题。

线程同步的方式有两种:

1.同步代码块

2.同步方法

2.1同步代码块:

在代码块声明上,加上synchronized

  1. synchronized (锁对象) {
  2. 可能会产生线程安全问题的代码
  3. }

同步代码块中的锁对象可以是任意的对象;但多个线程时,要使用同一个锁对象才能够保证线程安全。

多线程代码:

  1. package com.oracle.Demo02;
  2.  
  3. public class Demo {
  4.  
  5. public static void main(String[] args) {
  6. Tickets t=new Tickets();
  7. Thread t0=new Thread(t);
  8. Thread t1=new Thread(t);
  9. Thread t2=new Thread(t);
  10. t0.start();
  11. t1.start();
  12. t2.start();
  13.  
  14. }
  15.  
  16. }

使用同步代码块:

  1. package com.oracle.Demo02;
  2. //卖票任务
  3. //synchronized(任意对象){
  4. // 线程要操作的共享数据
  5. //}
  6. //同步代码块解决线程不安全的原理:
  7. // 线程遇到同步代码块时,线程会先判断你的同步锁有没有,如果有,就获取同步锁,进入同步去执行代码,执行完毕后,
  8. //再把锁还回去。
  9. // 在同步中,线程进行了休眠,此时另一个线程会执行,遇到同步代码块的时候,会判断同步锁有没有,如果没有,
  10. //没有获取到同步锁的线程,不能进入同步去执行,被阻挡在同步代码块外面
  11. public class Tickets implements Runnable{
  12. private int ticket=100;
  13. //private Object obj=new Object();
  14. public void run() {
  15. while(true){
  16. synchronized (this) { //这里的this 可以为任意对象,替换成上面的obj也可以,为自身对象this也可以
  17. if(ticket>0){
  18. try {
  19. Thread.sleep(10);
  20. } catch (InterruptedException e) {
  21. e.printStackTrace();
  22. }
  23. System.out.println(Thread.currentThread().getName()+"出售第"+(ticket--)+"张票");
  24. }
  25. }
  26. }
  27.  
  28. }
  29.  
  30. }

2.2 同步方法

在方法声明上,加上synchronized

  1. public synchronized void method(){
  2. 可能会产生线程安全问题的代码
  3. }

同步方法中的锁对象是 this

多线程代码:

  1. package com.oracle.Demo03;
  2.  
  3. public class Demo {
  4.  
  5. public static void main(String[] args) {
  6. Tickets t=new Tickets();
  7. Thread t0=new Thread(t);
  8. Thread t1=new Thread(t);
  9. Thread t2=new Thread(t);
  10. t0.start();
  11. t1.start();
  12. t2.start();
  13.  
  14. }
  15.  
  16. }

使用同步代码块:

  1. package com.oracle.Demo03;
  2.  
  3. public class Tickets implements Runnable{
  4. private int ticket=100;
  5. public void run() {
  6. while(true){
  7. method();
  8. }
  9. }
  10. //同步方法:解决线程不安全问题
  11. //问题1:同步方法有锁吗?有,锁是本类引用,this关键字
  12. //问题2:如果你的同步方法是静态的,还有同步锁吗?是this吗?
  13. //有,不是this,是本类自己,Tickets.class
  14.  
  15. public synchronized void method(){
  16. if(ticket>0){
  17. try {
  18. Thread.sleep(10);
  19. } catch (InterruptedException e) {
  20. e.printStackTrace();
  21. }
  22. System.out.println(Thread.currentThread().getName()+"出售第"+(ticket--)+"张票");
  23. }else{
  24. return;
  25. }
  26. }
  27.  
  28. }

静态同步方法: 在方法声明上加上static synchronized

  1. public static synchronized void method(){
  2. 可能会产生线程安全问题的代码
  3. }

 使用同步方法:

  1. package com.oracle.Demo03;
  2.  
  3. public class Tickets implements Runnable{
  4. private int ticket=100;
  5. public void run() {
  6. while(true){
  7. method();
  8. }
  9. }
  10. //同步方法:解决线程不安全问题
  11. //问题1:同步方法有锁吗?有,锁是本类引用,this关键字
  12. //问题2:如果你的同步方法是静态的,还有同步锁吗?是this吗?
  13. //有,不是this,是本类自己,Tickets.class
  14.  
  15. public synchronized void method(){
  16. if(ticket>0){
  17. try {
  18. Thread.sleep(10);
  19. } catch (InterruptedException e) {
  20. e.printStackTrace();
  21. }
  22. System.out.println(Thread.currentThread().getName()+"出售第"+(ticket--)+"张票");
  23. }else{
  24. return;
  25. }
  26. }
  27.  
  28. }

2.3死锁

同步锁使用的弊端:当线程任务中出现了多个同步锁(多个锁)时,如果同步中嵌套了其他的同步。这时容易引发一种现象:程序出现无限等待,这种现象我们称为死锁。

  1. synchronzied(A锁){
  2. synchronized(B锁){
  3.  
  4. }
  5. }

所以,不能在一个同步中再嵌套另一个同步

2.4  Lock接口

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

常用方法:

Lock提供了一个更加面对对象的锁,在该锁中提供了更多的操作锁的功能。

  1. package com.oracle.Demo04;
  2.  
  3. import java.util.concurrent.locks.Lock;
  4. import java.util.concurrent.locks.ReentrantLock;
  5. //Lock接口
  6. public class Tickets implements Runnable{
  7. private int ticket=100;
  8. private Lock lock=new ReentrantLock(); //创建一个锁的对象
  9. public void run() {
  10. while(true){
  11. lock.lock(); //提供锁
  12. if(ticket>0){
  13. try {
  14. Thread.sleep(10);
  15. System.out.println(Thread.currentThread().getName()+"出售第"+(ticket--)+"张票");
  16. } catch (InterruptedException e) {
  17. // TODO Auto-generated catch block
  18. e.printStackTrace();
  19. }finally{
  20. lock.unlock(); //执行完毕释放锁
  21. }
  22. }
  23. }
  24.  
  25. }
  26.  
  27. }

 2.5等待唤醒机制

线程之间的通信:多个线程在处理同一个资源,但是处理的动作(线程的任务)却不相同。通过一定的手段使各个线程能有效的利用资源。而这种手段即—— 等待唤醒机制。

等待唤醒机制的方法:

  wait():等待。将正在执行的线程释放其执行资格 和 执行权,并存储到线程池中。

  notify():唤醒。唤醒线程池中被wait()的线程,一次只能唤醒一个,而且是随机的任意线程。

  notifyAll():唤醒全部。可以将线程池中所有被wait()的线程唤醒。

所谓唤醒的意思就是让线程池中的线程具备执行资格。必须注意的是,这些方法都是在 同步中才有效。同时这些方法在使用时必须标明所属锁,

这样才可以明确出这些方法操作的到底是哪个锁上的线程。

等待唤醒示例图:

自定义实体类:

  1. package com.oracle.Demo06;
  2. //等待唤醒机制 自定义实体类
  3. public class Resource {
  4. public String name;
  5. public String sex;
  6. public boolean flag=false;
  7. }

 线程类:

  1. package com.oracle.Demo06;
  2.  
  3. public class Input implements Runnable{
  4. private Resource r;
  5. public Input(Resource r){
  6. this.r=r;
  7. }
  8. public void run() {
  9. int i=0;
  10. while(true){
  11. synchronized(r){
  12. if(r.flag){
  13. try {
  14. r.wait();
  15. } catch (InterruptedException e) {
  16. // TODO Auto-generated catch block
  17. e.printStackTrace();
  18. }
  19. if(i%2==0){
  20. r.name="张三";
  21. r.sex="男";
  22. }else{
  23. r.name="lisi";
  24. r.sex="nv";
  25. }
  26. i++;
  27. }
  28. r.flag=true;
  29. r.notify();
  30. }
  31. }
  32.  
  33. }
  34.  
  35. }
  1. package com.oracle.Demo06;
  2.  
  3. public class Output implements Runnable{
  4. private Resource r;
  5. public Output(Resource r){
  6. this.r=r;
  7. }
  8. //flag:当flag为true的时候,代表赋值完成
  9. //为false,获取值完成
  10. public void run() {
  11. while(true){
  12. synchronized (r) {
  13. if(!r.flag){
  14. try {
  15. r.wait();
  16. } catch (InterruptedException e) {
  17. // TODO Auto-generated catch block
  18. e.printStackTrace();
  19. }
  20.  
  21. }
  22. System.out.println(r.name+"..."+r.sex);
  23. r.flag=false;
  24. r.notify();
  25. }
  26. }
  27. }
  28.  
  29. }

测试类:

  1. package com.oracle.Demo06;
  2.  
  3. public class Output implements Runnable{
  4. private Resource r;
  5. public Output(Resource r){
  6. this.r=r;
  7. }
  8. //flag:当flag为true的时候,代表赋值完成
  9. //为false,获取值完成
  10. public void run() {
  11. while(true){
  12. synchronized (r) {
  13. if(!r.flag){
  14. try {
  15. r.wait();
  16. } catch (InterruptedException e) {
  17. // TODO Auto-generated catch block
  18. e.printStackTrace();
  19. }
  20.  
  21. }
  22. System.out.println(r.name+"..."+r.sex);
  23. r.flag=false;
  24. r.notify();
  25. }
  26. }
  27. }
  28.  
  29. }

Java第三阶段学习(七、线程池、多线程)的更多相关文章

  1. Java第三阶段学习(六、多线程)

    一.进程和线程的区别: 进程:指正在运行的程序,当一个程序进入内存运行,就变成一个进程. 线程:线程是进程的一个执行单元. 总结:一个程序运行后至少会有一个进程,一个进程可以有多个线程. 多线程:多线 ...

  2. JAVA多线程学习七-线程池

    为什么用线程池 1.创建/销毁线程伴随着系统开销,过于频繁的创建/销毁线程,会很大程度上影响处理效率 例如: 记创建线程消耗时间T1,执行任务消耗时间T2,销毁线程消耗时间T3 如果T1+T3> ...

  3. Java第三阶段学习(十、XML学习)

    一.XML学习 1.模拟Servlet执行 在学习完前端及java与数据库后,将进行WEB编程阶段的学习.在WEB编程中,可以通过浏览器访问WEB服务器上的数据.这时WEB服务器就相当于另一台计算机. ...

  4. Java第三阶段学习(九、类加载器、反射)

    一.类加载器 1.类的加载: 当程序要使用某个类时,如果该类还未被加载到内存中,则系统会通过加载,连接,初始化三步来实现对这个类进行初始化. 1.1 加载: 就是指将class文件读入内存,并为之自动 ...

  5. Java第三阶段学习(八:网络通信协议、UDP与TCP协议)

    一.网络通信协议 1.概念: 通过计算机网络可以使多台计算机实现连接,位于同一个网络中的计算机在进行连接和通信时需要遵守一定的规则,在计算机网络中,这些连接和通信的规则被称为网络通信协议,它对数据的传 ...

  6. Java第三阶段学习(三、字符流、转换流)

    一.字节流读取中文时出现的问题: 文件中有中文时,用字节流读取会出现乱码的问题,因为一个中文为两个字节. 二.字符编码表 编码表:其实就是生活中字符和计算机二进制的对应关系表. 1.ascii: 一个 ...

  7. Java第三阶段学习(十四、JSP动态页面、EL表达式、JSTL标签库)

    一.JSP技术 1.jsp脚本和注释 jap脚本: 1)<%java代码%> ----- 内部的java代码翻译到service方法的内部,比如写在doget.dopost 内的代码 2) ...

  8. Java第三阶段学习(十三、会话技术、Cookie技术与Session技术)

    一.会话技术  1. 存储客户端状态 会话技术是帮助服务器记住客户端状态(区分客户端)的.  2. 会话技术 从打开一个浏览器访问某个站点,到关闭这个浏览器的整个过程,称为一次会话.会话技术就是记录这 ...

  9. Java第三阶段学习(十二、HttpServletRequest与HttpServletResponse)

    一.HttpServletRequest 1.概述: 我们在创建Servlet时会覆盖service()方法,或doGet()/doPost(),这些方法都有两个参数,一个为代表请求的request和 ...

随机推荐

  1. 设计模式C++学习笔记之十六(Observer观察者模式)

      16.1.解释 概念:定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新. main(), IObservable,被观察者接口 CHanFei ...

  2. (转!)利用Keras实现图像分类与颜色分类

    2018-07-19 全部谷歌渣翻加略微修改 大家将就的看哈 建议大佬们还是看看原文 点击收获原文 其中用到的示例文件 multi-output-classification 大家可以点击 下载 . ...

  3. linux进程内存布局

      一个程序本质上都是由 BSS 段.data段.text段三个组成的.这样的概念在当前的计算机程序设计中是很重要的一个基本概念,而且在嵌入式系统的设计中也非常重要,牵涉到嵌入式系统运行时的内存大小分 ...

  4. PID控制器开发笔记之八:带死区的PID控制器的实现

    在计算机控制系统中,由于系统特性和计算精度等问题,致使系统偏差总是存在,系统总是频繁动作不能稳定.为了解决这种情况,我们可以引入带死区的PID算法. 1.带死区PID的基本思想 带死区的PID控制算法 ...

  5. Confluence 6 手动安装语言包和找到更多语言包

    手动安装语言包 希望以手动的方式按照语言包,你需要按照下面描述的方式上传语言包.一旦你安装成功后,语言包插件将会默认启用. 插件通常以 JAR 或者 OBR(OSGi Bundle Repositor ...

  6. Confluence 6 数据库整合的方法 2:针对有大量附件的运行实例

    设置准备 这个方法仅仅针对附件存储在文件系统中.如果你存储附件在数据库中,请参考 Attachment Storage Configuration 文档中的内容来找到如何在 2 种不同的文件存储方式之 ...

  7. Confluence 6 链接到其他应用

    应用链接(Application Links)有时候也被称为 "AppLinks" 是一系列测插件能够允许你在 Atlassian  的应用中互相链接.链接 2 个应用能够允许你在 ...

  8. 浅析PHP中的闭包和匿名函数

    PHP闭包和匿名函数使用的句法与普通函数相同,但闭包和匿名函数其实是伪装成函数的对象(Closure类的实例) .下面给大家介绍PHP中的闭包和匿名函数知识,需要的朋友参考下吧   闭包是指在创建时封 ...

  9. mysql 各种关系代数的使用

    连接(JOIN) 选择运算表示为: R⋈S ,其中R和S为不同的两个关系 连接运算是选取两个指定关系中的属性满足给定条件的元祖连接在一起来组成一个新的关系 数学形式: JOIN 关系名1 AND 关系 ...

  10. bzoj1123 割点性质应用

    删掉无向图上任意一点,请求出将会增加的不连通的点对数 将无向图联通性的问题转化到搜索树方向上考虑 如果一个点不是割点,那么删掉该点的答案很简单,就是2*(n-1) 如果点u是割点,同时u在搜索树上有t ...