Java多线程(三)锁对象和线程池

1:锁(Lock)

  1.1       java提供了一个锁的接口,这个锁同样可以达到同步代码块的功能,API文档上说使用锁比使用synchronized更加灵活。

  1.2       如何使用这个“锁”

    //1.创建一个所对象,我们可以理解为写一个synchronized代码块

    public static Lock lock = new ReentrantLock();//用lock的一个子类去创建

    //2.假设有某程序中使用两把锁,这两把锁是类似于synchronized里的锁

    //要使用到Condition类,中文:条件、情况、制约、限制的意思,在API文档总称之为“条件、条件列队或者条件变量”

     public static Condition notFull = lock.newCondition();//Condition

    public static Condition notEmpty = lock.newCondition();

  1.3       Condition 将 Object 监视器方法(wait、notify 和 notifyAll)分解成截然不同的对象,以便通过将这些对象与任意 Lock 实现组合使用,为每个对象提供多个等待 set(wait-set)。其中,Lock 替代了 synchronized 方法和语句的使用,Condition 替代了 Object 监视器方法的使用。(摘自文档,重点是最后一句)

  1.4       重要方法(Condition的):

      await():等候,调用此方法线程将释放锁,进入等待状态

      signal():中文:信号、发信号。调用此方法可以唤醒一个等待总的线程

      signalAll():唤醒所有在等待重点的线程

1.5       使用Lock和Condition的生产和消费的代码。

  1 package com.java.lock;
  2
  3 import java.util.ArrayList;
  4 import java.util.List;
  5 import java.util.concurrent.locks.Condition;
  6 import java.util.concurrent.locks.Lock;
  7 import java.util.concurrent.locks.ReentrantLock;
  8
  9 public class ProduceCustomerDemo1 {
 10
 11     public static void main(String[] args) {
 12         Produce p1 = new Produce();
 13         p1.setName("生产者1");
 14         Produce p2 = new Produce();
 15         p2.setName("生产者2");
 16         Produce p3 = new Produce();
 17         p3.setName("生产者3");
 18         Customer c1 = new Customer();
 19         c1.setName("消费者1");
 20         Customer c2 = new Customer();
 21         c2.setName("消费者2");
 22         Customer c3 = new Customer();
 23         c3.setName("消费者3");
 24         p1.start();
 25         p2.start();
 26         c1.start();
 27         c2.start();
 28         p3.start();
 29         c3.start();
 30     }
 31 }
 32
 33 class MyLock {
 34     public static Lock lock = new ReentrantLock();
 35     public static Condition notFull = lock.newCondition();
 36     public static Condition notEmpty = lock.newCondition();
 37     public static int num;// 编号
 38     public static int sum;// 库存
 39     public static Object obj = new Object();
 40     public static List<Integer> list = new ArrayList<Integer>();
 41 }
 42
 43 class Produce extends Thread {
 44     @Override
 45     public void run() {
 46         while (true) {
 47             //同步开始的标志,这里代替了synchronized
 48             MyLock.lock.lock();
 49             while (MyLock.sum >= 6) {// 在多个消费者操作这个数据时,每次都要判断而且是循环判断
 50                 try {
 51                     //notFull,调用await()方法进入等待状态,前提是sum》=6
 52                     MyLock.notFull.await();
 53                 } catch (InterruptedException e) {
 54                     e.printStackTrace();
 55                 }
 56             }
 57
 58             MyLock.sum++;
 59             MyLock.num++;
 60             MyLock.list.add(MyLock.num);
 61             try {
 62                 Thread.sleep(100);
 63             } catch (InterruptedException e) {
 64                 e.printStackTrace();
 65             }
 66             System.out.println(Thread.currentThread().getName() + "生产了一个产品,编号:"
 67                     + MyLock.num + ",现有:" + MyLock.sum + "个");
 68             //调用signal()方法将等待中的线程唤醒,也可以使用signalAll()方法
 69             MyLock.notEmpty.signal();
 70             //同步结束的标志
 71             MyLock.lock.unlock();
 72         }
 73     }
 74 }
 75
 76 class Customer extends Thread {
 77
 78     @Override
 79     public void run() {
 80         while (true) {
 81             //同步代码块开始
 82             MyLock.lock.lock();
 83             while (MyLock.sum == 0) {
 84                 try {
 85                     //进入线程等待状态,前提是sum==0
 86                     MyLock.notEmpty.await();
 87                 } catch (InterruptedException e) {
 88                     e.printStackTrace();
 89                 }
 90             }
 91             int ran = (int) (Math.random() * MyLock.sum);
 92             MyLock.sum--;
 93             try {
 94                 Thread.sleep(100);
 95             } catch (InterruptedException e) {
 96                 e.printStackTrace();
 97             }
 98             int number = MyLock.list.remove(ran);
 99             System.out.println(Thread.currentThread().getName() + "消费了一个包子,编号:"
100                     + number + ",现有:" + MyLock.sum + "个");
101             //唤醒等待中的一个线程
102             MyLock.notFull.signal();
103             //同步代码块结束
104             MyLock.lock.unlock();
105         }
106     }
107
108 }

