Atomic 类的原子操作是依赖java中的魔法类sun.misc.Unsafe来实现的,而这个类为我们提供了访问底层的机制,这种机制仅供java核心类库使用,而不应该被普通用户使用。

获取Unsafe的实例

查看Unsafe的源码我们会发现它提供了一个getUnsafe()的静态方法。

@CallerSensitive
public static Unsafe getUnsafe() {
Class var0 = Reflection.getCallerClass();
if (!VM.isSystemDomainLoader(var0.getClassLoader())) {
throw new SecurityException("Unsafe");
} else {
return theUnsafe;
}
}

如果直接调用这个方法会抛出一个SecurityException异常,这是因为Unsafe仅供java内部类使用,外部类不应该使用它。通过反射拿到它的实例

public class UnsafeTest {
public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException {
Field f = Unsafe.class.getDeclaredField("theUnsafe");
f.setAccessible(true);
Unsafe unsafe = (Unsafe) f.get(null);
}
}

使用Unsafe实例化一个类

假如我们有一个简单的类如下:

class User {
int age; public User() {
this.age = 10;
}
}

如果我们通过构造方法实例化这个类,age属性将会返回10。

User user1 = new User();
// 打印10
System.out.println(user1.age);

如果我们调用Unsafe来实例化呢?

User user2 = (User) unsafe.allocateInstance(User.class);
// 打印0
System.out.println(user2.age);

age将返回0,因为 Unsafe.allocateInstance() 只会给对象分配内存,并不会调用构造方法,所以这里只会返回int类型的默认值0。

修改私有字段的值

使用Unsafe的putXXX()方法,我们可以修改任意私有字段的值。

public class UnsafeTest {
public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException, InstantiationException {
Field f = Unsafe.class.getDeclaredField("theUnsafe");
f.setAccessible(true);
Unsafe unsafe = (Unsafe) f.get(null); User user = new User();
Field age = user.getClass().getDeclaredField("age");
unsafe.putInt(user, unsafe.objectFieldOffset(age), 20); // 打印20
System.out.println(user.getAge());
}
} class User {
private int age; public User() {
this.age = 10;
} public int getAge() {
return age;
}
}

一旦我们通过反射调用得到字段age,我们就可以使用Unsafe将其值更改为任何其他int值。(当然,这里也可以通过反射直接修改)

抛出checked异常

我们知道如果代码抛出了checked异常,要不就使用try...catch捕获它,要不就在方法签名上定义这个异常,但是,通过Unsafe我们可以抛出一个checked异常,同时却不用捕获或在方法签名上定义它。

// 使用正常方式抛出IOException需要定义在方法签名上往外抛
public static void readFile() throws IOException {
throw new IOException();
}
// 使用Unsafe抛出异常不需要定义在方法签名上往外抛
public static void readFileUnsafe() {
unsafe.throwException(new IOException());
}

使用堆外内存

如果进程在运行过程中JVM上的内存不足了,会导致频繁的进行GC。理想情况下,我们可以考虑使用堆外内存,这是一块不受JVM管理的内存。

使用Unsafe的allocateMemory()我们可以直接在堆外分配内存,这可能非常有用,但我们要记住,这个内存不受JVM管理,因此我们要调用freeMemory()方法手动释放它。

假设我们要在堆外创建一个巨大的int数组,我们可以使用allocateMemory()方法来实现:

