第二篇
同步锁:
当多个线程操作临界资源时,可能会出现线程安全隐患问题。
临界资源可能是:
(1)某一个静态变量
(2)某一个实例变量
如果想解决这样的问题,需要使用同步操作:
异步操作:多线程的并发操作,相当于各干各的。
前提:有多个线程时
同步操作:在并发基础上,同一个方法内两行代码执行时间片段可以不挨着,但是其他线程不能对这两行代码有执行权,保证了代码的原子性。即这两行代码是同步的,当线程执行完后其他线程才有执行权。(相当于一个做完,另一个再做)
并发操作:多个线程同时开启,微观上断断续续,宏观上同时发生。
(断断续续:同一个方法内的两行代码不一定是两个挨着的时间片段)
线程在内部提供了一个内置的锁机制保证原子性,用于线程进行同步操作。
我们将需要同步的代码加上一个内置锁对象,当某一个线程执行到此代码时,会获取锁对象,其他线程需要等待。当获取锁对象的线程执行完同步块(或者因为异常)都会释放锁对象的使用权。
锁需要两点:
(1) 锁是一个对象,多个线程一定要使用同一个锁,,否则没有记忆性同步操作。
(2) 如果想进行同步,多个现象操作的必须是同一个锁
synchronized(锁对象的引用){
需要同步的代码块
}
同步代码块内注意:
- 尽可能的不要使用sleep()和yield(),因为比较占CPU资源
- 同步块越小越好,省CPU的资源
锁机制:当一个线程进入同步的代码块后,就会获得锁对象的使用权。其他线程如果执行到此处,会发现锁对象被占用,只能处于等待状态(锁池),当线程执行完同步的代码后或者出现异常,都会自动释放锁。
合适的锁对象:
必须是一个引用类型,而且必须使多个线程都可以使用这个锁对象,因此this对象比较适合
同步代码块的范围:
(1)可以是方法内的一部分代码,也可以是全部代码(相当于给方法上了锁)
(2)成员方法上添加修饰词synchronized,锁对象为this
如果一个类的所有成员方法都使用了同步关键字,当某一个线程操作了其中一个方法,另外的线程即使操作的不是这个方法,也会进入锁池状态
(3)静态方法上也可以添加synchronized,锁对象为类对象
调用方法:类名.class,每一种类都有一个唯一的类对象
锁对象:多个线程一定要使用同一个锁,否则没有达到同步操作
wait()/notify()notifyAll():
上述的方法都是Object类型提供的,用来调控线程的状态的。使用位置必须是同步块中,如果不是同步块中,会报异常。
wait():
当前获取锁对象的线程准备释放锁,给其他线程获取锁的机会。
wait(long time):
当前获取锁对象的线程如果没有被通知,在延迟time毫秒后,自动释放锁对象
wait(long time,int naons):
功能一样,只不过比上一个方法延迟的时间更加精确。
notify()/notifyAll():
当前获取锁对象的线程准备释放锁,使用此方法通知处于使用wait方法等待的线程。
notify():只会随机通知等待线程中的其中一个。
notifyAll():通过所有等待的线程来竞争锁。
线程池:1.如果每个任务都需要创建线程对象,内存开销大
2.方便管理线程对象
线程池原理:就是一些线程的集合,线程的状态不是死亡状态,当线程池从外面接受任务时,线程池管理器会查看是否有是否有空闲线程,如果有,会分配给它,若没有,任务处于等待队列中。
线程池类型:ExecutorService
另外一个类Executors里提供了多个静态方法来获取线程池对象
案例:day14--ThreadPoolDemo02
package com.hyxy.se.day14;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class ThreadPoolDemo02 {
public static void main(String[] args) {
ExecutorService pool=Executors.newFixedThreadPool(2);//维护了几个线程
Taske r1=new Taske("小A");
Taske r2=new Taske("小B");
Taske r3=new Taske("小C");
Taske r4=new Taske("小D");
Taske r5=new Taske("小E");
pool.execute(r1);
pool.execute(r2);
pool.execute(r3);
pool.execute(r4);
pool.execute(r5);
System.out.println("main方法结束");
}
}
方法1:
newSingleThreadExecutor()
获取单个线程的线程池对象,内部维护了一个无界队列用于存储
方法2:
newFixedThreadPool(int nThreads)
创建一个固定数量线程的线程池,维护一个无界队列(用于存任务)
方法3:
newCachedThreadPool()
创建一个可以根据需求来创建新线程的线程池对象,如果有可重用的,会优先使用
方法4:
newScheduledThreadPool(int corePoolSize)
创建一个线程池,可以进行设置延迟或定时执行
案例:
生产者--消费者--仓库模式:此模式脱离了仓库没有任何意义。
(1)仓库用来存储数据。
(2)仓库不满的情况下,生产者可以生产
(3)仓库中满足消费者的需求时,消费者就可以消费
package com.hyxy.se.day13;
public class ProductCustomerDemo01 {
public static void main(String[] args) {
Depot d=new Depot(30);
Customer c1=new Customer(d,"小A",20);
Customer c2=new Customer(d,"小B",50);
Customer c3=new Customer(d,"小C",30);
Customer c4=new Customer(d,"小D",45);
Productor p1=new Productor(d,"X1",30);
Productor p2=new Productor(d,"X2",50);
Productor p3=new Productor(d,"X3",70);
Productor p4=new Productor(d,"X4",40);
Productor p5=new Productor(d,"X5",60);
c1.start();
c2.start();
c3.start();
c4.start();
p1.start();
p2.start();
p3.start();
p4.start();
p5.start();
}
}
/*生产者*/
class Productor extends Thread{
private Depot depot;
private String name;
private int proNum;
public Productor(Depot depot, String name,int proNum) {
super();
this.depot = depot;
this.name = name;
this.proNum = proNum;
}
public void run(){
depot.product(proNum);
}
}
/*消费者*/
class Customer extends Thread{
private Depot depot;
private String name;
private int needNum;
public Customer(Depot depot, String name, int needNum) {
super();
this.depot = depot;
this.name = name;
this.needNum = needNum;
}
public void run(){
depot.consume(needNum);
}
}
class Depot{
private static int MAX_NUM=100;
private int num;
public Depot(int num){
this.num=num;
}
/*消费方法*/
public synchronized void consume(int needNum){
while(needNum>num){
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("仓库满足需求可以购买"+needNum);
num-=needNum;
System.out.println("成功购买,仓库剩余"+num);
this.notifyAll();
}
public synchronized void product(int proNum){
/*收到通知处于竞争锁状态,此时,如果锁被别的线程抢走了,我应该再次等待被通知*/
while(proNum+num>MAX_NUM){
try {
System.out.println("仓库空闲位置不足,不能生产"+proNum);
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("正在生产,数量为:"+proNum);
num+=proNum;
this.notifyAll();
System.out.println("生产完毕,仓库有"+num);
}
}