并发

66.同步访问共享的可变程序

读或写一个变量是原子的,除了long和double,读取一个非long或double类型的变量,可以保证返回的值是某个线程保存在该变量中的,即使多个线程在没有同步的情况下并发地修改这个变量也是如此

关键词synchronized可以保证同一时刻,只有线程可以执行某一种方法,或者一个代码块。

同步不仅可以阻止一个线程看到对象处于不一致的状态之中,还可以保证进入同步方法或者同步代码块的每一个线程,都看到由同一个锁保护的之前所有的修改效果

public class StopThread {
	private static boolean stopRequested;
	public static void main(String[] args) {
		Thread backgroundThread = new Thread(new Runnable() {
			
			@Override
			public void run() {
				int i = 0;
				while (!stopRequested) {
					i++;					
				}
			}
		});
		backgroundThread.start();
		stopRequested = true;
	}
}
这个程序永远不会停止,因为后台程序永远在循环

由于没有同步,不能保证后台线程何时看到主线程对stopRequested的值所做的改变,没有同步,虚拟机将这个代码转变成这样

	while(!done){
		i++;
	}
变成

	if(!done){
		while (true ) {
			i++;			
		}
	}
这种优化称为提升,结果是个活性失败

修正的一种方法是采用同步访问

public class StopThread {

	private static boolean stopRequested;
	private static synchronized void requestStop(){
		stopRequested = true;
	}
	private static synchronized boolean stopRequested(){
		return stopRequested;
	}
	public static void main(String[] args) throws Exception {
		Thread backgroundThread = new Thread(new Runnable() {
			
			@Override
			public void run() {
				int i = 0;
				while (!stopRequested()) {
					i++;					
				}
			}
		});
		backgroundThread.start();
		TimeUnit.SECONDS.sleep(1);
		requestStop();
	}
}
写方法(requestStop)和读方法(stopRequested)都被同步了,读和写没有都被同步,同步不会起作用

stopThread中被同步方法的动作即时没有同步也是原子性的,这些同步只是为了更好的通信,而不是互斥

可以stopRequested声明为volatile。虽然它不执行互斥访问,但可以保证任何一个线程在读取该域的时候都将看到最近被写入的值

public class StopThread {

	private static volatile boolean stopRequested;

	public static void main(String[] args) throws InterruptedException {
		Thread backgroundThread = new Thread(new Runnable() {
			
			@Override
			public void run() {
				int i = 0;
				while (!stopRequested) {
					i++;					
				}
			}
		});
		backgroundThread.start();
		TimeUnit.SECONDS.sleep(1);
		stopRequested =true;
	}
}

下面这个有问题,操作符++不是原子的,它先读取值,然后返回一个新值,相当于原来的值加上一,第二个线程在第一个线程读取旧值和写回新值期间读取这个域,第二个线程就会与第一个线程一起看到同一个值,并返回相同序列号,这就是安全性失败

	private static volatile int nextSerialNumber = 0;
	public static int generateSerialNumber(){
		return nextSerialNumber++;
	}
一种解决方法是,在它的声明中增加synchronized修饰符,确保多个调用不会较差存取,可以删除volatile,为了更可靠,用long代替int,或者在nextSerialNumber快要重叠时抛出异常

更好的做法是使用AtomicLong

	private static final AtomicLong nextSerialNum = new AtomicLong();
	public static long generateSerialNumber(){
		return nextSerialNum.getAndIncrement();
	}
不共享可变的数据,要么共享不可变的数据,将可变数据限制在单个线程中

当多个线程共享可变数据时,每个读或者写数据的线程都必须执行同步,如果只需要线程之间的交互通信,而不需要互斥,volatile就是一个可以接受的同步形式

67.避免过度同步
过度同步可能会导致性能下降,死锁,甚至不确定行为

将外来方法的调用移除同步的代码块

	private void notifyElementAdded(E element){
		List<SetObserver<E>> snapshot = null;
		synchronized (observers) {
			snapshot = new ArrayList<>();
		}
		for (SetObserver<E> setObserver : snapshot) {
			observers.added(this,element);
		}
	}

还可以使用CopyOnWriteArrayList,这是ArrayList的一种变体,通过重新拷贝整个底层数组,在这里实现所有的写操作,由于内部数组永远不改动,因此迭代不需要锁定,速度也快,大量使用,性能将大受影响

	private final List<SetObserver<E>> observers = new CopyOnWriteArrayList<>();
	public void addObserver(SetObserver<E> observer){
		observer.add(observer);
	}