class OffHeapArray {
// 一个int等于4个字节
private static final int INT = 4;
private long size;
private long address; private static Unsafe unsafe;
static {
try {
Field f = Unsafe.class.getDeclaredField("theUnsafe");
f.setAccessible(true);
unsafe = (Unsafe) f.get(null);
} catch (NoSuchFieldException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
} // 构造方法,分配内存
public OffHeapArray(long size) {
this.size = size;
// 参数字节数
address = unsafe.allocateMemory(size * INT);
} // 获取指定索引处的元素
public int get(long i) {
return unsafe.getInt(address + i * INT);
}
// 设置指定索引处的元素
public void set(long i, int value) {
unsafe.putInt(address + i * INT, value);
}
// 元素个数
public long size() {
return size;
}
// 释放堆外内存
public void freeMemory() {
unsafe.freeMemory(address);
}
}

在构造方法中调用allocateMemory()分配内存,在使用完成后调用freeMemory()释放内存。

使用方式如下:

OffHeapArray offHeapArray = new OffHeapArray(4);
offHeapArray.set(0, 1);
offHeapArray.set(1, 2);
offHeapArray.set(2, 3);
offHeapArray.set(3, 4);
offHeapArray.set(2, 5); // 在索引2的位置重复放入元素 int sum = 0;
for (int i = 0; i < offHeapArray.size(); i++) {
sum += offHeapArray.get(i);
}
// 打印12
System.out.println(sum); offHeapArray.freeMemory();

最后,一定要记得调用freeMemory()将内存释放回操作系统。

CompareAndSwap操作

JUC下面大量使用了CAS操作,它们的底层是调用的Unsafe的CompareAndSwapXXX()方法。这种方式广泛运用于无锁算法,与java中标准的悲观锁机制相比,它可以利用CAS处理器指令提供极大的加速。

比如,我们可以基于Unsafe的compareAndSwapInt()方法构建线程安全的计数器。

class Counter {
private volatile int count = 0; private static long offset;
private static Unsafe unsafe;
static {
try {
Field f = Unsafe.class.getDeclaredField("theUnsafe");
f.setAccessible(true);
unsafe = (Unsafe) f.get(null);
offset = unsafe.objectFieldOffset(Counter.class.getDeclaredField("count"));
} catch (NoSuchFieldException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
} public void increment() {
int before = count;
// 失败了就重试直到成功为止
while (!unsafe.compareAndSwapInt(this, offset, before, before + 1)) {
before = count;
}
} public int getCount() {
return count;
}
}

我们定义了一个volatile的字段count,以便对它的修改所有线程都可见,并在类加载的时候获取count在类中的偏移地址。

在increment()方法中,我们通过调用Unsafe的compareAndSwapInt()方法来尝试更新之前获取到的count的值,如果它没有被其它线程更新过,则更新成功,否则不断重试直到成功为止。

我们可以通过使用多个线程来测试我们的代码:

Counter counter = new Counter();
ExecutorService threadPool = Executors.newFixedThreadPool(100); // 起100个线程,每个线程自增10000次
IntStream.range(0, 100)
.forEach(i->threadPool.submit(()->IntStream.range(0, 10000)
.forEach(j->counter.increment()))); threadPool.shutdown(); Thread.sleep(2000); // 打印1000000
System.out.println(counter.getCount());

park/unpark

JVM在上下文切换的时候使用了Unsafe中的两个非常牛逼的方法park()和unpark()。

当一个线程正在等待某个操作时,JVM调用Unsafe的park()方法来阻塞此线程。

当阻塞中的线程需要再次运行时,JVM调用Unsafe的unpark()方法来唤醒此线程。

我们之前在分析java中的集合时看到了大量的LockSupport.park()/unpark(),它们底层都是调用的Unsafe的这两个方法。

总结

使用Unsafe几乎可以操作一切:

(1)实例化一个类;

(2)修改私有字段的值;

(3)抛出checked异常;

(4)使用堆外内存;

(5)CAS操作;

(6)阻塞/唤醒线程;

彩蛋

论实例化一个类的方式?

(1)通过构造方法实例化一个类;

(2)通过Class实例化一个类;

(3)通过反射实例化一个类;

(4)通过克隆实例化一个类;

(5)通过反序列化实例化一个类;

(6)通过Unsafe实例化一个类;

public class InstantialTest {

