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的生产和消费的代码。
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
2:线程池.
2.1:为什么会出现线程池。
线程的总时间=启动线程的时间t1+执行run方法的时间t2+销毁线程的时间t3。
如果t1+t3>t2时。这个时候应该减少启动线程和销毁线程的次数以节省时间。线程池可以解决这个问题。创建线程池,先启动固定个数的线程,让这些线程去执行任务,当一个线程执行完一个任务后,它会处于空闲状态,如果还有任务,它会继续执行其他的任务。当所有的任务执行完后,再销毁线程池中的线程。
以上一段是网上找的,说的就是那么一回事,减少线程启动和提高线程运行的质量,也提高了运行效率。现实中这种情况也很常见嘛。一个饭店,一般都有一大桶饭的吧,当有顾客来了,就可以直接在饭桶里盛饭给顾客了,如果你没有一桶饭,那么每次有一个顾客来你都要单独帮他煮一份,万一顾客很多呢?这个饭店会有很多锅用来煮饭吗?即使煮好了碗,那顾客还有一碗,岂不是又要重新开锅帮他煮一碗?这样不合适吧,等饭的时间都比吃饭的时间要长咯。所以,饭店里准备着一大桶饭比较好。线程池就相当于这个饭桶,顾客相当于每个线程。锅其实也可以理解为资源或者内存吧。线程池(饭桶)提高了线程运行(顾客吃饭)的效率了,也节省了线程的开启关闭所占用的内存(锅)。虽然这个例子没有提到线程的关闭,但是这个例子就是这么一个意思。
只创建固定的线程。让它执行更多的任务。请往下看线程池的创建。
2.2相关的类:(一般也就是这两三个类就足够了)
Executors(执行者):此包中所定义的 Executor、ExecutorService、ScheduledExecutorService、ThreadFactory 和 Callable 类的工厂和实用方法。
ExecutorService:一个接口
ThreadPoolExecutor:实现了ExecutorServer接口的一个子类
2.3
线程池的创建以及使用线程池开启线程。
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);
} } }
总结:以下是个人对线程的一下理解。
线程可以让一个程序同时去执行多个任务,而不必等待前面的任务执行完才能执行下一个。好比车道:单条通道只能让一辆车通过,多条车道能让多辆车通过,通道就是线程,让车通过就是任务,让车通过的速度哪个快就很明显了(前提是车的数量和车速不能相差太多)。线程是为了同步完成多项任务,不是为了提高运行效率,而是为了提高资源的使用效率从而提高了系统的效率。
当多线程操作同一个数据时就会发生线程安全问题,解决的方法就是实现线程的同步,同步有两种方法,1:使用synchronized(同步)关键字,synchronized还可以分为synchronized方法和synchronized代码块;2:使用Lock,配合Condition对象。同步是解决的安全性问题,但是同时也带来了两个问题。1:执行效率下降;2:同步死锁。死锁在同步嵌套(同步中又有同步)发生。
线程太多,开启和关闭用的时间就多了,为了提高线程运行的效率更高,用到了线程池。线程池使得线程的开启和关闭的时间大大减少,提高了线程运行效率。
Java多线程(三)锁对象和线程池的更多相关文章
- Java多线程学习(八)线程池与Executor 框架
目录 历史优质文章推荐: 目录: 一 使用线程池的好处 二 Executor 框架 2.1 简介 2.2 Executor 框架结构(主要由三大部分组成) 2.3 Executor 框架的使用示意图 ...
- 线程池 | Java多线程,彻底搞懂线程池
熟悉Java多线程编程的同学都知道,当我们线程创建过多时,容易引发内存溢出,因此我们就有必要使用线程池的技术了. 最近看了一些相关文章,并亲自研究了一下源码,发现有些文章还是有些问题的,所以我也总结了 ...
- Java多线程操作同一个对象,线程不安全
Java多线程操作同一个对象 发现问题:多个线程操作同一资源的情况下,线程不安全,数据紊乱 代码: package multithreading; // Java多线程操作同一个对象 // 买火车票的 ...
- java多线程系类:JUC线程池:04之线程池原理(三)(转)
转载请注明出处:http://www.cnblogs.com/skywang12345/p/3509960.html 本章介绍线程池的生命周期.在"Java多线程系列--"基础篇& ...
- java多线程系类:JUC线程池:03之线程池原理(二)(转)
概要 在前面一章"Java多线程系列--"JUC线程池"02之 线程池原理(一)"中介绍了线程池的数据结构,本章会通过分析线程池的源码,对线程池进行说明.内容包 ...
- 《java学习三》并发编程 -------线程池原理剖析
阻塞队列与非阻塞队 阻塞队列与普通队列的区别在于,当队列是空的时,从队列中获取元素的操作将会被阻塞,或者当队列是满时,往队列里添加元素的操作会被阻塞.试图从空的阻塞队列中获取元素的线程将会被阻塞,直到 ...
- java多线程系类:JUC线程池:02之线程池原理(一)
在上一章"Java多线程系列--"JUC线程池"01之 线程池架构"中,我们了解了线程池的架构.线程池的实现类是ThreadPoolExecutor类.本章,我 ...
- java多线程系类:JUC线程池:01之线程池架构
概要 前面分别介绍了"Java多线程基础"."JUC原子类"和"JUC锁".本章介绍JUC的最后一部分的内容--线程池.内容包括:线程池架构 ...
- java多线程(三)——锁机制synchronized(同步语句块)
用关键字synchronized声明方法在某些情况下是有弊端的,比如A线程调用同步方法之行一个长时间的任务,那么B线程必须等待比较长的时间,在这样的情况下可以使用synchronized同步语句快来解 ...
随机推荐
- 还原bak到localdb的问题:The logical database file cannot be found ldf
主要环境相关因素:win7,ms sql 2012,ms localdb,msms 2012. 步骤: 1,让DBA给一个bak文件到本地来做测试,DBA按自己的工作流程得到bak文件. 2,在msm ...
- (2)-生成JSONObject的方法
生成JSONObject一般有两种方式,通过javabean或者map类型来生成.如下面的例子: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 2 ...
- Android-Sqlite-SQL操作增删改查
一想到Android到数据库,只需要想到一个类 SQLiteOpenHelper,然后写一个类继承 SQLiteOpenHelper,重写构造方法,对数据库进行配置 public class MySQ ...
- Xamarin 技术解析
Xamarin 是一套基于C#语言的跨平台移动应用开发工具,今年2月份微软宣布收购Xamarin,而后在4月份进行的Build大会上微软宣布将会在各个版本的Visual Studio中免费提供Xama ...
- java 封装返回json数据
做的东西,一直是用easyui的,和后台的交互数据都是json格式的. 今天想要单独弄一个json数据返回给前台,其实是比较简单的问题,json接触不多,记录一下. 代码: public static ...
- jmeter分布式环境
搭建jmeter分布式环境 (1)确定分布式结构,即1台机器部署master.几台机器部署slave? (2)将相同版本的jmeter分别拷贝到这几台机器 (3)修改maste ...
- 微信小程序web-view之动态加载html页面
官方推出的web-view方便了很多开发人员. 我们在做的时候,经常会想到写一个小程序的page然后通过动态加载web-view的形式来完成其他功能页面的开发. 之前研究web-view的时候发现网上 ...
- 纸壳CMS列表Grid的配置
纸壳CMS(ZKEACMS)里的Grid是一个TagHelper,是对jQuery插件datatables的一个配置封装. Easy.Mvc.TagHelpers.GridTagHelper grid ...
- BASE64编码乱码问题的浅层分析与解释
本文由作者朱臻授权网易云社区发布. 1问题案例 曾在开发过程中,我们遇到了BASE64编码乱码的问题,该问题的场景如下: 当web前端,将带有中文字符的字符串base64编码后,传到后端.当后端将数据 ...
- dubbo事件通知机制(1)
此文已由作者岳猛授权网易云社区发布. 欢迎访问网易云社区,了解更多网易技术产品运营经验. dubbo事件通知机制:http://dubbo.io/books/dubbo-user-book/demos ...