Java多线程 锁对象和线程池
2016-07-13 15:41
344 查看
原文出处:新哥
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的生产和消费的代码。
2.1、为什么会出现线程池。
线程的总时间=启动线程的时间t1+执行run方法的时间t2+销毁线程的时间t3。
如果t1+t3>t2时。这个时候应该减少启动线程和销毁线程的次数以节省时间。线程池可以解决这个问题。创建线程池,
先启动固定个数的线程,让这些线程去执行任务,当一个线程执行完一个任务后,它会处于空闲状态,如果还有任务,它会继续执
行其他的任务。当所有的任务执行完后,再销毁线程池中的线程。
以上一段是网上找的,说的就是那么一回事,减少线程启动和提高线程运行的质量,也提高了运行效率。现实中这种
情况也很常见嘛。一个饭店,一般都有一大桶饭的吧,当有顾客来了,就可以直接在饭桶里盛饭给顾客了,如果你没有一桶饭,那
么每次有一个顾客来你都要单独帮他煮一份,万一顾客很多呢?这个饭店会有很多锅用来煮饭吗?即使煮好了碗,那顾客还有一
碗,岂不是又要重新开锅帮他煮一碗?这样不合适吧,等饭的时间都比吃饭的时间要长咯。所以,饭店里准备着一大桶饭比较好。
线程池就相当于这个饭桶,顾客相当于每个线程。锅其实也可以理解为资源或者内存吧。线程池(饭桶)提高了线程运行(顾客吃
饭)的效率了,也节省了线程的开启关闭所占用的内存(锅)。虽然这个例子没有提到线程的关闭,但是这个例子就是这么一个意
思。
只创建固定的线程。让它执行更多的任务。请往下看线程池的创建。
2.2、相关的类:(一般也就是这两三个类就足够了)
Executors(执行者):此包中所定义的 Executor、ExecutorService、ScheduledExecutorService、
ThreadFactory 和 Callable 类的工厂和实用方法。
ExecutorService:一个接口
ThreadPoolExecutor:实现了ExecutorServer接口的一个子类
2.3、 线程池的创建以及使用线程池开启线程。
总结:以下是个人对线程的一下理解。
线程可以让一个程序同时去执行多个任务,而不必等待前面的任务执行完才能执行下一个。好比车道:单条通道只能让一辆车通过,多条车道能让多辆车通过,通道就是线程,让车通过就是任务,让车通过的速度哪个快就很明显了(前提是车的数量和车速不能相差太多)。线程是为了同步完成多项任务,不是为了提高运行效率,而是为了提高资源的使用效率从而提高了系统的效率。
当多线程操作同一个数据时就会发生线程安全问题,解决的方法就是实现线程的同步,同步有两种方法,
1:使用synchronized(同步)关键字,synchronized还可以分为synchronized方法和synchronized代码块;
2:使用Lock,配合Condition对象。
同步是解决的安全性问题,但是同时也带来了两个问题。
1:执行效率下降;
2:同步死锁。死锁在同步嵌套(同步中又有同步)发生。
线程太多,开启和关闭用的时间就多了,为了提高线程运行的效率更高,用到了线程池。线程池使得线程的开启和关闭的时间大大减少,提高了线程运行效率。
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的生产和消费的代码。
<span style="font-size:14px;">package com.java.lock; import java.util.ArrayList; import java.util.List; import java.util.concurrent.locks.Condition; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; public class ProduceCustomerDemo1 { public static void main(String[] args) { Produce p1 = new Produce(); p1.setName("生产者1"); Produce p2 = new Produce(); p2.setName("生产者2"); Produce p3 = new Produce(); p3.setName("生产者3"); Customer c1 = new Customer(); c1.setName("消费者1"); Customer c2 = new Customer(); c2.setName("消费者2"); Customer c3 = new Customer(); c3.setName("消费者3"); p1.start(); p2.start(); c1.start(); c2.start(); p3.start(); c3.start(); } } class MyLock { public static Lock lock = new ReentrantLock(); public static Condition notFull = lock.newCondition(); public static Condition notEmpty = lock.newCondition(); public static int num;// 编号 public static int sum;// 库存 public static Object obj = new Object(); public static List<Integer> list = new ArrayList<Integer>(); } class Produce extends Thread { @Override public void run() { while (true) { //同步开始的标志,这里代替了synchronized MyLock.lock.lock(); while (MyLock.sum >= 6) {// 在多个消费者操作这个数据时,每次都要判断而且是循环判断 try { //notFull,调用await()方法进入等待状态,前提是sum》=6 MyLock.notFull.await(); } catch (InterruptedException e) { e.printStackTrace(); } } MyLock.sum++; MyLock.num++; MyLock.list.add(MyLock.num); try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName() + "生产了一个产品,编号:" + MyLock.num + ",现有:" + MyLock.sum + "个"); //调用signal()方法将等待中的线程唤醒,也可以使用signalAll()方法 MyLock.notEmpty.signal(); //同步结束的标志 MyLock.lock.unlock(); } } } class Customer extends Thread { @Override public void run() { while (true) { //同步代码块开始 MyLock.lock.lock(); while (MyLock.sum == 0) { try { //进入线程等待状态,前提是sum==0 MyLock.notEmpty.await(); } catch (InterruptedException e) { e.printStackTrace(); } } int ran = (int) (Math.random() * MyLock.sum); MyLock.sum--; try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } int number = MyLock.list.remove(ran); System.out.println(Thread.currentThread().getName() + "消费了一个包子,编号:" + number + ",现有:" + MyLock.sum + "个"); //唤醒等待中的一个线程 MyLock.notFull.signal(); //同步代码块结束 MyLock.lock.unlock(); } } } Lock和Condition</span>
2:线程池.
2.1、为什么会出现线程池。线程的总时间=启动线程的时间t1+执行run方法的时间t2+销毁线程的时间t3。
如果t1+t3>t2时。这个时候应该减少启动线程和销毁线程的次数以节省时间。线程池可以解决这个问题。创建线程池,
先启动固定个数的线程,让这些线程去执行任务,当一个线程执行完一个任务后,它会处于空闲状态,如果还有任务,它会继续执
行其他的任务。当所有的任务执行完后,再销毁线程池中的线程。
以上一段是网上找的,说的就是那么一回事,减少线程启动和提高线程运行的质量,也提高了运行效率。现实中这种
情况也很常见嘛。一个饭店,一般都有一大桶饭的吧,当有顾客来了,就可以直接在饭桶里盛饭给顾客了,如果你没有一桶饭,那
么每次有一个顾客来你都要单独帮他煮一份,万一顾客很多呢?这个饭店会有很多锅用来煮饭吗?即使煮好了碗,那顾客还有一
碗,岂不是又要重新开锅帮他煮一碗?这样不合适吧,等饭的时间都比吃饭的时间要长咯。所以,饭店里准备着一大桶饭比较好。
线程池就相当于这个饭桶,顾客相当于每个线程。锅其实也可以理解为资源或者内存吧。线程池(饭桶)提高了线程运行(顾客吃
饭)的效率了,也节省了线程的开启关闭所占用的内存(锅)。虽然这个例子没有提到线程的关闭,但是这个例子就是这么一个意
思。
只创建固定的线程。让它执行更多的任务。请往下看线程池的创建。
2.2、相关的类:(一般也就是这两三个类就足够了)
Executors(执行者):此包中所定义的 Executor、ExecutorService、ScheduledExecutorService、
ThreadFactory 和 Callable 类的工厂和实用方法。
ExecutorService:一个接口
ThreadPoolExecutor:实现了ExecutorServer接口的一个子类
2.3、 线程池的创建以及使用线程池开启线程。
<span style="font-size:14px;">import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; public class ThreadPoolDemo1 { public static void main(String[] args) { /* * 创建一个大小为 3 的线程池,newFixedThreadPool(int nThread) * 意思是这个线程池中最多同时可运行三个线程,如果要想执行其他线程应该等待线程池池有线程结束 */ ExecutorService executor = Executors.newFixedThreadPool(3); /*Produce p1 = new Produce(); Customer c1 = new Customer();*/ /*for(int i=0; i<3; i++){ executor.execute(p1); executor.execute(c1); }*/ //用循环来开启三个线程,也可以手动一个个开启 for(int i=0; i<3; i++){ executor.execute(new MyRunnable()); } /*MyRunnable mr = new MyRunnable(); executor.execute(mr); executor.execute(mr); executor.execute(mr);*/ //线程执行完毕关闭线程池 executor.shutdown(); } } class MyRunnable implements Runnable{ @Override public void run() { for(int i=0; i<10; i++){ System.out.println(Thread.currentThread().getName()+":"+i); } } }import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; public class ThreadPoolDemo1 { public static void main(String[] args) { /* * 创建一个大小为 3 的线程池,newFixedThreadPool(int nThread) * 意思是这个线程池中最多同时可运行三个线程,如果要想执行其他线程应该等待线程池池有线程结束 */ ExecutorService executor = Executors.newFixedThreadPool(3); /*Produce p1 = new Produce(); Customer c1 = new Customer();*/ /*for(int i=0; i<3; i++){ executor.execute(p1); executor.execute(c1); }*/ //用循环来开启三个线程,也可以手动一个个开启 for(int i=0; i<3; i++){ executor.execute(new MyRunnable()); } /*MyRunnable mr = new MyRunnable(); executor.execute(mr); executor.execute(mr); executor.execute(mr);*/ //线程执行完毕关闭线程池 executor.shutdown(); } } class MyRunnable implements Runnable{ @Override public void run() { for(int i=0; i<10; i++){ System.out.println(Thread.currentThread().getName()+":"+i); } } }</span>
总结:以下是个人对线程的一下理解。
线程可以让一个程序同时去执行多个任务,而不必等待前面的任务执行完才能执行下一个。好比车道:单条通道只能让一辆车通过,多条车道能让多辆车通过,通道就是线程,让车通过就是任务,让车通过的速度哪个快就很明显了(前提是车的数量和车速不能相差太多)。线程是为了同步完成多项任务,不是为了提高运行效率,而是为了提高资源的使用效率从而提高了系统的效率。
当多线程操作同一个数据时就会发生线程安全问题,解决的方法就是实现线程的同步,同步有两种方法,
1:使用synchronized(同步)关键字,synchronized还可以分为synchronized方法和synchronized代码块;
2:使用Lock,配合Condition对象。
同步是解决的安全性问题,但是同时也带来了两个问题。
1:执行效率下降;
2:同步死锁。死锁在同步嵌套(同步中又有同步)发生。
线程太多,开启和关闭用的时间就多了,为了提高线程运行的效率更高,用到了线程池。线程池使得线程的开启和关闭的时间大大减少,提高了线程运行效率。
相关文章推荐
- FastJson关于java的使用
- Java中重载和重写的区别
- Java 基础【12】 文件(文件夹) 创建和删除
- Java8初体验(二)Stream语法详解
- java--关于线程同步(5种同步方式)
- spring声明式事务管理的几种方式
- 谈谈对Spring IOC的理解
- s:datepicker No tag "datetimepicker" defined in tag library imported with prefix "s
- 【奔跑的菜鸟】eclipse的一些实用的快捷操作
- 利用Java生成静态HMTL页面
- Eclipse 4.6 neon搭建Arcgis for Android的开发环境
- Java异常抛出与对象深浅复制
- JAVAAPI学习之Calendar类;Calendar类set()、add()、roll()方法区别
- 文件上传之SpringMVC
- eclipse修改后无法正常保存文件的BUG解决
- java Html2Image 实现html转图片功能
- SpringMvc原理
- java面向对象一:实现继承、重载、重写
- s:textfield reqiuredString 验证框架 validators 无法将页面的输入值赋值给action中的属性?
- java_reflect_01