React Native特点
跨平台
使用js写出页面组件代码被React框架统一转成Virtual DOM树,Virtual DOM树是UI结构的一层抽象,可以被转换成任何支持端的UI视图。
ReactNative框架将Virtual DOM 转成原APP的UIView树。
热修复
ReactNative的产物bundle包,bundle包中包含的为RN业务所需要的所有资源,包括代码和资源。bundle的加载方式是APP启动时从后台下载,然后通过js虚拟机执行的。
所以可以将每次业务迭代修改后的代码上传到服务,进行用户无感知版本更新。
注意:
bundle中的业务代码不能修改APP现有的原生行为,不能调用私有API,不然禁止上架。
bundle包中的js是经过babel转义后的普通js,而非jsx语法糖。

JS与Native交互的基本原理
JS引擎
iOS侧使用的JavaScriptCore作为bundle产物的js执行引擎。
JS与Native交互的基本原理很简单,就是在JS的全局上下文添加成员变量。原生调用JS是JS在JS上下文中添加方法成员变量,然后原生调用。JS调用原生是原生往JS上下文中添加方法成员变量,然后JS调用。
JS调用原生
通过将block对象赋值给js全局上下文中的全局变量,js内部调用这个全局方法进行执行。
ctx[@"NativeMethod"] = ^(NSString *name) {
// do something
return someResult
}
原生调用JS
先创建一个JS上下文对象,在上下文中添加方法的全局变量。原生获取上下文的全局变量Value, 然后调用,执行这个JS方法。
// 创建一个ctx的JS上下文
JSContent *ctx = [[JSContent alloc] init];
// 创建一个变量name
[ctx evaluateScript:@"var name = 'jack'"];
// 创建一个方法
[ctx evaluateScript:@"var sayHi = function(name) { return 'hello ' + name }"]; // 通过ctx上下文对象,获取到hello方法
JSValue *sayHiUnction = ctx[@"sayHi"];
// 运行js方法
JSValue *greetings = [sayHiUnction callWithArguments:@[@"jack"]; // hello jack
ReactNative框架中原生与JS的调用基本思路也是这种,只不过考虑到大量的Native对象注册会污染js引擎中的上下文,增加了一层Bridge。
原生和JS之间的交互都是通过Bridge这个通道,通过里面的几个基础方法进行交互。原生与JS的交互是异步的。
另外,Facebook为了提升RN框架中JS的执行效率,专门推出了一个JS引擎 Hermes, 在关键指标上,相比于JSCore,V8提升了不少,比较易于RN框架集成。

ReactNative核心知识

RCTBridge: ReactNative中原生与JS交互的通道
RCTBridge用于给js引擎提供原生扩展接口。将原生功能如定位,3D等通过Bridge将其封装成JS接口,然后注入到js引擎的上下文中。
RN框架启动的简单流程为:首先将js代码加载到内存,然后创建RCTBridge实例,然后创建RCTRootContentView内容展示的容器视图,然后调用JS上下文中AppRegistry对象的runApplication方法,并将@[moduleName, appParameters]组件名,参数传递给JS。
// RCTRootView.m
- (void)javaScriptDidLoad:(NSNotification *)notification
{
RCTAssertMainQueue();
RCTBridge *bridge = notification.userInfo[@"bridge"];
if (bridge != _contentView.bridge) {
[self bundleFinishedLoading:bridge];
}
} - (void)bundleFinishedLoading:(RCTBridge *)bridge
{
// 省略创建RCTRootContentView... [self runApplication:bridge]; // 省略添加一个RCTRootContentView...
} - (void)runApplication:(RCTBridge *)bridge
{
NSString *moduleName = _moduleName ?: @""; // 这里是@"NewProject"
NSDictionary *appParameters = @{
@"rootTag": _contentView.reactTag,
@"initialProps": _appProperties ?: @{},
}; [bridge enqueueJSCall:@"AppRegistry" method:@"runApplication" args:@[moduleName, appParameters]
completion:NULL];
}
 
原生调用JS
在JS上下文中,调用JS的方式是通过方法:global.batchedBridge.callFunctionReturnFlushedQueue

所以RN在原生侧的的JS引擎的封装对象中使用成员变量保存了这个JS的函数指针,原生调用JS时,通过传递参数 moduleid 和 methodid 完成方法的调用。
void JSIExecutor::bindBridge() {
std::call_once(bindFlag_, [this] {
SystraceSection s("JSIExecutor::bindBridge (once)");
Value batchedBridgeValue =
runtime_->global().getProperty(*runtime_, "__fbBatchedBridge");
if (batchedBridgeValue.isUndefined() || !batchedBridgeValue.isObject()) {
throw JSINativeException(
"Could not get BatchedBridge, make sure your bundle is packaged correctly");
} Object batchedBridge = batchedBridgeValue.asObject(*runtime_);
callFunctionReturnFlushedQueue_ = batchedBridge.getPropertyAsFunction(
*runtime_, "callFunctionReturnFlushedQueue");
invokeCallbackAndReturnFlushedQueue_ = batchedBridge.getPropertyAsFunction(
*runtime_, "invokeCallbackAndReturnFlushedQueue");
flushedQueue_ =
batchedBridge.getPropertyAsFunction(*runtime_, "flushedQueue");
});
}
 
JS调用原生
JS调用原生通常是通过原生主动处理_eventQueue中的事件,特殊情况会直接调用原生注册给JS的nativeFlushQueueImmediate方法, 并传递moduleName 、methodName、callback 参数给这个方法完成调用。

void JSIExecutor::initializeRuntime() {
SystraceSection s("JSIExecutor::initializeRuntime");
runtime_->global().setProperty(
*runtime_,
"nativeModuleProxy",
Object::createFromHostObject(
*runtime_, std::make_shared<NativeModuleProxy>(nativeModules_))); runtime_->global().setProperty(
*runtime_,
"nativeFlushQueueImmediate",
Function::createFromHostFunction(
*runtime_,
PropNameID::forAscii(*runtime_, "nativeFlushQueueImmediate"),
1,
[this](
jsi::Runtime &,
const jsi::Value &,
const jsi::Value *args,
size_t count) {
if (count != 1) {
throw std::invalid_argument(
"nativeFlushQueueImmediate arg count must be 1");
}
callNativeModules(args[0], false);
return Value::undefined();
})); runtime_->global().setProperty(
*runtime_,
"nativeCallSyncHook",
Function::createFromHostFunction(
*runtime_,
PropNameID::forAscii(*runtime_, "nativeCallSyncHook"),
1,
[this](
jsi::Runtime &,
const jsi::Value &,
const jsi::Value *args,
size_t count) { return nativeCallSyncHook(args, count); })); runtime_->global().setProperty(
*runtime_,
"globalEvalWithSourceUrl",
Function::createFromHostFunction(
*runtime_,
PropNameID::forAscii(*runtime_, "globalEvalWithSourceUrl"),
1,
[this](
jsi::Runtime &,
const jsi::Value &,
const jsi::Value *args,
size_t count) { return globalEvalWithSourceUrl(args, count); })); if (runtimeInstaller_) {
runtimeInstaller_(*runtime_);
}
bool hasLogger(ReactMarker::logTaggedMarker);
if (hasLogger) {
ReactMarker::logMarker(ReactMarker::CREATE_REACT_CONTEXT_STOP);
}
}
Virtual DOM 虚拟DOM
 
虚拟DOM的特点
1.用于描述页面的UI结构:在作用上虚拟DOM和普通的DOM是一样的。
2.平台无关性:虚拟DOM表示的UI结构是对UI的一层抽象,它是平台无关的。具体的UI渲染是交个具体的平台渲染引擎进行的,如iOS,安卓自身的渲染引擎。
虚拟DOM对标签的定义
虚拟DOM把标签分为2类:原子型标签,组合型标签。
原子型标签是平台支持型的基础标签,如果RCTView, RCTText。对应浏览器页面中,原子型标签有h1,li,div等。
组合型标签是用户自定义的组件,它在虚拟DOM中对应的是自定义标签构造器函数,页面渲染时调用这个构造函数,创建一个实例,然后调用实例的render方法,组合型标签的render方法内会把组合标签进行拆解,最后拆解成基本的原子型标签。
var ele = {
...
type: type, // 元素的类型
key: key, // 元素key标示
ref: ref, // 元素的引用
props: props, // 元素的参数,包含children
...
} // example 1
<div>hello</div>
// 会被描述为 {type: 'div',
props: {
children: ['hello']
}
} // example 2
<CustomerComponents />
// 会被描述为
{
type: CustomerComponents
}
 
