Java程序猿必学第十九篇—— 多线程高级

/*
1.生产者消费者模型
   技术点:
   多线程模型,安全锁机制,等待与唤醒

   男人挣钱,女人花钱天经地义
   有钱就花,没钱限制借钱消费


   男人挣钱---生产者负责生产
   女人花钱---消费者负责消费
   有钱就花---我们需要准备一个仓库存钱,消费者看库存,库存有钱才能消费
   库存没钱---等待消费
   库存满了---生产者停止生产,等待消费者消费后,有库存空间才挣
*/
  


//======================代码案例==========================
/**
  多线程 生产与消费模型: 
 分析:
 1,创建生产者类继承Thread,只负责生产
 2. 创建消费者类继承Thread,只负责消费
 3. 两个线程共同操作仓库类Store
 4. 加入安全锁及等待与唤醒机制
 
 问题1:生产两个动作,消费也是两个动作,当你打印生产,还没加库存;就已经消费了,库存值不对
 处理:加锁 ,解决生产与消费的数据混乱
 
 问题2: 仓满和仓空的限制
处理:  限制仓满,生产者等待(wait);仓空,消费者等待(wait)      

细节: 
1. 等待时,会将资源交出去
2. 锁对象与等待唤醒的对象是同一个 

扩展: 多个生产与多个消费情况
    判断中改为while
   
 *
 */
public class Test1 {
	public static void main(String[] args) {
		Store store = new Store();
		new Producter(store).start();
		new Customer(store).start();
		
		//多个生产者和消费者线程
		new Producter(store).start();
		new Customer(store).start();
	}
}

//-----生产者-----
public class Producter extends Thread {
	private Store store;
	public Producter(Store store){  //库存要存进来
		this.store = store;
	}
	@Override
	public void run() {
		while(true) {
			try {
				store.push();
			} catch (InterruptedException e) {
				e.printStackTrace();
			}  //生产者负责生产
		}
	}
}

//----消费者----
public class Customer extends Thread {
	private Store store;
	public Customer(Store store) {
		this.store = store;
	}
	@Override
	public void run() {
		while(true) {
			try {
				store.pop();
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}  //消费者负责消费
		}
	}
}

//---库存---
public class Store {
	private int num;  //单位万
	
	public void push() throws InterruptedException {  //库存生产
		
		synchronized (this) {
			//生产20件货,就满了,停止生产
			while(num>=20) {
				this.wait();
			}
			
			num++;
			System.out.println("生产者已经生产了第"+num+"件货");
			
			this.notify();  //唤醒等待的线程
		}
		
	}
	
	public void pop() throws InterruptedException {  //库存消费
		synchronized (this) { //
			while(num<=0) {
				this.wait();  //等待消费,等待时,相当于把自身资源交出去了
			}
			System.out.println("消费者已经消费了第"+num+"件货");
			num--;
			
			this.notify();  //唤醒等待的线程
		}
		
	}
}


 


//2. 多线程高级
  //2.1 线程池(重点)
/*
   概念:可以认为是线程对象的容器,预先创建多个线程对象,然后根据这些对象可以实现复用
  好处:减少创建和销毁线程的数目,提升性能 
  复用机制: 用完了线程对象后,重新回收到线程池中
*/

class Task implements Runnable{
	@Override
	public void run() {
		for(int i=1;i<=10;i++) {
			System.out.println(Thread.currentThread().getName()+"===>"+i);
		}
	};
}

public class Test1 {
	public static void main(String[] args) {
		//创建一个单个线程的线程池对象
		//ExecutorService es = Executors.newSingleThreadExecutor();
		
		//创建一个带缓冲区的线程池对象,灵活控制线程对象,有多少任务,我就创建多少个线程对象
		//ExecutorService es = Executors.newCachedThreadPool();
		
		//创建固定个数的线程池对象,如果任务过多,则用回收线程对象去处理多余任务
		ExecutorService es = Executors.newFixedThreadPool(2);
		Task task = new Task();
		es.submit(task);  //类似,线程.start()
		es.submit(task);
		es.submit(task); //谁先用完并回收,就谁去执行新任务
		
		es.shutdown();  //关闭线程池
	}
}



//2.2 Callable接口
//Callable接口: 和Runnable类似,也是处理任务的接口
//区别: Callable是带返回值的接口,可以将线程中处理完的数据返回

//Future接口,该对象接收submit的返回值,可以求出call返回值

//案例: 使用两个线程计算1~50,51~100的和,并进行汇总
public class Test2 {
	public static void main(String[] args) throws InterruptedException, ExecutionException {
		ExecutorService es = Executors.newFixedThreadPool(2);
		Future<Integer> future = es.submit(new Callable<Integer>() {
			@Override
			public Integer call() throws Exception {
				int sum = 0;
				for(int i=1;i<=50;i++) {
					sum += i;
				}
				return sum;
			}
		});
		
		Future<Integer> future2 = es.submit(new Callable<Integer>() {

			@Override
			public Integer call() throws Exception {
				int sum = 0;
				for(int i=51;i<=100;i++) {
					sum += i;
				}
				return sum;
			}
		});
		
		//get方法类似线程基础的join,在主线程中阻塞
		int sum = future.get()+future2.get();
		System.out.println("两个线程的和:"+sum);
		
	}
}