68.executer和task优先于线程
	ExecutorService eService =Executors.newSingleThreadExecutor();
	eService.execute(runnable);
	eService.shutdown;
就是线程池

69.并发工具优先于wait和notify
70.线程安全性的文档化

71.慎用延迟初始化

延迟初始化降低了初始化类或者创建实例的开销,却增加了访问被延迟初始的域的开销

如果域只在类的实例部分被访问,并且初始化这个域的开销很高,可能就值得进行延迟初始化;如果俩个或多个线程共享一个延迟初始化的域,采用某种形式的同步是很重要的
正常初始化

private final FieldType field = computeFieldValue();

延迟初始化,要用到同步访问方法

	private FieldType field;
	synchronized FieldType getField(){
		if(field == null){
			field = computeFieldValue();
		}
		return field;
	}
出于性能的考虑需要对静态域使用延迟初始化,第一次读取时被初始化
	private static class FieldHolder{
		static final FieldType field = computeFieldValue();
	}
	static FieldType getField(){
		return FieldHolder.field;
	}
出于性能考虑需要对实例域使用延迟初始化,使用双重检查模式,这种模式避免了在域被初始化后访问这个域时的锁定开销,俩次检查域的值,第一次检查时没有锁定,看看这个域是否被初始化了,第二次检查有锁定,只有当第二次检查时表明这个域没有被初始化,才进行初始化,如果域已经被初始化就不会有锁定,result变量为了确保field只在已经被初始化的情况下读取一次,可以提高性能。
	private volatile FieldType field;
	FieldType getField(){
		FieldType result = field;
		if(result == null){
			synchronized (this) {
				result = field;
				if(result == null){
					field = result = computerFieldValue();
				}
			}
			return result;
		}
延迟初始化一个可以接受重复初始化的实例域,使用单冲检查模式

	private volatile FieldType field;
	private FieldType getField(){
		FieldType result = field;
		if(result == null){
			field = result = cpmputeFieldValue();
		}
		return result;
	}
不在意是否每个线程都重新计算域的值,并且域的类型为基本类型,而不是long或者double,可以从单重检查模式的域中删除volatile,加快了某些域上的域访问,增加了额外的初始化,(直到访问该域的每个线程都进行一次初始化)
72.不要依赖于线程调度器
任何依赖于线程调度器来达到正确性或者性能要求的程序,很可能都是不可移植的

确保可运行线程的平均数量不明显多于处理器的数量

保持可运行线程数量尽可能少的主要办法是,让每个线程做些有意义的工作,然后等待更多有意义的工作

过多的线程处于等待状态,性能会极大的降低

如果一个程序不能工作,是由于某些线程无法获得足够的cpu时间,不要用Thread.yield来修正,因为不可移植,更好的解决办法是重构应用程序,减少可并发运行的线程数量。

调整线程优先级不可移植,用它解决严重的活性问题不合理,在找到并修正底层的真正原因之前,这个问题可能还会出现

73.避免使用线程组


内容概要:本文针对国内加密货币市场预测研究较少的现状,采用BP神经网络构建了CCi30指数预测模型。研究选取2018年3月1日至2019年3月26日共391天的数据作为样本,通过“试凑法”确定最优隐结点数目,建立三层BP神经网络模型对CCi30指数收盘价进行预测。论文详细介绍了数据预处理、模型构建、训练及评估过程,包括数据归一化、特征工程、模型架构设计(如输入层、隐藏层、输出层)、模型编译与训练、模型评估(如RMSE、MAE计算)以及结果可视化。研究表明,该模型在短期内能较准确地预测指数变化趋势。此外,文章还讨论了隐层节点数的优化方法及其对预测性能的影响,并提出了若干改进建议,如引入更多技术指标、优化模型架构、尝试其他时序模型等。 适合人群:对加密货币市场预测感兴趣的研究人员、投资者及具备一定编程基础的数据分析师。 使用场景及目标:①为加密货币市场投资者提供一种新的预测工具和方法;②帮助研究人员理解BP神经网络在时间序列预测中的应用;③为后续研究提供改进方向,如数据增强、模型优化、特征工程等。 其他说明:尽管该模型在短期内表现出良好的预测性能,但仍存在一定局限性,如样本量较小、未考虑外部因素影响等。因此,在实际应用中需谨慎对待模型预测结果,并结合其他分析工具共同决策。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值