UI渲染
RN框架与浏览器的对比:在浏览器中,JS通过调用DOM API创建UI视图。在RN中,JS通过调用RCTUIManager来创建iOS,Android移动端的UI视图。
RN的UI渲染是基于虚拟DOM的,通过根据不同的平台调用不同平台的Bridge, Brideg再调用不同平台的的RCTUIManager进行UI的创建。
 

其他

三条线程
RN内部有三条线程在同时运行着:Shadow Thread, JS Thread, UI Thread。
JS Thread:JS线程,负责JS与原生的交互,它们的交互是异步的,每次调用都是将block放入队列中,等js代码执行完后,读取事件队列进行处理。
UI Thread:UI主线程,负责页面的交互与渲染, 由RCTUIManager使用。
Shadow Thread: 负责将flex布局转成Native的布局,由yago引擎使用。
三个队列
RN框架内,原生与JS的交换类型分两种:UI和事件,这2这种事件的处理都是异步的,它们都是将事件顺序放置到队列中,在合适的时机被调用。
事件的处理在RCTBridge中处理,UI的处理在RCTUIManager中处理。
JS调用原生异步事件队列:_eventQueue队列
原生调用JS异步事件队列:_pendingCalls队列
UI更新异步事件处理队列:_pendingUIBlocks队列
JSI
javascript interface js虚拟机通用接口层,是针对JS引擎封装的上层API框架,使用JSI做JS引擎调用的优点:
1.底部可以任意替换JS引擎而不影响上层JS引擎的使用。如:可以任意替换JavaScript Core, V8等。
2.通过JSI,JavaScript可以持有C++宿主对象的引用,所以可以直接调用原生方法(UIView, NativeModule),它与现在统一使用Bridge这个通道和消息异步调用比起来,提高了消息发送的及时性,避免了消息队列执行的等待。
React Native核心知识在框架中的使用
 