    private static Unsafe unsafe;
static {
try {
Field f = Unsafe.class.getDeclaredField("theUnsafe");
f.setAccessible(true);
unsafe = (Unsafe) f.get(null);
} catch (NoSuchFieldException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
} public static void main(String[] args) throws Exception {
// 1. 构造方法
User user1 = new User();
// 2. Class,里面实际也是反射
User user2 = User.class.newInstance();
// 3. 反射
User user3 = User.class.getConstructor().newInstance();
// 4. 克隆
User user4 = (User) user1.clone();
// 5. 反序列化
User user5 = unserialize(user1);
// 6. Unsafe
User user6 = (User) unsafe.allocateInstance(User.class); System.out.println(user1.age);
System.out.println(user2.age);
System.out.println(user3.age);
System.out.println(user4.age);
System.out.println(user5.age);
System.out.println(user6.age);
} private static User unserialize(User user1) throws Exception {
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("D://object.txt"));
oos.writeObject(user1);
oos.close(); ObjectInputStream ois = new ObjectInputStream(new FileInputStream("D://object.txt"));
// 反序列化
User user5 = (User) ois.readObject();
ois.close();
return user5;
} static class User implements Cloneable, Serializable {
private int age; public User() {
this.age = 10;
} @Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
}

附 Unsafe.class:

package sun.misc;

import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.security.ProtectionDomain;
import sun.reflect.CallerSensitive;
import sun.reflect.Reflection; public final class Unsafe
{
static
{
registerNatives();
Reflection.registerMethodsToFilter(Unsafe.class, new String[] { "getUnsafe" });
} private static final Unsafe theUnsafe = new Unsafe();
public static final int INVALID_FIELD_OFFSET = -1; @CallerSensitive
public static Unsafe getUnsafe()
{
Class localClass = Reflection.getCallerClass();
if (!VM.isSystemDomainLoader(localClass.getClassLoader())) {
throw new SecurityException("Unsafe");
}
return theUnsafe;
} @Deprecated
public int getInt(Object paramObject, int paramInt)
{
return getInt(paramObject, paramInt);
} @Deprecated
public void putInt(Object paramObject, int paramInt1, int paramInt2)
{
putInt(paramObject, paramInt1, paramInt2);
} @Deprecated
public Object getObject(Object paramObject, int paramInt)
{
return getObject(paramObject, paramInt);
} @Deprecated
public void putObject(Object paramObject1, int paramInt, Object paramObject2)
{
putObject(paramObject1, paramInt, paramObject2);
} @Deprecated
public boolean getBoolean(Object paramObject, int paramInt)
{
return getBoolean(paramObject, paramInt);
} @Deprecated
public void putBoolean(Object paramObject, int paramInt, boolean paramBoolean)
{
putBoolean(paramObject, paramInt, paramBoolean);
} @Deprecated
public byte getByte(Object paramObject, int paramInt)
{
return getByte(paramObject, paramInt);
} @Deprecated
public void putByte(Object paramObject, int paramInt, byte paramByte)
{
putByte(paramObject, paramInt, paramByte);
} @Deprecated
public short getShort(Object paramObject, int paramInt)
{
return getShort(paramObject, paramInt);
} @Deprecated
public void putShort(Object paramObject, int paramInt, short paramShort)
{
putShort(paramObject, paramInt, paramShort);
} @Deprecated
public char getChar(Object paramObject, int paramInt)
{
return getChar(paramObject, paramInt);
} @Deprecated
public void putChar(Object paramObject, int paramInt, char paramChar)
{
putChar(paramObject, paramInt, paramChar);
} @Deprecated
public long getLong(Object paramObject, int paramInt)
{
return getLong(paramObject, paramInt);
} @Deprecated
public void putLong(Object paramObject, int paramInt, long paramLong)
{
putLong(paramObject, paramInt, paramLong);
} @Deprecated
public float getFloat(Object paramObject, int paramInt)
{
return getFloat(paramObject, paramInt);
} @Deprecated
public void putFloat(Object paramObject, int paramInt, float paramFloat)
{
putFloat(paramObject, paramInt, paramFloat);
} @Deprecated
public double getDouble(Object paramObject, int paramInt)
{
return getDouble(paramObject, paramInt);
} @Deprecated
public void putDouble(Object paramObject, int paramInt, double paramDouble)
{
putDouble(paramObject, paramInt, paramDouble);
} public void setMemory(long paramLong1, long paramLong2, byte paramByte)
{
setMemory(null, paramLong1, paramLong2, paramByte);
} public void copyMemory(long paramLong1, long paramLong2, long paramLong3)
{
copyMemory(null, paramLong1, null, paramLong2, paramLong3);
} @Deprecated
public int fieldOffset(Field paramField)
{
if (Modifier.isStatic(paramField.getModifiers())) {
return (int)staticFieldOffset(paramField);
}
return (int)objectFieldOffset(paramField);
} @Deprecated
public Object staticFieldBase(Class<?> paramClass)
{
Field[] arrayOfField = paramClass.getDeclaredFields();
for (int i = 0; i < arrayOfField.length; i++) {
if (Modifier.isStatic(arrayOfField[i].getModifiers())) {
return staticFieldBase(arrayOfField[i]);
}
}
return null;
} public static final int ARRAY_BOOLEAN_BASE_OFFSET = theUnsafe
.arrayBaseOffset(boolean[].class);
public static final int ARRAY_BYTE_BASE_OFFSET = theUnsafe
.arrayBaseOffset(byte[].class);
public static final int ARRAY_SHORT_BASE_OFFSET = theUnsafe
.arrayBaseOffset(short[].class);
public static final int ARRAY_CHAR_BASE_OFFSET = theUnsafe
.arrayBaseOffset(char[].class);
public static final int ARRAY_INT_BASE_OFFSET = theUnsafe
.arrayBaseOffset(int[].class);
public static final int ARRAY_LONG_BASE_OFFSET = theUnsafe
.arrayBaseOffset(long[].class);
public static final int ARRAY_FLOAT_BASE_OFFSET = theUnsafe
.arrayBaseOffset(float[].class);
public static final int ARRAY_DOUBLE_BASE_OFFSET = theUnsafe
.arrayBaseOffset(double[].class);
public static final int ARRAY_OBJECT_BASE_OFFSET = theUnsafe
.arrayBaseOffset(Object[].class);
public static final int ARRAY_BOOLEAN_INDEX_SCALE = theUnsafe
.arrayIndexScale(boolean[].class);
public static final int ARRAY_BYTE_INDEX_SCALE = theUnsafe
.arrayIndexScale(byte[].class);
public static final int ARRAY_SHORT_INDEX_SCALE = theUnsafe
.arrayIndexScale(short[].class);
public static final int ARRAY_CHAR_INDEX_SCALE = theUnsafe
.arrayIndexScale(char[].class);
public static final int ARRAY_INT_INDEX_SCALE = theUnsafe
.arrayIndexScale(int[].class);
public static final int ARRAY_LONG_INDEX_SCALE = theUnsafe
.arrayIndexScale(long[].class);
public static final int ARRAY_FLOAT_INDEX_SCALE = theUnsafe
.arrayIndexScale(float[].class);
public static final int ARRAY_DOUBLE_INDEX_SCALE = theUnsafe
.arrayIndexScale(double[].class);
public static final int ARRAY_OBJECT_INDEX_SCALE = theUnsafe
.arrayIndexScale(Object[].class);
public static final int ADDRESS_SIZE = theUnsafe.addressSize(); public final int getAndAddInt(Object paramObject, long paramLong, int paramInt)
{
int i;
do
{
i = getIntVolatile(paramObject, paramLong);
} while (!compareAndSwapInt(paramObject, paramLong, i, i + paramInt));
return i;
} public final long getAndAddLong(Object paramObject, long paramLong1, long paramLong2)
{
long l;
do
{
l = getLongVolatile(paramObject, paramLong1);
} while (!compareAndSwapLong(paramObject, paramLong1, l, l + paramLong2));
return l;
} public final int getAndSetInt(Object paramObject, long paramLong, int paramInt)
{
int i;
do
{
i = getIntVolatile(paramObject, paramLong);
} while (!compareAndSwapInt(paramObject, paramLong, i, paramInt));
return i;
} public final long getAndSetLong(Object paramObject, long paramLong1, long paramLong2)
{
long l;
do
{
l = getLongVolatile(paramObject, paramLong1);
} while (!compareAndSwapLong(paramObject, paramLong1, l, paramLong2));
return l;
} public final Object getAndSetObject(Object paramObject1, long paramLong, Object paramObject2)
{
Object localObject;
do
{
localObject = getObjectVolatile(paramObject1, paramLong);
} while (!compareAndSwapObject(paramObject1, paramLong, localObject, paramObject2));
return localObject;
} private static void throwIllegalAccessError()
{
throw new IllegalAccessError();
} private static native void registerNatives(); public native int getInt(Object paramObject, long paramLong); public native void putInt(Object paramObject, long paramLong, int paramInt); public native Object getObject(Object paramObject, long paramLong); public native void putObject(Object paramObject1, long paramLong, Object paramObject2); public native boolean getBoolean(Object paramObject, long paramLong); public native void putBoolean(Object paramObject, long paramLong, boolean paramBoolean); public native byte getByte(Object paramObject, long paramLong); public native void putByte(Object paramObject, long paramLong, byte paramByte); public native short getShort(Object paramObject, long paramLong); public native void putShort(Object paramObject, long paramLong, short paramShort); public native char getChar(Object paramObject, long paramLong); public native void putChar(Object paramObject, long paramLong, char paramChar); public native long getLong(Object paramObject, long paramLong); public native void putLong(Object paramObject, long paramLong1, long paramLong2); public native float getFloat(Object paramObject, long paramLong); public native void putFloat(Object paramObject, long paramLong, float paramFloat); public native double getDouble(Object paramObject, long paramLong); public native void putDouble(Object paramObject, long paramLong, double paramDouble); public native byte getByte(long paramLong); public native void putByte(long paramLong, byte paramByte); public native short getShort(long paramLong); public native void putShort(long paramLong, short paramShort); public native char getChar(long paramLong); public native void putChar(long paramLong, char paramChar); public native int getInt(long paramLong); public native void putInt(long paramLong, int paramInt); public native long getLong(long paramLong); public native void putLong(long paramLong1, long paramLong2); public native float getFloat(long paramLong); public native void putFloat(long paramLong, float paramFloat); public native double getDouble(long paramLong); public native void putDouble(long paramLong, double paramDouble); public native long getAddress(long paramLong); public native void putAddress(long paramLong1, long paramLong2); public native long allocateMemory(long paramLong); public native long reallocateMemory(long paramLong1, long paramLong2); public native void setMemory(Object paramObject, long paramLong1, long paramLong2, byte paramByte); public native void copyMemory(Object paramObject1, long paramLong1, Object paramObject2, long paramLong2, long paramLong3); public native void freeMemory(long paramLong); public native long staticFieldOffset(Field paramField); public native long objectFieldOffset(Field paramField); public native Object staticFieldBase(Field paramField); public native boolean shouldBeInitialized(Class<?> paramClass); public native void ensureClassInitialized(Class<?> paramClass); public native int arrayBaseOffset(Class<?> paramClass); public native int arrayIndexScale(Class<?> paramClass); public native int addressSize(); public native int pageSize(); public native Class<?> defineClass(String paramString, byte[] paramArrayOfByte, int paramInt1, int paramInt2, ClassLoader paramClassLoader, ProtectionDomain paramProtectionDomain); public native Class<?> defineAnonymousClass(Class<?> paramClass, byte[] paramArrayOfByte, Object[] paramArrayOfObject); public native Object allocateInstance(Class<?> paramClass)
throws InstantiationException; @Deprecated
public native void monitorEnter(Object paramObject); @Deprecated
public native void monitorExit(Object paramObject); @Deprecated
public native boolean tryMonitorEnter(Object paramObject); public native void throwException(Throwable paramThrowable); public final native boolean compareAndSwapObject(Object paramObject1, long paramLong, Object paramObject2, Object paramObject3); public final native boolean compareAndSwapInt(Object paramObject, long paramLong, int paramInt1, int paramInt2); public final native boolean compareAndSwapLong(Object paramObject, long paramLong1, long paramLong2, long paramLong3); public native Object getObjectVolatile(Object paramObject, long paramLong); public native void putObjectVolatile(Object paramObject1, long paramLong, Object paramObject2); public native int getIntVolatile(Object paramObject, long paramLong); public native void putIntVolatile(Object paramObject, long paramLong, int paramInt); public native boolean getBooleanVolatile(Object paramObject, long paramLong); public native void putBooleanVolatile(Object paramObject, long paramLong, boolean paramBoolean); public native byte getByteVolatile(Object paramObject, long paramLong); public native void putByteVolatile(Object paramObject, long paramLong, byte paramByte); public native short getShortVolatile(Object paramObject, long paramLong); public native void putShortVolatile(Object paramObject, long paramLong, short paramShort); public native char getCharVolatile(Object paramObject, long paramLong); public native void putCharVolatile(Object paramObject, long paramLong, char paramChar); public native long getLongVolatile(Object paramObject, long paramLong); public native void putLongVolatile(Object paramObject, long paramLong1, long paramLong2); public native float getFloatVolatile(Object paramObject, long paramLong); public native void putFloatVolatile(Object paramObject, long paramLong, float paramFloat); public native double getDoubleVolatile(Object paramObject, long paramLong); public native void putDoubleVolatile(Object paramObject, long paramLong, double paramDouble); public native void putOrderedObject(Object paramObject1, long paramLong, Object paramObject2); public native void putOrderedInt(Object paramObject, long paramLong, int paramInt); public native void putOrderedLong(Object paramObject, long paramLong1, long paramLong2); public native void unpark(Object paramObject); public native void park(boolean paramBoolean, long paramLong); public native int getLoadAverage(double[] paramArrayOfDouble, int paramInt); public native void loadFence(); public native void storeFence(); public native void fullFence();
}

原文链接:https://www.cnblogs.com/tong-yuan/p/Unsafe.html

并发之ATOMIC原子操作--Unsafe解析(三)的更多相关文章

