• 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 }
    View Code

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

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

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

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

       

  • 相关阅读:
    Access sql语句创建表及字段类型
    30条HTML代码编写指南 for入门者
    21 个HTML网页转RSS Feeds的工具
    51 个漂亮的电子商务网站设计分享
    如何更改列表项前的New标记的天数设置(daystoshownewicon )
    如何使Layouts里的页面应用站点母板页
    SPCAMLEditor使用系列(2)利用SPCAMLEditor,实现列表顺序号。
    在SharePoint中使用自定义的服务器控件(Web Control)
    开发支持三级目录的导航菜单
    CAML查询时用户类型字段的处理
  • 原文地址:https://www.cnblogs.com/xinge1993/p/4738149.html
Copyright © 2020-2023  润新知