同步锁与线程池的奥秘

第二篇

同步锁

当多个线程操作临界资源时,可能会出现线程安全隐患问题。

临界资源可能是

(1)某一个静态变量

(2)某一个实例变量

如果想解决这样的问题,需要使用同步操作:

异步操作:多线程的并发操作,相当于各干各的。

 

前提:有多个线程时

同步操作:在并发基础上,同一个方法内两行代码执行时间片段可以不挨着,但是其他线程不能对这两行代码有执行权,保证了代码的原子性。即这两行代码是同步的,当线程执行完后其他线程才有执行权。(相当于一个做完,另一个再做)

 

并发操作:多个线程同时开启,微观上断断续续,宏观上同时发生。

(断断续续:同一个方法内的两行代码不一定是两个挨着的时间片段)

 

线程在内部提供了一个内置的锁机制保证原子性,用于线程进行同步操作。

我们将需要同步的代码加上一个内置锁对象,当某一个线程执行到此代码时,会获取锁对象,其他线程需要等待。当获取锁对象的线程执行完同步块(或者因为异常)都会释放锁对象的使用权。

锁需要两点

(1) 锁是一个对象,多个线程一定要使用同一个锁,,否则没有记忆性同步操作。

(2) 如果想进行同步,多个现象操作的必须是同一个锁

 synchronized(锁对象的引用){

        需要同步的代码块

 }

同步代码块内注意

  1. 尽可能的不要使用sleep()和yield(),因为比较占CPU资源
  2. 同步块越小越好,省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);
	}
}

猜你喜欢

转载自blog.csdn.net/qq_42721694/article/details/82194432