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();
其中,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时。这个时候应该减少启动线程和销毁线程的次数以节省时间。线程池可以解决这个问题。创建线程池,先启动固定个数的线程,让这些线程去执行任务,当一个线程执行完一个任务后,它会处于空闲状态,如果还有任务,它会继续执行其他的任务。当所有的任务执行完后,再销毁线程池中的线程。
线程池(饭桶)提高了线程运行(顾客吃饭)的效率了,也节省了线程的开启关闭所占用的内存(锅)。虽然这个例子没有提到线程的关闭,但是这个例子就是这么一个意思。
只创建固定的线程。让它执行更多的任务。请往下看线程池的创建。
两三个类就足够了)
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:同步死锁。死锁在同步嵌套(同步中又有同步)发生。
线程太多,开启和关闭用的时间就多了,为了提高线程运行的效率更高,用到了线程池。线程池使得线程的开启和关闭的时间大大减少,提高了线程运行效率。