疯狂java


您现在的位置: 疯狂软件 >> 新闻资讯 >> 正文

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 list = new ArrayList();

  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 }

  复制代码

  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:同步死锁。死锁在同步嵌套(同步中又有同步)发生。

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