Lock和Condition

2:线程池.

2.1:为什么会出现线程池。

线程的总时间=启动线程的时间t1+执行run方法的时间t2+销毁线程的时间t3。

如果t1+t3>t2时。这个时候应该减少启动线程和销毁线程的次数以节省时间。线程池可以解决这个问题。创建线程池,先启动固定个数的线程,让这些线程去执行任务,当一个线程执行完一个任务后,它会处于空闲状态,如果还有任务,它会继续执行其他的任务。当所有的任务执行完后,再销毁线程池中的线程。

      以上一段是网上找的,说的就是那么一回事,减少线程启动和提高线程运行的质量,也提高了运行效率。现实中这种情况也很常见嘛。一个饭店,一般都有一大桶饭的吧,当有顾客来了,就可以直接在饭桶里盛饭给顾客了,如果你没有一桶饭,那么每次有一个顾客来你都要单独帮他煮一份,万一顾客很多呢?这个饭店会有很多锅用来煮饭吗?即使煮好了碗,那顾客还有一碗,岂不是又要重新开锅帮他煮一碗?这样不合适吧,等饭的时间都比吃饭的时间要长咯。所以,饭店里准备着一大桶饭比较好。线程池就相当于这个饭桶,顾客相当于每个线程。锅其实也可以理解为资源或者内存吧。线程池(饭桶)提高了线程运行(顾客吃饭)的效率了,也节省了线程的开启关闭所占用的内存(锅)。虽然这个例子没有提到线程的关闭,但是这个例子就是这么一个意思。

只创建固定的线程。让它执行更多的任务。请往下看线程池的创建。

2.2相关的类:(一般也就是这两三个类就足够了)

Executors(执行者):此包中所定义的 Executor、ExecutorService、ScheduledExecutorService、ThreadFactory 和 Callable 类的工厂和实用方法。
                            ExecutorService:一个接口

ThreadPoolExecutor:实现了ExecutorServer接口的一个子类

2.3
线程池的创建以及使用线程池开启线程。

 1 import java.util.concurrent.ExecutorService;
 2 import java.util.concurrent.Executors;
 3
 4 public class ThreadPoolDemo1 {
 5     public static void main(String[] args) {
 6         /*
 7          * 创建一个大小为 3 的线程池,newFixedThreadPool(int nThread)
 8          * 意思是这个线程池中最多同时可运行三个线程,如果要想执行其他线程应该等待线程池池有线程结束
 9          */
10         ExecutorService executor = Executors.newFixedThreadPool(3);
11         /*Produce p1 = new Produce();
12         Customer c1 = new Customer();*/
13         /*for(int i=0; i<3; i++){
14             executor.execute(p1);
15             executor.execute(c1);
16         }*/
17         //用循环来开启三个线程,也可以手动一个个开启
18         for(int i=0; i<3; i++){
19             executor.execute(new MyRunnable());
20         }
21         /*MyRunnable mr = new MyRunnable();
22         executor.execute(mr);
23         executor.execute(mr);
24         executor.execute(mr);*/
25         //线程执行完毕关闭线程池
26         executor.shutdown();
27     }
28 }
29
30 class MyRunnable implements Runnable{
31
32     @Override
33     public void run() {
34         for(int i=0; i<10; i++){
35             System.out.println(Thread.currentThread().getName()+":"+i);
36         }
37
38     }
39
40 }

总结:以下是个人对线程的一下理解。

    线程可以让一个程序同时去执行多个任务,而不必等待前面的任务执行完才能执行下一个。好比车道:单条通道只能让一辆车通过,多条车道能让多辆车通过,通道就是线程,让车通过就是任务,让车通过的速度哪个快就很明显了(前提是车的数量和车速不能相差太多)。线程是为了同步完成多项任务,不是为了提高运行效率,而是为了提高资源的使用效率从而提高了系统的效率。

    当多线程操作同一个数据时就会发生线程安全问题,解决的方法就是实现线程的同步,同步有两种方法,1:使用synchronized(同步)关键字,synchronized还可以分为synchronized方法和synchronized代码块;2:使用Lock,配合Condition对象。同步是解决的安全性问题,但是同时也带来了两个问题。1:执行效率下降;2:同步死锁。死锁在同步嵌套(同步中又有同步)发生。

    线程太多,开启和关闭用的时间就多了,为了提高线程运行的效率更高,用到了线程池。线程池使得线程的开启和关闭的时间大大减少,提高了线程运行效率。

时间: 2024-10-06 01:10:11

Java多线程(三)锁对象和线程池的相关文章

Java多线程(四)、线程池(转)

Java多线程(四).线程池 分类: javaSE综合知识点 2012-09-19 17:46 3943人阅读 评论(1) 收藏 举报 系统启动一个新线程的成本是比较高的,因为它涉及到与操作系统的交互.在这种情况下,使用线程池可以很好的提供性能,尤其是当程序中需要创建大量生存期很短暂的线程时,更应该考虑使用线程池. 与数据库连接池类似的是,线程池在系统启动时即创建大量空闲的线程,程序将一个Runnable对象传给线程池,线程池就会启动一条线程来执行该对象的run方法,当run方法执行结束后,该线