  1. 并发之atomic原子操作

    Atomic类 Atomic类是一个简单的高效的.线程安全的递增递减方案,在多线程或者并发环境中,我们常常会遇到这种情况 int i=0; i++ 稍有经验的同学都知道这种写法是线程不安全的.为了达到 ...

  2. 并发之ATOMIC原子操作--CAS乐观锁原理(二)

    1.乐观锁介绍 程序完成并发操作时,访问数据时每次不加锁,假设没有冲突去完成某项操作,如果因为冲突失败就重试,直到成功为止.就是当去做某个修改或其他操作的时候它认为不会有其他线程来做同样的操作(竞争) ...

  3. 并发之java.util.concurrent.atomic原子操作类包

    15.JDK1.8的Java.util.concurrent.atomic包小结 14.Java中Atomic包的原理和分析 13.java.util.concurrent.atomic原子操作类包 ...

  4. [置顶] Android开发之XML文件的解析

    Android系统开发之XML文件的解析 我们知道Http在网络传输中的数据组织方式有三种分别为:XML方式.HTML方式.JSON方式.其中XML为可扩展标记语言,如下: <?xml vers ...

  5. C++11开发中的Atomic原子操作

    C++11开发中的Atomic原子操作 Nicol的博客铭 原文  https://taozj.org/2016/09/C-11%E5%BC%80%E5%8F%91%E4%B8%AD%E7%9A%84 ...

