线程池、线程通信代码,记忆:信号量,Lock等

3 篇文章 0 订阅
1 篇文章 0 订阅

线程通讯: wait - notify -notifyAll

信号量:Semaphore

线程池:
死锁:

线程池调度器: Executors ExecutorService Executor

Lock :ReentrantLock

ThreadLocal

原子操作类等

一:线程通信:wait - notify -notifyAll
两个线程:一个收鸡蛋,一个母鸡下蛋

public class Test {

	private boolean hasEggs = false;// 现在没有鸡蛋
	private byte[] lock = new byte[0];// 最小的锁

	/*
	 * 有个人的线程
	 */
	Thread manT = new Thread(new Runnable() {
		public void run() {
			while (true) {
				if (hasEggs) {
					System.out.println("可以收鸡蛋了。(^∀^)");
					hasEggs = false;
				} else {
					System.out.println("等待,收取鸡蛋");
					try {
						//通过锁住这个资源来
						synchronized (lock) {
							lock.wait();
							//wait,把资源放开,等待线程的唤醒
							//sleep不放开资源,只是睡多少时间的问题
						}
					} catch (InterruptedException e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					}			
				}
			}
		}
	});

	Thread geGeda = new Thread(new Runnable() {
		public void run() {
			while (true) {
				try {
					Thread.sleep(200);
					System.out.println("母鸡下蛋,=v=");
					hasEggs = true;
//						锁住资源
					synchronized (lock) {
						lock.notify();
						//唤醒被沉睡的资源,也就是lock.wait的资源
						//还有一个notifyall的提醒机制,提示所有被wait 的来获取锁
					}
				} catch (InterruptedException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}
		}
	});
/*
 * 睡几秒,把Thread.sleep睡觉这个方法提出来
 */
	private void sleep(int m) {
		try {
			TimeUnit.MILLISECONDS.sleep(m);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
	}
	private void startall() {
		geGeda.start();
		manT.start();
	}
	public static void main(String[] args) {
		new Test().startall();
	}}

笔记提取:
//wait,把资源放开,等待线程的唤醒
//sleep不放开资源,只是睡多少时间的问题
private byte[] lock = new byte[0];// 最小的锁
synchronized (lock) { 被锁住的资源 }:这是一个锁

lock.notify(); //唤醒被沉睡的资源,也就是lock.wait的资源
//还有一个notifyall的提醒机制,提示所有被wait 的来获取锁(容易又引起资源的争抢)

线程池:

public class Task {
public static void main(String[] args) {
	try {
		fix();// 重用固定线程数的线程池
		sch();// 安排在给定延迟后运行命令或者定期地执行
	} catch (InterruptedException e) {
		// TODO Auto-generated catch block
		e.printStackTrace();
	} catch (ExecutionException e) {
		e.printStackTrace();
	}

}

/*
 * 安排在给定延迟后运行命令或者定期地执行
 */
private static void sch() throws InterruptedException, ExecutionException {
	ExecutorService newPool = Executors.newScheduledThreadPool(5);
	// 安排在给定延迟后运行命令或者定期地执行
	Set<Runnable> taskSet = new HashSet();

	taskSet.add(() -> {
		for (int i = 0; i < 5; i++) {
			String name = Thread.currentThread().getName();
			System.out.println(name + "干活yi" + i);
		}

	});
	taskSet.add(() -> {
		for (int i = 0; i < 5; i++) {
			String name = Thread.currentThread().getName();
			System.out.println(name + "干活er" + i);
		}

	});
	taskSet.add(() -> {
		for (int i = 0; i < 5; i++) {
			String name = Thread.currentThread().getName();
			System.out.println(name + "干活san" + i);
		}

	});

	newPool.shutdown();
}

/*
 * 重用固定线程数的线程池
 */
private static void fix() throws InterruptedException {
	ExecutorService newPool = Executors.newFixedThreadPool(5);// 重用固定线程数的线程池

	// 五个任务
	Set<Callable<Integer>> taskSet = new HashSet<>();

	taskSet.add(() -> {

		String name = Thread.currentThread().getName();
		for (int i = 0; i < 10; i++) {
			System.out.println(name + "做任务yi" + i);
		}
		return 1;
	});
	taskSet.add(() -> {

		String name = Thread.currentThread().getName();
		for (int i = 0; i < 10; i++) {
			System.out.println(name + "做任务er" + i);
		}
		return 2;
	});
	taskSet.add(() -> {

		String name = Thread.currentThread().getName();
		for (int i = 0; i < 10; i++) {
			System.out.println(name + "做任务san" + i);
		}
		return 3;
	});
	List<Future<Integer>> invokeAll = newPool.invokeAll(taskSet);
	for (Future<Integer> future : invokeAll) {
		System.out.println(future);
	}
	newPool.shutdown();

}
}

笔记提取:

newScheduledThreadPool,安排在给定延迟后运行命令或者定期地执行
newFixedThreadPool重用固定线程数的线程池
/*
* 通过上面的程序过程(他们的区别),知道callable是有参数返回的,而runnable是没有参数返回
* callable实现call方法,runnable实现run方法
* 最好不要重复的去使用runnable,容易什么都不出来,现在他已经死了,用不起了(╥╯^╰╥),其实以前还能用的
    */
 invokeAll():
会调用存在于参数集合中的所有 Callable 对象,并且返回一个包含Future对象的集合,可以通过这个返回的集合来管理每个Callable的执行结果
需要注意的是,任务有可能因为异常而导致运行结束,所以它可能并不是真的成功运行了。但是我们没有办法通过 Future 对象来了解到这个差异

背诵:

•线程池主要用来解决线程生命周期开销问题和资源不足问题。通过对多个任务重复使用线程,线程创建的开销就被分摊到了多个任务上了,而且由于在请求到达时线程已经存在,所以消除了线程创建所带来的延迟。这样,就可以立即为请求服务,使用应用程序响应更快

自己理解:

线程池主要解决线程的生命周期开销和资源不足的问题。通过创建多个线程,使多个任务重复的使用,这几个被创建的线程,这样创建线程的开销被分摊到各个任务中来。由于请求到达,线程已经被创建好,可以消除线程所带来的延迟,这样,立即处理请求,使响应程序会更快。

死锁:
每个 线程都使用一点资源,此时资源已经用完,等另外的资源的释放,一直使资源耗尽

public class Philosopher extends Thread {
//死锁
	private String x1;
	private String x2;
	private int idx;

	public Philosopher(String x1, String x2, int idx) {
		this.x1 = x1;
		this.x2 = x2;
		this.idx = idx;
	}

	public void run() {
		if (idx % 2 == 1) {
			synchronized (x1) {
				System.out.println("A x1锁定");
				sleep();
				System.out.println("A 尝试获取x1的锁");
				try {
					x1.wait();
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
				synchronized (x2) {
					System.out.println("A x2锁定");
					sleep();
				}
			}
		} else

		{
			synchronized (x2) {
				System.out.println("B x2锁定");
				sleep();
				System.out.println("B 尝试获取x1的锁");
				synchronized (x1) {
					System.out.println("B x1锁定");
					sleep();
					x1.notify();
				}
			}

		}
	}

	private void sleep() {
		try {
			Thread.sleep(1000);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
	}

}

信号量:

概念:进行资源协调调度的工具,保障多线程共享资源的可用性,保证它们能够正确、合理的使用公共资源。

怎么用:一个计数信号量。维护了一个许可集。在许可可用前会阻塞每一个资源申请请求,然后再获取该许可。信号量提供一个方法添加一个许可,从而可能释放一个正在阻塞的获取者。信号量对可用许可进行计数,并采取相应的行动。拿到信号量许可的线程可以进入代码,否则就等待。通过申请和释放方法获取和释放访问许可。

生产者消费者通常用型号量的方式来处理

背诵:

•信号量,有时被称为信号灯,是在多线程环境下使用的一种设施, 它负责协调各个线程, 以保证它们能够正确、合理的使用公共资源,一个计数信号量。从概念上讲,信号量维护了一个许可集。如有必要,在许可可用前会阻塞每一个资源申请请求,然后再获取该许可。信号量提供一个方法添加一个许可,从而可能释放一个正在阻塞的获取者

自己的话来说:

信号量有时候被称 信号灯 ,在 多线程 下,负责 调度 各个线程,保证 正确、合理使用 公共资源 ,用计量单位计数,从而可以释放 一群 正在 堵塞 的线程。拿到 信号量就可以 进入 被锁住 的代码块否则就等待。通过申请、释放的方法来获取和释放访问许可。

生产者-消费者

•生产者-消费者模式描述一块缓冲区作为仓库,生产者可以将产品放入仓库,消费者则可以从仓库中取走产品

Lock:锁对象

我们并不能直接看到在哪里加了锁,在哪里释放了锁,为了更清晰的表达如何加锁和释放锁,JDK5中提供了一个新的锁对象Lock(接口),•默认实现:ReentrantLock:

•公平锁:

先来一定先排队,一定先获取锁

•非公平锁:

不保证上述条件。非公平锁的吞吐量更高

重入锁被最近的一个成功lock的线程占有(unlock后释放)。该类有一个重要特性体现在构造器上,构造器接受一个可选参数,是否是公平锁,默认是非公平锁

代码:

Lock图片

ThreadLocal:共享的散列表(在同一个线程中共享一个变量)

用线程对象作为键,需要线程中共享的变量作为值保存起来,每次使用散列表.get(Thread.currentThread())的方式获取共享变量的本线程

作用:

1:解决多线程程序的并发问题

2:并不是一个Thread,而是Thread的局部变量,当使用ThreadLocal维护变量时,ThreadLocal为每个使用该变量的线程提供独立的变量副本,所以每个线程都可以独立地改变自己的副本,而不会影响其它线程所对应的副本

3:从线程的角度看,目标变量就象是线程的本地变量,这也是类名中“Local”所要表达的意思

方法: •void set(T value):将此线程局部变量的当前线程副本中的值设置为指定值

​ •void remove():移除此线程局部变量当前线程的值

​ •protected T initialValue():返回此线程局部变量的当前线程的“初始值”

​ •T get():返回此线程局部变量的当前线程副本中的值

原子操作类

概念:JDK5之后,Java提供了粒度更细、量级更轻,并且在多核处理器具有高性能的原子操作类。把竞争的范围缩小到单个变量上。

相当于泛化的volatile变量,能够支持原子读取-修改-写操作。

(例如:提供了get和set方法,这些volatile类型的变量在读取与写入上有着相同的内存语义。)

原子操作类在Java.util.concurrent.atomic包下,可以分为四种类型的原子更新类

​ •原子更新基本类型

​ •原子更新数组类型

​ •原子更新引用

​ •原子更新属性

使用原子方式更新基本类型

​ •AtomicBoolean:原子更新布尔变量

​ •AtomicInteger:原子更新整型变量

​ •AtomicLong:原子更新长整型变量

通过原子更新数组里的某个元素

​ •AtomicIntegerArray:原子更新整型数组的某个元素

​ •AtomicLongArray:原子更新长整型数组的某个元素

​ •AtomicReferenceArray:原子更新引用类型数组的某个元素

AtomicIntegerArray常用的方法有:

​ •int **addAndSet(int i,int **delta):以原子方式将输入值与数组中索引为i的元素相加

​ •boolean **compareAndSet **( int i, int expect, int update):如果当前值等于预期值,则以原子方式更新数组中索引为i的值为update值

需要更新引用类型往往涉及多个变量:

​ •AtomicReference:原子更新引用类型

​ •AtomicReferenceFieldUpdater:原子更新引用类型里的字段

​ •AtomicMarkableReference:原子更新带有标记位的引用类型

如果需要原子更新某个类某个字段,就需要用到原子更新字段类,可以使用以下几个类:

​ •AtomicIntegerFieldUpdater:原子更新整型字段

​ •AtomicLongFieldUpdater:原子更新长整型字段

​ •AtomicStampedReference:原子更新带有版本号的引用类型。

要想原子更新字段,需要两个步骤

​ •每次必须使用newUpdater创建一个更新器,并且需要设置想要更新的类的字段

​ •更新类的字段(属性)必须为public volatile

线程的工具类:(自己创建的 )
工作线程
1:创建时,wait.
2:当有任务时,唤醒(notify)线程,执行任务
3:任务完毕之后进入wait

线程的运行状态(生命周期):
1:新建 NEW:new创建线程对象
2:就绪 : 调用start方法后,得到了除CPU时间以外的所有资源
3:运行 : 获得CPU时间,进入运行状态
4:阻塞/等待 : 遇到sleep、yield、wait等等
5:结束 : 中断或者执行结束

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值