React Native核心功能在RN项目启动时会进行各自的初始化,生成bundle运行上下文。在类型上可以分为2类:
1.JS与原生的事件处理:创建RCTBridge桥接通道。
2.UI交互与更新的事件处理:创建RCTRootView容器视图。
APP启动,React Native运行环境初始化。
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
if (!self.bridge) {
self.bridge = [self createBridgeWithDelegate:self launchOptions:launchOptions];
} NSDictionary *initProps = [self prepareInitialProps];
UIView *rootView = [self createRootViewWithBridge:self.bridge moduleName:self.moduleName initProps:initProps]; if (@available(iOS 13.0, *)) {
rootView.backgroundColor = [UIColor systemBackgroundColor];
} else {
rootView.backgroundColor = [UIColor whiteColor];
} self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];
UIViewController *rootViewController = [self createRootViewController];
rootViewController.view = rootView;
self.window.rootViewController = rootViewController;
[self.window makeKeyAndVisible];
return YES;
}
 
JS与原生的事件处理:创建RCTBridge桥接通道
RCTBridge的主要逻辑是在batchedBridge中,主要初始化流程为:
1.初始化Native Modules
2.创建Native Modules配置表
3.准备JS引擎工厂,创建JS引擎
4.将Modules配置信息注册到JS引擎中
5.加载boundle代码
6.执行boundle代码
- (void)setUp
{
RCT_PROFILE_BEGIN_EVENT(0, @"-[RCTBridge setUp]", nil); //_performanceLogger日志工具初始化 //_bundleURL获取 //batchedBridge创建 self.batchedBridge = [[bridgeClass alloc] initWithParentBridge:self];
[self.batchedBridge start]; RCT_PROFILE_END_EVENT(RCTProfileTagAlways, @"");
} batchedBridge是RCTCXXBridge,它的初始化方法如下:
- (instancetype)initWithParentBridge:(RCTBridge *)bridge
{
RCTAssertParam(bridge); if ((self = [super initWithDelegate:bridge.delegate
bundleURL:bridge.bundleURL
moduleProvider:bridge.moduleProvider
launchOptions:bridge.launchOptions])) {
_parentBridge = bridge;
_performanceLogger = [bridge performanceLogger]; registerPerformanceLoggerHooks(_performanceLogger); /**
* Set Initial State
*/
_valid = YES;
_loading = YES;
_moduleRegistryCreated = NO;
_pendingCalls = [NSMutableArray new];
_displayLink = [RCTDisplayLink new];
_moduleDataByName = [NSMutableDictionary new];
_moduleClassesByID = [NSMutableArray new];
_moduleDataByID = [NSMutableArray new];
_objCModuleRegistry = [RCTModuleRegistry new];
[_objCModuleRegistry setBridge:self];
_bundleManager = [RCTBundleManager new];
[_bundleManager setBridge:self];
_viewRegistry_DEPRECATED = [RCTViewRegistry new];
[_viewRegistry_DEPRECATED setBridge:self];
_callableJSModules = [RCTCallableJSModules new];
[_callableJSModules setBridge:self]; [RCTBridge setCurrentBridge:self]; [[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(handleMemoryWarning)
name:UIApplicationDidReceiveMemoryWarningNotification
object:nil]; RCTLogSetBridgeModuleRegistry(_objCModuleRegistry);
RCTLogSetBridgeCallableJSModules(_callableJSModules);
}
return self;
} - (void)start
{
RCT_PROFILE_BEGIN_EVENT(RCTProfileTagAlways, @"-[RCTCxxBridge start]", nil); [[NSNotificationCenter defaultCenter] postNotificationName:RCTJavaScriptWillStartLoadingNotification
object:_parentBridge
userInfo:@{@"bridge" : self}]; //启动JS线程
_jsThread = [[NSThread alloc] initWithTarget:[self class] selector:@selector(runRunLoop) object:nil];
_jsThread.name = RCTJSThreadName;
_jsThread.qualityOfService = NSOperationQualityOfServiceUserInteractive;
#if RCT_DEBUG
_jsThread.stackSize *= 2;
#endif
[_jsThread start]; dispatch_group_t prepareBridge = dispatch_group_create(); [_performanceLogger markStartForTag:RCTPLNativeModuleInit]; //1.初始化Native Modules
[self registerExtraModules];
//2.创建Native Modules配置表
// Initialize all native modules that cannot be loaded lazily
(void)[self _initializeModules:RCTGetModuleClasses() withDispatchGroup:prepareBridge lazilyDiscovered:NO];
[self registerExtraLazyModules]; [_performanceLogger markStopForTag:RCTPLNativeModuleInit]; // This doesnt really do anything. The real work happens in initializeBridge.
_reactInstance.reset(new Instance); __weak RCTCxxBridge *weakSelf = self; // 3.准备JS引擎工厂,创建JS引擎
std::shared_ptr<JSExecutorFactory> executorFactory;
if (!self.executorClass) {
if ([self.delegate conformsToProtocol:@protocol(RCTCxxBridgeDelegate)]) {
id<RCTCxxBridgeDelegate> cxxDelegate = (id<RCTCxxBridgeDelegate>)self.delegate;
executorFactory = [cxxDelegate jsExecutorFactoryForBridge:self];
}
// 4.将Modules配置信息注册到JS引擎中
if (!executorFactory) {
auto installBindings = RCTJSIExecutorRuntimeInstaller(nullptr);
#if RCT_USE_HERMES
executorFactory = std::make_shared<HermesExecutorFactory>(installBindings);
#else
executorFactory = std::make_shared<JSCExecutorFactory>(installBindings);
#endif
}
} else {
id<RCTJavaScriptExecutor> objcExecutor = [self moduleForClass:self.executorClass];
executorFactory.reset(new RCTObjcExecutorFactory(objcExecutor, ^(NSError *error) {
if (error) {
[weakSelf handleError:error];
}
}));
} //_turboModuleRegistry是一个TurboModule注册表,TurboModule是JS在RN中的一种优化方式,将常用的JS代码编译成可执行代码,提高执行速度。
/**
* id<RCTCxxBridgeDelegate> jsExecutorFactory may create and assign an id<RCTTurboModuleRegistry> object to
* RCTCxxBridge If id<RCTTurboModuleRegistry> is assigned by this time, eagerly initialize all TurboModules
*/
if (_turboModuleRegistry && RCTTurboModuleEagerInitEnabled()) {
for (NSString *moduleName in [_turboModuleRegistry eagerInitModuleNames]) {
[_turboModuleRegistry moduleForName:[moduleName UTF8String]];
} for (NSString *moduleName in [_turboModuleRegistry eagerInitMainQueueModuleNames]) {
if (RCTIsMainQueue()) {
[_turboModuleRegistry moduleForName:[moduleName UTF8String]];
} else {
id<RCTTurboModuleRegistry> turboModuleRegistry = _turboModuleRegistry;
dispatch_group_async(prepareBridge, dispatch_get_main_queue(), ^{
[turboModuleRegistry moduleForName:[moduleName UTF8String]];
});
}
}
} // Dispatch the instance initialization as soon as the initial module metadata has
// been collected (see initModules)
dispatch_group_enter(prepareBridge);
[self ensureOnJavaScriptThread:^{
[weakSelf _initializeBridge:executorFactory];
dispatch_group_leave(prepareBridge);
}]; // 5.加载boundle代码
// Load the source asynchronously, then store it for later execution.
dispatch_group_enter(prepareBridge);
__block NSData *sourceCode;
[self
loadSource:^(NSError *error, RCTSource *source) {
if (error) {
[weakSelf handleError:error];
} sourceCode = source.data;
dispatch_group_leave(prepareBridge);
}
onProgress:^(RCTLoadingProgress *progressData) { }]; // 模块和js代码加载完成后,执行js代码
// Wait for both the modules and source code to have finished loading
dispatch_group_notify(prepareBridge, dispatch_get_global_queue(QOS_CLASS_USER_INTERACTIVE, 0), ^{
RCTCxxBridge *strongSelf = weakSelf;
if (sourceCode && strongSelf.loading) {
// 6.执行boundle代码
[strongSelf executeSourceCode:sourceCode sync:NO];
}
});
RCT_PROFILE_END_EVENT(RCTProfileTagAlways, @"");
}
 
初始化Native Modules与创建Native Modules配置表
把本地的RN模块都收集起来,包括RN框架自带的和用户自定义的,将模块信息保存到Bridge的变量中,用于与JS交换。
_moduleDataByName = [NSMutableDictionary new];
_moduleClassesByID = [NSMutableArray new];
_moduleDataByID = [NSMutableArray new];
 
JS发送消息到Native时,通过- (id)moduleForName:(const char *)moduleName;查询到模块详情,进行模块调用。
_objCModuleRegistry = [RCTModuleRegistry new];
[_objCModuleRegistry setBridge:self];
准备JS引擎工厂,创建JS引擎与将Modules配置信息注册到JS引擎中
RN将Native Modules信息收集完成后保存到成员变量中,这个成员变量是一个数组。使用moduleConfig保存模块的模块名,方法名。然后将这些数据注入到JS引擎中。
JS调用原生时,通过模块名,方法名,参数调用原生方法。在原生调用JS时,会将调用放入_pendingCalls队列中,进行异步执行。而JS调原生是将调用放入到_eventQueue队列中,进行异步执行。
JS可以通过方法nativeFlushQueueImmediate直接调用Native,但是一般JS不会这样做,而是等原生自己去_eventQueue队列中自己去取任务做处理。
// js thread only (which surprisingly can be the main thread, depends on used JS executor)
- (void)flushEventsQueue
{
[_eventQueueLock lock];
NSDictionary *events = _events;
_events = [NSMutableDictionary new];
NSMutableArray *eventQueue = _eventQueue;
_eventQueue = [NSMutableArray new];
_eventsDispatchScheduled = NO;
[_eventQueueLock unlock]; for (NSNumber *eventId in eventQueue) {
[self dispatchEvent:events[eventId]];
}
}
 
UI交互与更新的事件处理:创建RCTRootView容器视图
 
RCTRootView为RN页面的入口,在RCTRootView初始化过程中,会创建RCTRootContentView作为内容视图放置在RCTRootView的底部作为根视图。
RCTRootContentView的初始化方法中,在uiManager中将RCTRootContentView注册成根视图。
- (instancetype)initWithFrame:(CGRect)frame
bridge:(RCTBridge *)bridge
reactTag:(NSNumber *)reactTag
sizeFlexiblity:(RCTRootViewSizeFlexibility)sizeFlexibility
{
if ((self = [super initWithFrame:frame])) {
_bridge = bridge;
self.reactTag = reactTag;
_sizeFlexibility = sizeFlexibility;
_touchHandler = [[RCTTouchHandler alloc] initWithBridge:_bridge];
[_touchHandler attachToView:self];
[_bridge.uiManager registerRootView:self];
}
return self;
}
RCTUIManager是RN中UI的管理者,它负责处理所有与UI相关的事情,如:注入到JS中的创建View方法。
在createView方法中可以看到,RN对View的操作都是双份的,分别作用在RCTShadowView和UIView上。RCTShadowView和UIView的关系类似于虚拟DOM和DOM的关系。
RCTShadowView是一个虚拟DOM树,是一个结构体,用于描述视图的样式和事件,比较轻量级。
在RN中当调用setState更新组件状态时,就会生成一个新的虚拟DOM,然后RN将新的虚拟DOM与旧的虚拟DOM进行Diff对比,生成差异对象,然后遍历差异对象,将所有的改动更新到UI上。而更新到了Native是先更新到RCTShadowView上,等合适的时候再统一更新到UI上。
UI更新操作也是异步的,更新任务被放置在_pendingUIBlocks队列上,在UI变化时或Bridge批出来结束时刷新这个队列。
RCT_EXPORT_METHOD(createView
: (nonnull NSNumber *)reactTag viewName
: (NSString *)viewName rootTag
: (nonnull NSNumber *)rootTag props
: (NSDictionary *)props)
{
RCTComponentData *componentData = _componentDataByName[viewName];
if (componentData == nil) {
RCTLogError(@"No component found for view with name \"%@\"", viewName);
} // Register shadow view
RCTShadowView *shadowView = [componentData createShadowViewWithTag:reactTag];
if (shadowView) {
[componentData setProps:props forShadowView:shadowView];
_shadowViewRegistry[reactTag] = shadowView;
RCTShadowView *rootView = _shadowViewRegistry[rootTag];
RCTAssert(
[rootView isKindOfClass:[RCTRootShadowView class]] || [rootView isKindOfClass:[RCTSurfaceRootShadowView class]],
@"Given `rootTag` (%@) does not correspond to a valid root shadow view instance.",
rootTag);
shadowView.rootView = (RCTRootShadowView *)rootView;
} // Dispatch view creation directly to the main thread instead of adding to
// UIBlocks array. This way, it doesnt get deferred until after layout.
__block UIView *preliminaryCreatedView = nil; void (^createViewBlock)(void) = ^{
// Do nothing on the second run.
if (preliminaryCreatedView) {
return;
} preliminaryCreatedView = [componentData createViewWithTag:reactTag rootTag:rootTag]; if (preliminaryCreatedView) {
self->_viewRegistry[reactTag] = preliminaryCreatedView;
}
}; // We cannot guarantee that asynchronously scheduled block will be executed
// *before* a block is added to the regular mounting process (simply because
// mounting process can be managed externally while the main queue is
// locked).
// So, we positively dispatch it asynchronously and double check inside
// the regular mounting block. RCTExecuteOnMainQueue(createViewBlock); [self addUIBlock:^(__unused RCTUIManager *uiManager, __unused NSDictionary<NSNumber *, UIView *> *viewRegistry) {
createViewBlock(); if (preliminaryCreatedView) {
[componentData setProps:props forView:preliminaryCreatedView];
}
}]; [self _shadowView:shadowView didReceiveUpdatedProps:[props allKeys]];
}
参考文章
https://juejin.cn/post/6916452544956858382#heading-11
https://juejin.cn/post/6844904184542822408
https://juejin.cn/post/6844904184500715527

