learning, progress, future.

skydh


  • 首页

  • 归档

java线程的并发加锁控制

发表于 2017-12-07

synchronized和Lock的加锁机制

  synchronized关键字加锁。
  第一,我们先明确几个概念。
  我们使用synchronized关键字时,锁不是加在代码上,而是加在对象上。
  我先准备了一个测试类来验证信息,Counter类是一个公共线程信息类

  package thread;

import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class Counter {

    public final Lock lock = new ReentrantLock();
    private Condition condition = lock.newCondition();
    private volatile int a = 1;
    private Students stu = new Students();

    public int geta() {
        return a;
    }

    public synchronized void add() {
        a++;
        try {
            SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
            System.out.println("add开始执行" + df.format(new Date()));
            Thread.sleep(10000);
            System.out.println("add is ing");
            System.out.println("add执行结束" + df.format(new Date()));
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    public synchronized void add1() {
        a++;
        try {
            SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
            System.out.println("add1开始执行" + df.format(new Date()));
            Thread.sleep(10000);
            System.out.println("add1 is ing");
            System.out.println("add1开始执行" + df.format(new Date()));
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    public void add2() {
        synchronized (stu) {
            SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
            System.out.println("add2开始执行" + df.format(new Date()));
            System.out.println("add2 is ing");
            System.out.println("add2开始执行" + df.format(new Date()));
        }
    }

    public static synchronized void addStatic() {

        try {
            SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
            System.out.println("addStatic开始执行" + df.format(new Date()));
            Thread.sleep(10000);
            System.out.println("addStatic is ing");
            System.out.println("addStatic开始执行" + df.format(new Date()));
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    public void add3() {

        lock.lock();
        try {
            System.out.println("进入add3");
            a++;

            Thread.sleep(10000);
            System.out.println("add3");

        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        lock.unlock();
    }

    public void add4() {



        lock.lock();
        System.out.println("进入add4");
        try {
            Thread.sleep(10000);
        } catch (InterruptedException e) {

            e.printStackTrace();
        }
        System.out.println("add4");

        condition.signal();
        lock.unlock();

    }



}
 
  创建一个Counter类的对象,这个对象里面一共有4个锁,第一个就是counter对象锁,第二个就是students对象锁,第三个就是Students这个类的锁,因为每个类都是Class类的对象。第四个就是ReentrantLock这个可重入锁。   这几个概念明确后,写几个demo来测试说明下:
  
package thread;

public class Test003 {

    public class Thread1 extends Thread {
        private Counter counter;

        public Thread1(Counter counter) {
            this.counter = counter;
        }

        public void run() {
            counter.add();
        }
    }

    public class Thread2 extends Thread {
        private Counter counter;

        public Thread2(Counter counter) {
            this.counter = counter;
        }

        public void run() {
            counter.add1();
        }
    }

    public class Thread3 extends Thread {
        private Counter counter;

        public Thread3(Counter counter) {
            this.counter = counter;
        }

        public void run() {
            counter.add2();
        }
    }

    public class ThreadStatic extends Thread {
        public void run() {
            Counter.addStatic();
        }
    }

    public class Thread4 extends Thread {
        private Counter counter;

        public Thread4(Counter counter) {
            this.counter = counter;
        }

        public void run() {
            counter.add3();
        }
    }

    public class Thread5 extends Thread {
        private Counter counter;

        public Thread5(Counter counter) {
            this.counter = counter;
        }

        public void run() {
            counter.add4();
        }
    }
}

 
  这些是即将要调用的线程类,已来方便我们使用测试。 第一种情况:
public static void main(String[] args) throws InterruptedException {

        Counter counter = new Counter();
        Test003 test003 = new Test003();
        Thread1 thread1 = test003.new Thread1(counter);
        Thread2 thread2 = test003.new Thread2(counter);

        thread1.start();
        thread2.start();

    }
  结果如下:
add1开始执行2017-12-21 14:28:48
add1 is ing
add1开始执行2017-12-21 14:28:58
add开始执行2017-12-21 14:28:58
add is ing
add执行结束2017-12-21 14:29:08

  结果解析:无论执行多少次,要么add1先执行,要么add先执行,但是任意一个没有执行完,另一个是不会执行的,即便调用了sleep方法,为什么呢。因为这2个线程公用一个对象,而调用的方法都加了synchronized的方法,由于第一个执行执行方法的将获得对象锁。其余的线程就无法获取到对象锁,只能进入等待队列。   第二种情况:
public static void main(String[] args) throws InterruptedException {
        Counter counter = new Counter();
        Test003 test003 = new Test003();
        Thread1 thread1 = test003.new Thread1(counter);
        Thread3 thread3 = test003.new Thread3(counter);
        thread1.start();
        thread3.start();
    }
  结果如下
add2开始执行2017-12-21 14:41:10
add2 is ing
add开始执行2017-12-21 14:41:10
add2开始执行2017-12-21 14:41:10
add is ing
add执行结束2017-12-21 14:41:20
  结果解析:2个线程交叉执行,虽然都加了锁,但是是不同的对象锁,因此交叉执行。 第三种情况:
public static void main(String[] args) throws InterruptedException {
        Counter counter = new Counter();
        Counter counter1 = new Counter();
        Test003 test003 = new Test003();
        Thread1 thread1 = test003.new Thread1(counter);
        Thread2 thread2 = test003.new Thread2(counter1);
        thread1.start();
        thread2.start();
    }
  结果如下:
add开始执行2017-12-21 14:45:03
add1开始执行2017-12-21 14:45:03
add1 is ing
add is ing
add1开始执行2017-12-21 14:45:13
add执行结束2017-12-21 14:45:13
  结果解析:虽然都是一个类,一个代码,但是是2个对象,synchronized这个关键字加锁是加到对象身上去了。而这个2个线程锁持有的Counter对象时不一样的,因此锁也是不一样的,故交叉执行很正常。   第四种情况:
public static void main(String[] args) throws InterruptedException {
        Counter counter = new Counter();
        Test003 test003 = new Test003();
        Thread1 thread1 = test003.new Thread1(counter);
        ThreadStatic threadStatic = test003.new ThreadStatic();
        thread1.start();
        threadStatic.start();
    }
  结果如下:
addStatic开始执行2017-12-21 18:35:55
add开始执行2017-12-21 18:35:55
addStatic is ing
add is ing
addStatic开始执行2017-12-21 18:36:05
add执行结束2017-12-21 18:36:05
  结果解析:结果交叉执行为何,因为静态方法是属于类本身的,那么在这个静态方法上加的synchronized,那么这个锁则对应的是类本身,那么2个线程执行普通方法和静态方法(都加了synchronized关键字)时是并发执行的了。   第五种情况:
public static void main(String[] args) throws InterruptedException {
        Counter counter = new Counter();
        Test003 test003 = new Test003();
        Thread4 thread4 = test003.new Thread4(counter);
        Thread5 thread5 = test003.new Thread5(counter);
        thread4.start();
        thread5.start();
    }
  结果如下:
进入add4
add4
进入add3
add3
  结果解析:同一个Counter对象那么对应同一个ReentrantLock锁。那么这2个线程所执行的方法都加了ReentrantLock,那么执行的时候注定要加锁等待,所以无法并发执行,结果按顺序执行。 ## 并发条件控制   正如大家所知,synchronized关键字对应了wait()方法和notify()方法来控制并发控制,那么lock呢,也对应一系列的方法来处理。我们使用Condition这个类的方法来加以控制,下面,我将展示这2个加锁方式的控制的简单实现   第一个:lock的条件控制


    public final Lock lock = new ReentrantLock();
    private Condition condition = lock.newCondition();
    private volatile int a = 1;
    public void add3() {
        System.out.println("进入add3");
        lock.lock();
        try {
            a++;
            System.out.println("add3");
            condition.await();
            System.out.println("add31");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        lock.unlock();
    }

    public void add4() {
        System.out.println("add4");
        while (true) {
            if (a > 1) {
                System.out.println("进入循环");
                lock.lock();
                System.out.println("add4");
                System.out.println("add41");
                condition.signal();
                lock.unlock();
                break;
            }
        }
    }
public static void main(String[] args) throws InterruptedException {
        Counter counter = new Counter();
        Test003 test003 = new Test003();
        Thread4 thread4 = test003.new Thread4(counter);
        Thread5 thread5 = test003.new Thread5(counter);
        thread4.start();
        thread5.start();
    }
  结果如下
add4
进入add3
add3
进入循环
add4
add41
add31
  结果解析:2个线程无论谁先执行,先获取到锁的总是add3()方法。因为a=1,无法获取到锁,因此先执行add3方法,在放弃锁并进入到等待队列,然后a>1了,那么add4有机会执行代码了。在唤醒add3()方法,恩,完美。   第二种情况:使用Object类的wait方法。
    private volatile int a = 1;
    public void add() {

        synchronized (this) {
            try {
                SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
                System.out.println("add开始执行" + df.format(new Date()));
                // Thread.sleep(10000);
                a++;
                wait();
                System.out.println("add is ing");
                System.out.println("add执行结束" + df.format(new Date()));
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    public void add1() {
        while (true) {
            if (a > 1) {
                synchronized (this) {
                    SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
                    System.out.println("add1开始执行" + df.format(new Date()));
                    // Thread.sleep(10000);
                    System.out.println("add1 is ing");
                    System.out.println("add1开始执行" + df.format(new Date()));
                    notify();
                }
                break;
            }
        }
    }
    public static void main(String[] args) throws InterruptedException {

        Counter counter = new Counter();

        Test003 test003 = new Test003();
        Thread1 thread1 = test003.new Thread1(counter);

        Thread2 thread2 = test003.new Thread2(counter);

        thread1.start();
        thread2.start();
        thread1.join();
}

 结果如下


add开始执行2017-12-21 19:32:23
add1开始执行2017-12-21 19:32:23
add1 is ing
add1开始执行2017-12-21 19:32:23
add is ing
add执行结束2017-12-21 19:32:23

  结果解析:逻辑和上面一样,只是不同的实现方式罢了。

  wait()方法在这里要再次说下,他是在加了synchronized这个关键字的对象调用的,不然报错。还有Thread类的join()方法源码里面的wait()方法是让主线程进入等待队列,并且等待这个锁,当这个子线程执行完后,则会自动调用notify方法,让等待子线程对象锁的主线程可以继续执行,恩,完美解释join()方法。

  

     

java线程的常用方法和属性介绍

发表于 2017-11-29

start()

  start方法是Thread 类的方法,在这个方法中会调用native方法(start0())来启动线程,为该线程分配资源。

sleep()

  sleep方法有2个方法。

   public static native void sleep(long millis) 


throws InterruptedException;
public static void sleep(long millis, int nanos)
throws InterruptedException {
if (millis < 0) {
throw new IllegalArgumentException(“timeout value is negative”);
}

    if (nanos < 0 || nanos > 999999) {
        throw new IllegalArgumentException(
                            "nanosecond timeout value out of range");
    }

    if (nanos >= 500000 || (nanos != 0 && millis == 0)) {
        millis++;
    }

    sleep(millis);
}


  看了源码,发现第二个纳秒级别的没什么用,实际上还是毫秒级别。
  sleep方法在进入阻塞队列时不会释放当前线程所持有的锁。

yield()

  该方法会让当前线程交出cpu权限,但是不能确定具体时间,和sleep方法一样不会释放当前线程所持有的锁,该方法会让线程直接进入就绪状态,很好理解。目的是为了让同等优先级的线程获得cpu执行的机会。

join()

  该方法是让当前线程阻塞,直到调用该方法的线程执行完毕。源码分析

 public final synchronized void join(long millis)
throws InterruptedException {
long base = System.currentTimeMillis();
long now = 0;

    if (millis < 0) {
        throw new IllegalArgumentException("timeout value is negative");
    }

    if (millis == 0) {
        while (isAlive()) {
            wait(0);
        }
    } else {
        while (isAlive()) {
            long delay = millis - now;
            if (delay <= 0) {
                break;
            }
            wait(delay);
            now = System.currentTimeMillis() - base;
        }
    }
} 


  这个是join的源码,代码很清晰,如果调用该方法的对象线程已启动,那么当前线程释放该对象锁,并进入条件等待队列里面,等待改线程执行完(线程如果在同步代码块中结束会自动调用notifyAll()方法来唤醒等待队列里面的线程)

wait() ,notifyAll(),notify()

  这3个方法一般一同出现。且都是Object类的方法,用来辅助操作线程类。
  这3个方法都必须在同步代码块中,不然就会报错。
  wait()方法会让当前线程释放锁,并进入到条件等待队列。
  notify()方法会随机唤醒条件等待队列的任意一个线程,并将其放到锁池里面。
  notifyAll()方法则是唤醒条件等待队列的所有线程,并将其都放到锁池里面。
  而锁池里面的线程来竞争所需要的对象锁,成功获取到锁的线程将加入到就绪队列里面。

interrupt(),isInterrupted(),interrupted()

  这3个方法表示线程的中断,这个很有意思,分多钟情况讨论。(在java中,中断不是强制停止改线程,而是给线程一个信号,让其自行处理何时退出)
  isInterrupted:就是返回对应线程的中断标志位是否为true。
  interrupted:返回当前线程的中断标志位是否为true,但它还有一个重要的副作用,就是清空中断标志位,也就是说,连续两次调用
  interrupted(),第一次返回的结果为true,第二次一般就是false 。
  interrupt:表示中断对应的线程。

  中断线程分情况讨论:
  1.还没start,或者已经结束,无效果
  2.运行中,中断无效,直到只能设置个中断位,直到线程走完或者进入阻塞。
  3.锁池,中断无效。
  4.阻塞,等待,会抛出异常,可以中断。
  ps(io等待大多都是可以中断的,但是inputStream的read不会相应中断。)
  中断不好使,因此最好自己在线程类里面提供关闭方法。比如

 class MyThread extends Thread{
private volatile boolean isStop = false;
@Override
public void run() {
int i = 0;
while(!isStop){
i++;
}
}

    public void setStop(boolean stop){
        this.isStop = stop;
    }
}

总结

  在这里我没有写具体的例子,因为例子大家可以自己写测试下,太占篇幅了,这里只举出一些源码。

java线程的3种创建方式及优缺点

发表于 2017-11-23

线程创建简介

  1.在java中表示线程的是Thread类。无论是那种方式创建线程,本质上都是创建Thread类的对象。
aaa
  2.Thread类继承Runnable接口,且也有以Runnable作为参数的构造方法。
aaa
  3.FutureTask这个类也实现了Runnable接口,故也可以作为参数传进Thread 里面来创建线程。

线程的创建

继承Thread类

package thread;

/**
 * 继承thread类来创建线程
 * 
 * @author dh
 *
 */
public class ExtendsThread extends Thread {

    public void run() {
        for (int i = 0; i < 10; i++) {
            System.out.println(Thread.currentThread().getName() + "    _"+i);
        }
    }

}
### 实现Runnable接口
package thread;

/**
 * 实现 Runnable接口
 * 
 * @author dh
 *
 */
public class ImplementRunnable implements Runnable {
    private int i;

    public ImplementRunnable(int i) {
        this.i = i;
    }

    @Override
    public void run() {

        for (; i < 20; i++) {
            System.out.println(Thread.currentThread().getName() + "   _" + i);
        }
    }

}
### 实现Callable接口
package thread;

import java.util.concurrent.Callable;

/**
 * 
 * @author dh
 *
 */
public class ImplementCallable implements Callable {

    private int i;

    public ImplementCallable(int i) {
        this.i = i;
    }

    @Override
    public String call() throws Exception {
        for (; i < 20; i++) {
            System.out.println(Thread.currentThread().getName() + "   _" + i);

        }
        return "1";
    }

}
### 创建线程
package thread;

import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;
/**
 * 
 * @author dh
 *
 */
public class TestD {

    public static void main(String[] args) throws InterruptedException, ExecutionException {
        ExtendsThread et = new ExtendsThread();
        et.run();



        ImplementRunnable imr = new ImplementRunnable(0);
        Thread thread = new Thread(imr);
        thread.start();
        Thread thread1 = new Thread(imr);
        thread1.start();


        ImplementCallable implCallable = new ImplementCallable(1);
        FutureTask ft = new FutureTask(implCallable);
        Thread thread2 = new Thread(ft);
        thread2.start();
        System.out.println(ft.get());

    }

}
}

结果如下:

main    _0
main    _1
main    _2
main    _3
main    _4
main    _5
main    _6
main    _7
main    _8
main    _9
Thread-1   _0
Thread-2   _0
Thread-1   _1
Thread-2   _2
Thread-1   _3
Thread-2   _4
Thread-1   _5
Thread-2   _6
Thread-1   _7
Thread-2   _8
Thread-1   _9
Thread-2   _10
Thread-1   _11
Thread-2   _12
Thread-1   _13
Thread-2   _14
Thread-1   _15
Thread-2   _16
Thread-1   _17
Thread-2   _18
Thread-1   _19
Thread-3   _1
Thread-3   _2
Thread-3   _3
Thread-3   _4
Thread-3   _5
Thread-3   _6
Thread-3   _7
Thread-3   _8
Thread-3   _9
Thread-3   _10
Thread-3   _11
Thread-3   _12
Thread-3   _13
Thread-3   _14
Thread-3   _15
Thread-3   _16
Thread-3   _17
Thread-3   _18
Thread-3   _19
1

3种方式创建的优缺点

1.继承Thread显然有个很大的缺点,java是单继承了,如果继承了Thread那么就无法继承其他类。但是继承Thread编写简单,实现方便。

2.实现Runnable接口和Callable接口。大致一样,区别就是Callable接口的实现可以有返回值,且可以抛出显示异常。其余大致一样。
他们的优势是实现接口,那么实现类可以有其他父类,避免的Thread的问题,其次可以用一个实现了该接口的对象来创建多个线程,从而方便一些基本的资源共享,因为是同一个对象。

java线程的基本概念

发表于 2017-11-23

进程和线程

进程的诞生

  操作系统中有2个任务A,B,任务A先执行,执行到一半需要io,因此要大量时间,在这个时间段内cpu是空闲的,浪费了资源,于是就有进程,当A暂时无法利用cpu,但是又不能销毁时,就把它暂存起来,让B来执行。B执行完或者需要A执行时,根据暂存的信息回复过来。


  每个进程都对应一定的内存空间,并且只能使用自己的内存空间,并且保留程序的运行状态,这个也为进程切换提供了基础。

线程的诞生

  第一,多核处理器的出现,为了更好的利用多核处理器,避免资源的浪费。第二程序的需要,人们需要在一个程序中并发执行任务。比如播放器边播放视频,我们还要可以边品论,显然要并发执行。第三。一个程序中出现了阻塞(io)而导致程序不能继续进行,这个很影响体验和操作,因此我们需要多线程,把这些耗时的操作都丢到子线程里面去,这样程序就可以继续走下去了。

线程的基本概念和注意点

  1.对操作系统来说,资源分配的基本单位是进程,而调度的基本单位是线程。


  2.对于一个程序来说,不一定多线程的效率就高,不能盲目的使用,多线程的使用和具体业务场景(交互之类的),以及机器的特性,比如是多核还是单核的机器等


3.每个线程表示一条单独的执行流,有自己的程序计数器,有自己的栈,但线程之间可以共享内存,它们可以访问和操作相同的对象。


  4.java是单线程编程语言,你要是不主动创建线程,那么就默认只有主线程,当然jvm还是会有其他很多后台精灵线程存在的,比如垃圾回收
(Erlang就是一种并发编程语言,每一个函数都可以当做独立的任务来驱动)

java线程的状态

aaa
  1.新建:创建一个线程对象


  2.可运行:线程调用了start方法,存在于可运行线程池中。


  3.运行态:线程获取到了cpu使用权。


  4.阻塞态:第一:运行的线程调用对象的wait()方法(该方法只存在于同步加锁代码块中),让出锁,进入等待队列。只用同样调用该对象的线程使用notify()方法才会进入到锁池中。 第二:运行的线程获取一个加锁的资源时,若该资源被占用,进入锁队列中,只用当锁队列中的线程获取到了加锁的对象资源时,才会进入到可运行状态中。第三:运行的线程执行sleep()或者join()方法,或者io,该线程会阻塞队列,等时间到,或者io完成,会自动进入可运行状态。


  5.死亡:线程执行完,或者异常中断退出。

浅析https

发表于 2017-11-21

基本属性定义

对称加密

  对称加密是最快速、最简单的一种加密方式,加密(encryption)与解密(decryption)用的是同样的密钥(secret key)

非对称加密

  非对称加密算法需要两个密钥:公开密钥(publickey)和私有密钥(privatekey)。公开密钥与私有密钥是一对,如果用公开密钥对数据进行加密,只有用对应的私有密钥才能解密;如果用私有密钥对数据进行加密,那么只有用对应的公开密钥才能解密。因为加密和解密使用的是两个不同的密钥,所以这种算法叫作非对称加密算法。经典的非对称加密算法有RSA算法。原理是利用了乘法不可逆和大数因子很难分解,2个大质数。

2种加密算法的优缺点

  对称加密,加密效果很好,速度快。但是在传输中如何安全是个问题。


  非对称加密,利于传输,但是速度慢,消耗性能,且加密内容不能超过公钥长度。

数字证书

  数字证书就是互联网通讯中标志通讯各方身份信息的一串数字,提供了一种在Internet上验证通信实体身份的方式,数字证书不是数字身份证,而是身份认证机构盖在数字身份证上的一个章或印(或者说加在数字身份证上的一个签名)。它是由权威机构——CA机构,又称为证书授权(Certificate Authority)中心发行的,人们可以在网上用它来识别对方的身份,简单来说就是包含服务器方基本信息和公钥信息和数字签名的证书。

数字签名

  将报文按双方约定的HASH算法计算得到一个固定位数的报文摘要。在数学上保证:只要改动报文中任何一位,重新计算出的报文摘要值就会与原先的值不相符。这样就保证了报文的不可更改性。
  将该报文摘要值用发送者的私人密钥加密,然后连同原报文一起发送给接收者,而产生的报文即称数字签名,简单来说就是对证书的基本信息进行摘要缩减后再用ca的公钥生成的一个签名。

https的诞生

aaa


  A和B进行通信,如果不加密这个hello信息会被中间人直接获取到,如果是账号密码信息,这将是一场灾难。
  加密分为对称加密和非对称加密,显然,对称加密速度快一点,那么我们使用对称加密来给我们的信息加密
aaa
  那么又有问题了,对于服务器端和客户端进行消息传递,我是如何把对称密钥s交给你,就算交给你了,别人来向我请求消息时,我如果也用密钥s,那么加密将毫无意义,如果每个客户端都存一个特定的密钥,那么服务器内存也将不够。
aaan
  那么我们只能用非对称加密来进行加密了,你瞧,就算中间人截取到了消息,用公钥打开后能开到信息,也无法得到私钥来修改,那么客户端得到的将是安全完整的消息。但是我们一直用非对称加密是不是太慢了。
aaan
  你看,我们可以先用非对称加密把随机生成的对称加密密钥发送到客户端,这样除了一开始我们使用非对称加密,后面的整个会话都用对称加密来处理,完美。
aaan
  咦,又出现问题了,客户端向服务器端请求公钥时,整个公钥直接被掉包,那么后续的岂不是都不行了。
还好,你有张良计我有过墙梯。
aaan
  我们服务器端可以向公证处请求证书,也就是数字证书,数字证书如何保证通信安全呢。第一:证书里面有个报文,包含了服务器端信息,域名,摘要算法,以及公钥,第二:有一个是对这些信息摘要后用CA私钥加密生成的数字签名。


  客户端得到证书后用报文信息中的摘要算法对报文进行进行摘要,获取到的摘要和用CA公钥解开的数字签名一致,那么这个证书是安全的了,否则不安全。对了CA的公钥就在本地浏览器的证书里面。


  为什么这样做?


  如果报文有了一丝丝的修改,那么生成的摘要就会发生变化。通过这个来判断报文是否修改。
  为什么不直接对报文加密?


  因为非对称加密是有限制的,加密的信息不能比公钥长,报文信息可能会比较多,那么最好就是生成一个摘要,对摘要进行加密。


  各位大佬,如果对上面的有问题,发我邮箱验证。donghang49@outlook.com

图片取自网络

创表语句转实体类

发表于 2017-11-20


为何规范化开发

对于一些企业级别的开发来说,开发必须规范,因为表太多了,相关的关联关系错综复杂。不规范将难以维护。

如何规范化开发

1.规范的接口设计。第一要符合rest,第二要命名规范优雅,第三要单一职责,第四要可扩展性,第五要文档描述清晰


2.规范的代码风格,比如命名的各种规范,属性,方法的命名规范,总之要符合开发要求


3.规范的表设计,由于表比较多,字段复杂,表之间的关联也很多,那么就要使用powerdesigner等uml来设计表,这样的话可以完整的保留表之间的关系。

一些小工具

  我是做erp开发的,所以遇见了一些比较大的表,一个表光字段就有60多个,而且一个功能有很多表相互关联,我只能使用powerdesigner这样的UML来设计表,设计完表之后,powerdesigner之类的会提供sql语句来创建表,但是最后的实体类就要我们来自己写,一想要写这么多字段,就很绝望,于是我在周末设计了一个小工具来帮助我开发。


  这个工具类可以帮助我们利用创表语句创建出基本的实体类,包含了注解,以及注释,至于get,set方法就交给IDE来创建了。


  下面是我的gitbub链接。


  点我一下就到了

 
  如果对这个小工具这个有好的建议,可以发我邮箱一起研究改进下。donghang49@outlook.com

1…1213

skydh

skydh

126 日志
© 2020 skydh
本站访客数:
由 Hexo 强力驱动
|
主题 — NexT.Pisces v5.1.3