Java多线程学习(八)线程池与Executor 框架

Java面试通关手册(Java学习指南,欢迎Star,会一直完善下去,欢迎建议和指导):https://github.com/Snailclimb/Java_Guide 历史优质文章推荐: Java并发编程指南专栏 分布式系统的经典基础理论 可能是最漂亮的Spring事务管理详解 面试中关于Java虚拟机(jvm)的问题看这篇就够了 目录: [TOC] 本节思维导图: 思维导图源文件+思维导图软件关注微信公众号:"Java面试通关手册" 回复关键字:"Java多线程"

java多线程系类:JUC线程池:03之线程池原理(二)(转)

概要 在前面一章"Java多线程系列--"JUC线程池"02之 线程池原理(一)"中介绍了线程池的数据结构,本章会通过分析线程池的源码,对线程池进行说明.内容包括:线程池示例参考代码(基于JDK1.7.0_40)线程池源码分析(一) 创建"线程池"(二) 添加任务到"线程池"(三) 关闭"线程池" 转载请注明出处:http://www.cnblogs.com/skywang12345/p/3509954.h

Java多线程的三大特性,线程池,JMM(Java内存模型)

一,线程的三大特性:原子性.可见性.有序性 1)原子性,即一个操作或者多个操作要么全部执行并且执行的过程不会被任何因素打断,要么就都不执行.原子性其实就是保证数据一致.线程安全一部分. 2)可见性,即当多个线程访问同一个变量时,一个线程修改了这个变量的值,其他线程能够立即看得到修改的值. 3)有序性,即程序执行的顺序按照代码的先后顺序执行.二.JAVA多线程的内存模型 共享内存模型指的就是Java内存模型(简称JMM),JMM决定一个线程对共享变量的写入时,能对另一个线程可见.从抽象的角度来看,

java多线程系类:JUC线程池:01之线程池架构

概要 前面分别介绍了"Java多线程基础"."JUC原子类"和"JUC锁".本章介绍JUC的最后一部分的内容--线程池.内容包括:线程池架构图线程池示例 转载请注明出处:http://www.cnblogs.com/skywang12345/p/3509903.html 线程池架构图 线程池的架构图如下: 1. Executor 它是"执行者"接口,它是来执行任务的.准确的说,Executor提供了execute()接口来执行

java多线程(三)——锁机制synchronized(同步语句块)

用关键字synchronized声明方法在某些情况下是有弊端的,比如A线程调用同步方法之行一个长时间的任务,那么B线程必须等待比较长的时间,在这样的情况下可以使用synchronized同步语句快来解决. 一.用同步代码块解决同步方法的弊端 Task类 1 package com.weishiyao.learn.day4.testSynchorized.ep2; 2 3 public class Task { 4 5 private String getData1; 6 private Stri

java多线程系类:JUC线程池:05之线程池原理(四)(转)

概要 本章介绍线程池的拒绝策略.内容包括:拒绝策略介绍拒绝策略对比和示例 转载请注明出处:http://www.cnblogs.com/skywang12345/p/3512947.html 拒绝策略介绍 线程池的拒绝策略,是指当任务添加到线程池中被拒绝,而采取的处理措施.当任务添加到线程池中之所以被拒绝,可能是由于:第一,线程池异常关闭.第二,任务数量超过线程池的最大限制. 线程池共包括4种拒绝策略,它们分别是:AbortPolicy, CallerRunsPolicy, DiscardOld

JAVA基础再回首(二十五)——Lock锁的使用、死锁问题、多线程生产者和消费者、线程池、匿名内部类使用多线程、定时器、面试题

JAVA基础再回首(二十五)--Lock锁的使用.死锁问题.多线程生产者和消费者.线程池.匿名内部类使用多线程.定时器.面试题 版权声明:转载必须注明本文转自程序员杜鹏程的博客:http://blog.csdn.net/m366917 我们来继续学习多线程 Lock锁的使用 虽然我们可以理解同步代码块和同步方法的锁对象问题,但是我们并没有直接看到在哪里加上了锁,在哪里释放了锁,为了更清晰的表达如何加锁和释放锁,JDK5以后提供了一个新的锁对象Lock Lock void lock():获取锁 v

Java多线程01(Thread类、线程创建、线程池)

Java多线程(Thread类.线程创建.线程池) 第一章 多线程 1.1 多线程介绍 1.1.1 基本概念 进程:进程指正在运行的程序.确切的来说,当一个程序进入内存运行,即变成一个进程,进程是处于运行过程中的程序,并且具有一定独立功能. 线程:线程是进程中的一个执行单元,负责当前进程中程序的执行,一个进程中至少有一个线程.一个进程中是可以有多个线程的,这个应用程序也可以称之为多线程程序. 简而言之:一个程序运行后至少有一个进程,一个进程中可以包含多个线程 1.1.2 单线程程序 - 从入口m