//2.3 重入锁
//重入锁:Lock接口    实现类:ReentrantLock
//与synchronized类似,用于加锁的
//提供了两个方法: lock()-获取锁   unlock()-释放锁

class MyList{
	String[] ss = {"A","B","",""};
	int   index = 2;
	Lock lock = new ReentrantLock();
	
	public void add(String value) {
		//有多个线程可以调用add方法
		lock.lock();  //加锁
		try {
			ss[index] = value;
			index++;
		} finally {
			lock.unlock();
		}
	}
}

public class Test1 {
	public static void main(String[] args) throws InterruptedException {
		MyList list = new MyList();
		Thread th1 = new Thread(new Runnable() {
			@Override
			public void run() {
				list.add("hello");
			}
		});
		th1.start();
		
		Thread th2 = new Thread(new Runnable() {
			@Override
			public void run() {
				list.add("world");
			}
		});
		th2.start();
		th1.join();   th2.join();
		
		System.out.println(Arrays.toString(list.ss));
	}
}


//2.4 读写锁
//读写锁:一般用在读远远高于写的情况,可以提升线程执行的性能
//读写锁,在读与读之间是不互斥,不阻塞的,所以性能会提升

class MyClass{
	//加入读写锁
	ReentrantReadWriteLock readWriteLock = new ReentrantReadWriteLock();
	ReadLock rl = readWriteLock.readLock();
	WriteLock wl = readWriteLock.writeLock();
	
	Integer value;
	public Integer getValue() {
		try {
			rl.lock();
			return value;
		} finally {
			rl.unlock();
		}
	}
	
	public void setValue(Integer value) {
		try {
			wl.lock();
			this.value = value;
		} finally {
			wl.unlock();
		}
		
	}
}

public class Test2 {
	public static void main(String[] args) {
		MyClass myClass = new MyClass();
		Runnable rt = new Runnable() {
			@Override
			public void run() {
				myClass.getValue();
			}
		};
		
		Runnable wt = new Runnable() {
			@Override
			public void run() {
				myClass.setValue(1);
			}
		};
		
		ExecutorService es = Executors.newFixedThreadPool(20);
		
		long start = System.currentTimeMillis();
		for(int i=0;i<18;i++) {
			es.submit(rt);  //18个线程用于读操作
		}
		for(int i=0;i<2;i++) {
			es.submit(wt);  //2个线程用于写操作
		}
		es.shutdown();
		while(!es.isTerminated()){}  //阻塞,确保所有线程结束后,才往下走
		
		System.out.println(System.currentTimeMillis()-start);
		
	}
}


//3.线程安全的集合
//3.1 Collections提供的安全集合

//Collections工具类中提供了针对List,Set,Map的安全集合
//当然他们的安全机制性能方面有比较大的影响,一般我们会用后续并发安全集合

public class Test1 {
	public static void main(String[] args) {
		List<Integer> list = Collections.synchronizedList(new ArrayList<Integer>());
		
		//多个线程进入add方法
		list.add(2);
	}
}


//3.2 CopyOnWriteArrayList
//CopyOnWriteArrayList: ArrayList的安全集合(读写并发)
//内部进行了加锁,且性能方面比Collestions提供的安全集合更高
//该安全集合具有读写分离的特性
//读无锁,写有锁,读写之间不互斥,不阻塞,在内部拷贝了一份副本存数据
//往往也是用在读多写上的情况
public class Test2 {
	public static void main(String[] args) {
		List<String> list = new CopyOnWriteArrayList<String>();
		
		for(int i=0;i<5;i++) {
			final int temp = i;
			new Thread(new Runnable() {
				@Override
				public void run() {
					
					//T1 T2 T3
					//读写并发执行---执行效率很高
					list.add(Thread.currentThread().getName()+"-->"+temp);
					
					System.out.println(list);  //读
				}
			}).start();
		}
	}
}


//3.3 CopyOnWriteArraySet
//CopyOnWriteArraySet: 安全的set集合,底层实现通过CopyOnWriteArrayList
//该类也是读写分离的set集合类,该类具备存储唯一性
public class Test3 {
	public static void main(String[] args) {
		Set<String> set = new CopyOnWriteArraySet<String>();
		set.add("苹果");
		set.add("香蕉");
		set.add("苹果");
		System.out.println(set);
	}
}

  

猜你喜欢

转载自blog.csdn.net/m0_62718093/article/details/121147396
今日推荐