ReactNative原理与核心知识点的更多相关文章

  1. Java核心知识点学习----线程中如何创建锁和使用锁 Lock,设计一个缓存系统

    理论知识很枯燥,但这些都是基本功,学完可能会忘,但等用的时候,会发觉之前的学习是非常有意义的,学习线程就是这样子的. 1.如何创建锁? Lock lock = new ReentrantLock(); ...

  2. Java核心知识点 --- 线程中如何创建锁和使用锁 Lock , 设计一个缓存系统

    理论知识很枯燥,但这些都是基本功,学完可能会忘,但等用的时候,会发觉之前的学习是非常有意义的,学习线程就是这样子的. 1.如何创建锁? Lock lock = new ReentrantLock(); ...

  3. jquery-2 jQuery原理和核心方法(多看学习视频)

    jquery-2  jQuery原理和核心方法(多看学习视频) 一.总结 一句话总结:jQuery就是普通的js对象,只不过方法比较多而已,属性就length一个. 1.jquery的链式操作的底层原 ...

  4. Spring Boot 自动配置的原理、核心注解以及利用自动配置实现了自定义 Starter 组件

    本章内容 自定义属性快速入门 外化配置 自动配置 自定义创建 Starter 组件 摘录:读书是读完这些文字还要好好用心去想想,写书也一样,做任何事也一样 图 2 第二章目录结构图 第 2 章 Spr ...

  5. Canal:同步mysql增量数据工具,一篇详解核心知识点

    老刘是一名即将找工作的研二学生,写博客一方面是总结大数据开发的知识点,一方面是希望能够帮助伙伴让自学从此不求人.由于老刘是自学大数据开发,博客中肯定会存在一些不足,还希望大家能够批评指正,让我们一起进 ...

  6. Redis 面霸篇:高频问题横扫核心知识点

    「码哥字节」从高频面试问题跟大家一起横扫 Redis 核心知识点,从根本上理解 Redis ,不做八股文的工具人,做扭转乾坤的大神. 码哥到如今已经写了 9 篇 Redis 连载,后台有小伙伴也让我写 ...

  7. javascript中的一些核心知识点以及需要注意的地方

    前言 近期杂事甚多,这些事情的积累对知识体系的提升有好处,但是却不能整理出来,也整理不出来 比如说我最近研究的Hybrid在线联调方案便过于依赖于业务,就算分享也不会有人读懂,若是抽一点来分享又意义不 ...

  8. Java核心知识点学习----多线程中的阻塞队列,ArrayBlockingQueue介绍

    1.什么是阻塞队列? 所谓队列,遵循的是先进先出原则(FIFO),阻塞队列,即是数据共享时,A在写数据时,B想读同一数据,那么就将发生阻塞了. 看一下线程的四种状态,首先是新创建一个线程,然后,通过s ...

  9. Java核心知识点学习----使用Condition控制线程通信

    一.需求 实现线程间的通信,主线程循环3次后,子线程2循环2次,子线程3循环3次,然后主线程接着循环3次,如此循环3次. 即:A->B->C---A->B->C---A-> ...

  10. Hbase的架构原理、核心概念

    Hbase的架构原理.核心概念 1.Hbase的表.行.列.列族 2.核心组件: Table和region Table在行的方向上分割为多个HRegion, 一个region由[startkey,en ...

随机推荐

  1. JsonCpp JSON格式处理库的介绍和使用(面向业务编程-文件格式处理)

    JsonCpp JSON格式处理库的介绍和使用(面向业务编程-文件格式处理) 介绍 JSON是一种轻量级的数据交换格式,它是一种键值对的集合.它的值可以是数字.字符串.布尔值.序列. 想知道更多有关J ...

  2. ARM 详解

    一.ARM 的发展史 ARM 的发展历史可以追溯到 1978 年,当年克里斯·库里(Chris Currry)所任职的公司遭遇财务危机,发展每况愈下,库里在和创始人深入沟通后,决定离职.当时的库里对微 ...

  3. [MyBatis]MyBatis系列:模糊查询的4种实现方式【待完善】

    背景 客户现网遇到的1个子问题. 方案 LIKE + Concat(strA, strB) ... 参考文献 MyBatis系列:模糊查询的4种实现方式

  4. [Java]排序算法>交换排序>【快速排序】(O(N*logN)/不稳定/N较大/无序/仅顺序存储)

    1 快速排序 1.1 算法思想 快速排序是由冒泡排序改进而得的. 在冒泡排序过程中,只对相邻的2个记录进行比较:因此,每次交换2个相邻记录时,只能消除1个逆序. 若能通过2个(不相邻)记录的1次交换, ...

  5. 前端里那些你不知道的事儿之 【window.onload】

    作者:京东科技 孙凯 一.前言 相信很多前端开发者在做项目时同时也都做过页面性能优化,这不单是前端的必备职业技能,也是考验一个前端基础是否扎实的考点,而性能指标也通常是每一个开发者的绩效之一.尤其马上 ...

  6. 【Note】倍增

    真的不会.QAQ 目录 简介 大家都见过的应用:倍增求 \(\text{LCA}\) 倍增求 \(\text{LCA}\) ,但是动态加点,但是不会 \(lct\) 例题:[ZJOI2012]灾难(D ...

  7. React+Antd在使用form表单提交DatePicker日期框的时候会出现少八小时的情况

    在使用antd做form表单提交的时候,突然发现了一个很有意思的bug.就是在使用datepicker组件日期框的时候会出现提交后少一天的问题 我在网上搜索了许多解决办法,也是困扰了我一天的时间,下面 ...

  8. 【Visual Leak Detector】库的 22 个 API 使用说明

    说明 使用 VLD 内存泄漏检测工具辅助开发时整理的学习笔记.本篇主要介绍 VLD 库提供的 22 个外部接口.同系列文章目录可见 <内存泄漏检测工具>目录 目录 说明 1. 头文件简介 ...

  9. MySQL 中常见的几种高可用架构部署方案

    MySQL 中的集群部署方案 前言 MySQL Replication InnoDB Cluster InnoDB ClusterSet InnoDB ReplicaSet MMM MHA Galer ...

  10. 开心档之MySQL 复制表

    MySQL 复制表 如果我们需要完全的复制MySQL的数据表,包括表的结构,索引,默认值等. 如果仅仅使用CREATE TABLE ... SELECT命令,是无法实现的. 本章节将为大家介绍如何完整 ...