  6. Java并发之synchronized关键字深度解析(二)

    前言 本文继续[Java并发之synchronized关键字深度解析(一)]一文而来,着重介绍synchronized几种锁的特性. 一.对象头结构及锁状态标识 synchronized关键字是如何实 ...

  7. OS中atomic的实现解析

    OS中atomic的实现解析 转自:http://my.oschina.net/majiage/blog/267409    摘要 atomic属性线程安全,会增加一定开销,但有些时候必须自定义ato ...

  8. Fixflow引擎解析(三)(模型) - 创建EMF模型来读写XML文件

    Fixflow引擎解析(四)(模型) - 通过EMF扩展BPMN2.0元素 Fixflow引擎解析(三)(模型) - 创建EMF模型来读写XML文件 Fixflow引擎解析(二)(模型) - BPMN ...

  9. 【转】C语言文件操作解析(三)

    原文网址:http://www.cnblogs.com/dolphin0520/archive/2011/10/07/2200454.html C语言文件操作解析(三) 在前面已经讨论了文件打开操作, ...

随机推荐

  1. excel给一列数据添加前缀和后缀

    1.选中一列单元格后设置单元格格式,在自定义中输入-----“前缀”@(前缀为需要添加的内容) 输入----@“后缀” 2.字符串拼接:“前缀”&B1,B1&“后缀”

