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的生产和消费的代码。

8f900a89c6347c561fdf2122f13be562.png

961ddebeb323a10fe0623af514929fc1.png

1 packagecom.java.lock;2

3 importjava.util.ArrayList;4 importjava.util.List;5 importjava.util.concurrent.locks.Condition;6 importjava.util.concurrent.locks.Lock;7 importjava.util.concurrent.locks.ReentrantLock;8

9 public classProduceCustomerDemo1 {10

11 public static voidmain(String[] args) {12 Produce p1 = newProduce();13 p1.setName("生产者1");14 Produce p2 = newProduce();15 p2.setName("生产者2");16 Produce p3 = newProduce();17 p3.setName("生产者3");18 Customer c1 = newCustomer();19 c1.setName("消费者1");20 Customer c2 = newCustomer();21 c2.setName("消费者2");22 Customer c3 = newCustomer();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 classMyLock {34 public static Lock lock = newReentrantLock();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 = newObject();40 public static List list = new ArrayList();41 }42

43 class Produce extendsThread {44 @Override45 public voidrun() {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 extendsThread {77

78 @Override79 public voidrun() {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

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

8f900a89c6347c561fdf2122f13be562.png

961ddebeb323a10fe0623af514929fc1.png

1 importjava.util.concurrent.ExecutorService;2 importjava.util.concurrent.Executors;3

4 public classThreadPoolDemo1 {5 public static voidmain(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(newMyRunnable());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 implementsRunnable{31

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

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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值