  2. 服务起不来,查看ps axj 看服务是否为守护进程(TPGID 为-1)

    在linux命令行中输入: ps axj 查看服务进程的 TPGID 字段的值是否为-1 ,为-1表示为守护进程 不为-1表示不是守护进程,服务启动不起来,或者启动起来后又被杀死了

  3. script标签引入脚本的引入位置与效果

    用script标签引入脚本的引入位置大致有两种情况: 1,在head中引入: 2,在body末尾引入: 浏览器由上到下解析代码,正常情况下,先解析head中的代码,在解析body中的代码:放在head ...

  4. 2019-08-20 纪中NOIP模拟B组

    T1 [JZOJ3490] 旅游(travel) 题目描述 ztxz16如愿成为码农之后,整天的生活除了写程序还是写程序,十分苦逼.终于有一天,他意识到自己的生活太过平淡,于是决定外出旅游丰富阅历. ...

  5. CR(Code Review)代码评审如何发挥作用

    在CODE中经常会发起代码评审和进行评审任务,可是根据目前的做法流程,我认为它就是走走形式,为了应付检查,根本没有达到预期的效果,即审查代码质量.学习他人写的代码和提高自身写代码的能力.对此,将从两方 ...

  6. 一些java基础知识的备忘

    接口和抽象类的区别是什么? 接口的方法默认是 public,所有方法在接口中不能有实现(Java 8 开始接口方法可以有默认实现),而抽象类可以有非抽象的方法. 接口中除了static.final变量 ...

  7. navicat永久激活

    https://jingyan.baidu.com/article/f54ae2fc51f0311e92b84998.html

  8. tcp与http协议 以及python的实现

    htpp协议 Rquest Headers格式: 请求方法空格URL空格协议版本回车符换行符 头部字段名:值回车符换行符 ··· 头部字段名:值回车符换行符 回车符换行符 请求数据 socket网络聊 ...

  9. SIFT算法原理(2)-极值点的精确定位

    在SIFT解析(一)建立高斯金字塔中,我们得到了高斯差分金字塔: 检测DOG尺度空间极值点 SIFT关键点是由DOG空间的局部极值点组成的.以中心点进行3X3X3的相邻点比较,检测其是否是图像域和尺度 ...

  10. python UI自动化之处理多窗口

    前言 有些页面的链接打开后,会重新打开一个窗口,想要在新页面上操作,就需要先切换窗口了.获取窗口的唯一标识用句柄表示,所以只需要切换句柄,我们就能在多个页面上灵活自如的操作了. 1.元素有属性,浏览器 ...