第一章与第七章——概述

Java多线程

进程和线程

进程:即为OS中运行的程序,它是OS管理的基本运行单元。

线程:进程中独立运行的子任务。例如在QQ运行时,有很多子任务在同时运行,比如好友视频线程,下载文件线程,传输数据线程等等。

 

多线程的优点

在使用多任务OS中,我们可以最大限度地利用CPU的空闲时间处理其他任务,即CPU在不同任务之间不停切换。而使用多线程技术后,可以在同一时间内运行更多不同种类的任务。

按照传统的理解,程序中的代码应该是一行一行的执行,上一行代码若没有执行完毕,下一行代码则不会执行,而多线程是若上一行代码没有执行完毕,下面的代码就可以执行了,两行代码可以交替的执行。

 

使用多线程

实现多线程编程有两种:继承Thread类 和 实现Runnable接口

但实际上,Thread类实现了Runnable接口,它们之间具有多态关系;其次,为了支持多继承,可以以实现Runnable接口的方式。

实际上,这两种方式创建的线程在工作时性质是一样的。

(1)继承Thread类

    public static class myThread extends Thread {
        private int index;

        public myThread(int index) {
            this.index = index;
        }

        @Override
        public void run() {
            System.out.println("myThreead-" + index);
        }
    }


    public static void main(String[] args) {
       myThread mythead1 = new myThread(1);
       myThread mythead2 = new myThread(2);
       myThread mythead3 = new myThread(3);

       mythead1.start();
       mythead2.start();
       mythead3.start();
    }

执行start()方法的顺序不代表线程启动的顺序

 

(2)实现Runnable接口

  若希望创建的线程类已有一个父类,此时不能继承Thread类,因此可以实现Runnable接口来应对这种情况。

    public static class myRunnable implements Runnable {
        @Override
        public void run() {
            System.out.println("");
        }
    }

在Thread类的构造函数中,有两个构造函数可以传递Runnable接口

Thread(Runnable target);

Thread(Runnable target, String name);

        Runnable myRunnable = new myRunnable();
        Thread thread = new Thread(myRunnable);
        thread.start();
        System.out.println("结束");

 

实例变量和线程安全

自定义线程类中的实例变量,对于其他线程可以有共享和不共享之分。

(1)不共享数据index的情况

    public static class myThread extends Thread {
        private int index = 5;

        public myThread(String name) {
            this.setName(name);
        }

        @Override
        public void run() {
            index--;
            System.out.println(this.currentThread().getName() + "计算 " + index);
        }
    }


    public static void main(String[] args) {
        new myThread("A").start();
        new myThread("B").start();
        new myThread("C").start();

    }

创建3个线程,每个线程有各自的index变量

 

(2)共享数据index的情况

    public static class myThread extends Thread {
        private int index = 5;



        @Override
        public void run() {
            index--;
            System.out.println(this.currentThread().getName() + "计算 " + index);
        }
    }


    public static void main(String[] args) {
        myThread mT = new myThread();
        new Thread(mT, "A").start();
        new Thread(mT, "B").start();
        new Thread(mT, "C").start();
    }

 

在某些JVM中,index--的操作分成3步:

  • 取得原有index值
  • 计算index-1
  • 对index进行赋值

在这3个步骤,若有多个线程同时访问,则一定会出现非线程安全问题。

非线程安全问题:多个线程对同一对象中同一个实例变量进行操作时,会出现值更改,值不同步问题

可以用 synchronized 关键字来修饰方法来解决此问题

@Override
public synchronized  void run() {
       index--;
       System.out.println(this.currentThread().getName() + "计算 " + index);
}

index-- 在println()方法里使用出现的问题

在前面 非线程安全的例子中,index-- 是作为单独的一行。下面我们将这个语句放在输出语句println()中

        @Override
        public   void run() {
            System.out.println(this.currentThread().getName() + "计算 " + (--index));
        }

这样执行的结果还是会出现非线程安全的问题。

虽然println()方法在内部是同步的,但index--操作是在进入输出方法前发生的,因此仍有出现问题的概率

 

isAlive()方法

isAlive()方法是测试线程是否处于活动状态。

活动状态:线程已启动且尚未终止,处于正在运行或者准备开始运行的状态。

    public static class myThread extends Thread {

        @Override
        public   void run() {
            System.out.println("状态:" + this.isAlive());
        }
    }


    public static void main(String[] args) {
        myThread mT = new myThread();
        System.out.println("开始:" + mT.isAlive());
        mT.start();
        System.out.println("结束:" + mT.isAlive());

    }

 

运行结果:

开始:false
结束:true
状态:true

需要说明的是

System.out.println("结束:" + mT.isAlive());

虽然结果是true,但此值是不确定的,打印true是因为myThread线程尚未执行完毕。若将main函数改为

    public static void main(String[] args) throws InterruptedException{
        myThread mT = new myThread();
        System.out.println("开始:" + mT.isAlive());
        mT.start();
        Thread.sleep(1000);   // 添加此句
        System.out.println("结束:" + mT.isAlive());
    }

则结束那一段输出为 false。因为myThread对象已在1s内执行完毕。

另外在使用isAlive()方法时,若是将线程对象以构造参数的方式传递该Thread对象进行start()启动时,运行的结果和前面示例是有差异的。造成差异的原因是Thread.currentThread()和this的差异。下面测试这个实验:

    public static class myThread extends Thread {

        public myThread() {
            System.out.println("构造函数开始----");
            System.out.println("Thread.currentThread().getName()线程:" + Thread.currentThread().getName());
            System.out.println("Thread.currentThread().isAlive()线程是否存活:" + Thread.currentThread().isAlive());

            System.out.println("this.getName()线程:" + this.getName());
            System.out.println("this.isAlive()线程是否存活:" + this.isAlive());
            System.out.println("构造函数结束----");
        }


        @Override
        public   void run() {
            System.out.println("run函数开始----");
            System.out.println("Thread.currentThread().getName()线程:" + Thread.currentThread().getName());
            System.out.println("Thread.currentThread().isAlive()线程是否存活:" + Thread.currentThread().isAlive());

            System.out.println("this.getName()线程:" + this.getName());
            System.out.println("this.isAlive()线程是否存活:" + this.isAlive());
            System.out.println("run函数结束----");
;
        }
    }


    public static void main(String[] args) throws InterruptedException{
        myThread myThread = new myThread();
        Thread thread = new Thread(myThread);

        System.out.println("main函数开始,线程thread: " + thread.isAlive());
        thread.setName("A");
        thread.start();
        System.out.println("main函数结束,线程thread:" + thread.isAlive());

    }

 

输出语句为:

构造函数开始----
Thread.currentThread().getName()线程:main
Thread.currentThread().isAlive()线程是否存活:true
this.getName()线程:Thread-0
this.isAlive()线程是否存活:false
构造函数结束----
main函数开始,线程thread: false
main函数结束,线程thread:true
run函数开始----
Thread.currentThread().getName()线程:A
Thread.currentThread().isAlive()线程是否存活:true
this.getName()线程:Thread-0
this.isAlive()线程是否存活:false
run函数结束----

 

currentThread()方法

currentThread()方法返回代码段正在被哪个线程调用的信息。

下面我们测试 this 和 Thread.currentThread 之间的差异:

    public static class myThread extends Thread {
        private int index = 3;

        public myThread() {
            System.out.println("构造函数开始------------------------");

            System.out.println("Thread.currentThread().getName()线程:" + Thread.currentThread().getName());
            System.out.println("this.getName()线程:" + this.getName());
            System.out.println("Thread.currentThread() == this):" + (Thread.currentThread() == this));

            System.out.println("构造函数结束------------------------");
        }


        @Override
        public   void run() {
            System.out.println("run函数开始------------------------");

            System.out.println("Thread.currentThread().getName()线程:" + Thread.currentThread().getName());
            System.out.println("this.getName()线程:" + this.getName());
            System.out.println("Thread.currentThread() == this):" + (Thread.currentThread() == this));

            System.out.println("run函数结束------------------------");
;
        }
    }

    public static void main(String[] args) throws InterruptedException{
        myThread myThread = new myThread();
        Thread thread = new Thread(myThread);

        thread.setName("A");
        thread.start();
    }

输出结果:

构造函数开始------------------------
Thread.currentThread().getName()线程:main
this.getName()线程:Thread-0
Thread.currentThread() == this):false
构造函数结束------------------------
run函数开始------------------------
Thread.currentThread().getName()线程:A
this.getName()线程:Thread-0
Thread.currentThread() == this):false
run函数结束------------------------
 

倘若我们是将线程对象myThread以构造参数的方式传递给Thread对象后进行start()方法,则在Thread的run方法中调用的是target.run()方法。即myThread.run()。

public void run() {  
    if (target != null) {  
        target.run();  
    }  
}  

此时Thread.currentThread()是Thread的引用thread,而this是myThread的引用。

而如果是直接调用线程对象myThread的start()方法,则 Thread.currentThread() 和 this 都是 myThread的引用

    public static void main(String[] args) throws InterruptedException{
        myThread myThread = new myThread();
        myThread.setName("A");
        myThread.start();
    }

 

sleep()方法

该方法能在指定的毫秒数内让 当前正在执行的线程 休眠。

此处”当前正在执行的线程“ 是指 this.currentThread()返回的线程

 

interruop()方法

该方法是将当前线程标记上停止的标志(但不会直接停止)。下面测试是否会停止线程:

    public static class MYThread extends Thread {
        @Override
        public void run() {
            for(int i = 0; i < 500000; i++)
                System.out.println("i= " + (i + 1));
        }
    }

    public static void main(String[] args) {
        try {

            MYThread tmp  = new myThread.MYThread();
            tmp.start();
            Thread.sleep(1000);
            tmp.interrupt();

        }catch (InterruptedException e) {
            System.out.println("抓住");
            e.printStackTrace();
        }
    }

输出为打印1~500000。因此调用 interrupt()方法并没有停止线程。

判断线程是否是停止状态

Thread类提供了两种方法来判断:

this.interrupted():测试当前线程是否已中断,该方法是静态方法。(当前线程是指运行this.interrupted()方法的线程)

this.isinterrupted():测试线程是否已中断

我们来看第一种方法 interrupted()

public class MyThread extends Thread {
    @Override
    public void run() {
        for(int i = 0; i < 1000; i++) {
            System.out.println("i= " + i);
        }
    }
}

 

public class Thread_Test {

    public static void main(String[] args) {
        try {
            MyThread tmp  = new MyThread();
            tmp.start();
            tmp.interrupt();
            Thread.sleep(10);
//            Thread.currentThread().interrupt();

            System.out.println("是否已停止:" + tmp.interrupted());
            System.out.println("是否已停止:" +  tmp.interrupted());
        }catch (InterruptedException e) {
            System.out.println("抓住");
            e.printStackTrace();
        }
    }
}

输出:

i= 216
i= 217
是否已停止:false
是否已停止:false
i= 218
i= 219
i= 220

由此可知,这个”当前线程“是main,他从未中断过,因此为false。

为了使main线程产生中断效果,修改以下测试函数

public class Thread_Test {

    public static void main(String[] args) {
        try {
            MyThread tmp  = new MyThread();
            tmp.start();
            Thread.sleep(10);
            Thread.currentThread().interrupt();     // 修改

            System.out.println("是否已停止:" + tmp.interrupted());
            System.out.println("是否已停止:" +  tmp.interrupted());
        }catch (InterruptedException e) {
            System.out.println("抓住");
            e.printStackTrace();
        }
    }
}

输出片段为:


i= 338
是否已停止:true
是否已停止:false
i= 339
 

至于为什么是第二个布尔值是false。参考一下官方文档对interrupted()方法的解释:

该方法测试当前线程是否已经中断,并清除该线程中断状态。

也就是说,如果连续两次调用该方法,由于第一次调用会清除其中断状态,第二次则会返回false(排除这两次之间线程再次中断的情况)

 

(2)isInterrupted()方法

isInterrupted()方法 不会清除状态标志,因此会打印两个true。

public class Thread_Test {

    public static void main(String[] args) {
        try {
            MyThread tmp  = new MyThread();
            tmp.start();
            Thread.sleep(10);

            tmp.interrupt();

            System.out.println("是否已停止:" + tmp.isInterrupted());
            System.out.println("是否已停止:" +  tmp.isInterrupted());
        }catch (InterruptedException e) {
            System.out.println("抓住");
            e.printStackTrace();
        }
    }
}

输出为:

i= 303
是否已停止:true
是否已停止:true
i= 304

 

能停止的线程——异常法

我们可以在线程中用for语句判断线程是否是停止状态,若是,则抛出异常,后面代码不再执行。

public class MyThread extends Thread {
    @Override
    public void run() {
        try {
            for(int i = 0; i < 10000; i++) {
                if(this.interrupted())  {
                    System.out.println("已是停止状态");
                    throw new InterruptedException();
                }
                System.out.println("i= " + i);
            }
                    System.out.println("for语句下方");
        }catch (InterruptedException e ) {
            System.out.println("进入MyThread类中的catch");
            e.printStackTrace();
        }
    }

}

 

public class Thread_Test {

    public static void main(String[] args) {
        try {
            MyThread tmp  = new MyThread();
            tmp.start();
            Thread.sleep(10);

            tmp.interrupt();

        }catch (InterruptedException e) {
            System.out.println("抓住");
            e.printStackTrace();
        }
        System.out.println("结束----------");
    }
}

输出:

i= 283
i= 284
i= 285
结束----------
已是停止状态
进入MyThread类中的catch
java.lang.InterruptedException
    at Thread.MyThread.run(MyThread.java:10)
 

 

sleep()中被停止

倘若线程在sleep()状态下被停止,会是什么效果:

public class MyThread extends Thread {
//   线程负责睡眠200000毫秒
    @Override
    public void run() {
        try {
            System.out.println("run 开始");
            Thread.sleep(200000);
            System.out.println("run 结束");
        }catch (InterruptedException e ) {
            System.out.println("sllep 中被停止,进入catch:" + this.isInterrupted());
            e.printStackTrace();
        }
    }
}

 

public class Thread_Test {

    public static void main(String[] args) {
        try {
            MyThread tmp  = new MyThread();
            tmp.start();
            Thread.sleep(200);

            tmp.interrupt();

        }catch (InterruptedException e) {
            System.out.println("抓住");
            e.printStackTrace();
        }
        System.out.println("结束----------");
    }
}

输出为:

run 开始
sllep 中被停止,进入catch:false
结束----------
java.lang.InterruptedException: sleep interrupted
    at java.lang.Thread.sleep(Native Method)
    at Thread.MyThread.run(MyThread.java:15)

在sleep状态下停止某一线程,会抛出InterruptedException,并清除停止状态值(即变为false)。

如果是先interrupt()后再sleep(),则同样会进入catch语句。

 

return停止线程

将interrupt()方法与return结合使用可以达到停止线程的效果:

public class MyThread extends Thread {
    @Override
    public void run() {
        while (true) {
            if(this.isInterrupted()) {
                System.out.println("停止");
                return;
            }
            System.out.println("time:" + System.currentTimeMillis());
        }
    }
}

 

public class Thread_Test {
    public static void main(String[] args) throws InterruptedException{
       MyThread thread = new MyThread();
       thread.start();
       Thread.sleep(2000);
       thread.interrupt();
    }
}

 

但还是建议使用 ”抛异常“的方法来实现线程的停止,因为在catch块中还可以将异常向上抛,使线程停止的事件得以传播。

yield()方法

yield()方法是放弃当前的CPU资源,将它让给其他任务去占用CPU执行时间。但放弃的时间不确定,有可能刚刚放弃,马上又获得CPU时间片。

我们来通过运行时间来测试yield()方法的使用效果

public class MyThread extends Thread {

    @Override
    public void run() {
        long begin = System.currentTimeMillis();
        int count = 0;
        for(int i = 0; i < 50000; i++) {
//            Thread.yield();
            count++;
        }
        long end = System.currentTimeMillis();
        System.out.println("用时:" + (end - begin));
    }
}

 

public class Thread_Test {
    public static void main(String[] args) throws InterruptedException{
       MyThread thread = new MyThread();
       thread.start();

    }
}

将取消注释的程序结果和未取消注释的程序结果来对比,发现将CPU让给其他资源导致速度变慢(取消注释)

 

线程优先级

线程剋划分优先级,优先级高的线程得到的CPU资源较多,也就是说CPU优先执行优先级较高的线程对象中的任务。

我们通过 setPriority()方法设置线程优先级。来测试下优先级带来的效果:

public class MyThread extends Thread {

    @Override
    public void run() {
       long begin = System.currentTimeMillis();
       long res = 0;
       for(int j = 0; j < 10; j++) {
           for(int i = 0; i < 50000; i++) {
               Random random = new Random();
               random.nextInt();
               res++;
           }
       }
        long end = System.currentTimeMillis();
        System.out.println("-------------------线程1用时:" + (end - begin));
    }
}

 

public class MyThread2 extends Thread{
    @Override
    public void run() {
        long begin = System.currentTimeMillis();
        long res = 0;
        for(int j = 0; j < 10; j++) {
            for(int i = 0; i < 50000; i++) {
                Random random = new Random();
                random.nextInt();
                res++;
            }
        }
        long end = System.currentTimeMillis();
        System.out.println("-------------------线程2用时:" + (end - begin));
    }
}

 

public class Thread_Test {
    public static void main(String[] args) throws InterruptedException{
        for(int i = 0; i < 5; i++) {
            MyThread t1 = new MyThread();
            t1.setPriority(10);
            t1.start();

            MyThread2 t2 = new MyThread2();
            t2.setPriority(1);
            t2.start();
        }
    }
}
 

从输出结果来看,较高优先级MyThread对象总是大部分先执行完,但不代表它全部先执行完。

同时,优先级较高的线程不一定每一次都先执行完run()方法

 

守护线程

Java线程有两种线程:用户线程和守护线程。当进程中不存在非守护线程,守护线程会自动销毁。

守护线程最典型的应用就是GC(垃圾回收器)

通过setDaemon(true)设置为守护线程,下面我们将 daemonThread 对象设置为守护线程

public class daemonThread  extends Thread{
    private int index = 0;

    @Override
    public void run() {
        try {
            while (true) {
                index++;
                System.out.println("index= " + index);
                Thread.sleep(1000);
            }
        }catch (InterruptedException e) {e.printStackTrace();}
    }
}

 

public class Thread_Test {
    public static void main(String[] args) throws InterruptedException{
       try {
           daemonThread mt = new daemonThread();
           mt.setDaemon(true);
           mt.start();
           Thread.sleep(5000);
           System.out.println("停止");
       }catch (InterruptedException e) {e.printStackTrace();}
    }
}

 


线程组

线程组的作用是可以批量管理线程或线程组对象。我们可以把线程归属到某一个线程组中,而线程组里可以有线程对象,也可以有线程组。

 

main线程所在线程组的父线程组是system

 

取出线程组里的线程

public class Run {
    public static void main(String[] args) {
        ThreadGroup group = new ThreadGroup("A");
        System.out.println(group.activeCount());
        ThreadA Aa = new ThreadA(group, "Aa");
        Aa.start();
        Thread[] threads = new Thread[group.activeCount()];
        group.enumerate(threads);
        for(int i = 0; i < threads.length; i++) {
            System.out.println(threads[i].getName());
        }
    }
}

 

线程组自动归属特性

在实例化一个线程组group时,若不指定所属的线程组,则它会自动归到当前线程对象所属的线程组,即在这个线程组添加子线程组group。

public class test {
//            activeGroupCount()  取得当前线程组对象中的子线程组数量。
//            enumerate()         将线程组中的子线程以复制的形式拷贝到groupList中。
    public static void main(String[] args) {
        System.out.println("当前线程:" + Thread.currentThread().getName() + "所属的线程组名为:" +
                Thread.currentThread().getThreadGroup().getName() + ",该组有线程组数量:" +
                Thread.currentThread().getThreadGroup().activeGroupCount());

        ThreadGroup group = new ThreadGroup("新的组"); //他会自动加到main组
        System.out.println("当前线程:" + Thread.currentThread().getName() + "所属的线程组名为:" +
                Thread.currentThread().getThreadGroup().getName() + ",该组有线程组数量:" +
                Thread.currentThread().getThreadGroup().activeGroupCount());

        ThreadGroup[] groupList = new ThreadGroup[Thread.currentThread().getThreadGroup().activeGroupCount()];
        Thread.currentThread().getThreadGroup().enumerate(groupList);
        for(int i = 0; i < groupList.length; i++) {
            System.out.println("线程组名称:" + groupList[i].getName());
        }
    }
}

 

输出:

 

 

线程组内的线程批量停止

当调用线程组group的Interrupt()方法时,可以将该组中所有正在运行的线程批量停止。

 

1级关联

1级关联是指:父对象有子对象,但不创建子孙对象。比如:在创建一些线程时,为了有效管理这些线程,通常我们创建一个线程组,将这些线程归属组内。

public class ThreadA extends Thread {
    @Override
    public void run() {
        try {
            while(!Thread.currentThread().isInterrupted()) {
                System.out.println("线程名:" + Thread.currentThread().getName());
                Thread.sleep(3000);
            }
        }catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

public class ThreadB extends Thread {
    @Override
    public void run() {
        try {
            while(!Thread.currentThread().isInterrupted()) {
                System.out.println("线程名:" + Thread.currentThread().getName());
                Thread.sleep(3000);
            }
        }catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

public class Run {
    public static void main(String[] args) {
        ThreadA ta = new ThreadA();
        ThreadB tb = new ThreadB();
        ThreadGroup group = new ThreadGroup("线程组");
        Thread threadA = new Thread(group, ta);
        Thread threadB = new Thread(group, tb);
        threadA.setName("threadA");         threadB.setName("threadB");
        threadA.start();
        threadB.start();
        System.out.println("活动的线程数:" + group.activeCount());
        System.out.println("线程组名称:" + group.getName());
    }
}

 

输出:

 

Thread(ThreadGroup group, Runnable target)

以target的run方法作为线程执行体创建新线程,属于group组

 

多级关联

父对象有子对象,子对象创建创建其子对象。

测试:在main线程组中添加一个线程组A。然后在A组添加线程对象Z。

public class Run {
    public static void main(String[] args) {
       ThreadGroup mainGroup = Thread.currentThread().getThreadGroup();
       ThreadGroup group = new ThreadGroup(mainGroup, "A");
       Runnable runnable = new Runnable() {
           @Override
           public void run() {
               try {
                   System.out.println("执行方法");
                   Thread.sleep(10000);
               }catch (InterruptedException e) {
                   e.printStackTrace();
               }
           }
       };
       Thread newThread = new Thread(group, runnable);
       newThread.setName("Z");
       newThread.start();    //线程必须启动后才会归到组A中

       ThreadGroup[] groupList = new ThreadGroup[Thread.currentThread().getThreadGroup().activeCount()];
       Thread.currentThread().getThreadGroup().enumerate(groupList);
       System.out.println("main线程有多少个子线程组:" + groupList.length + " 名字为:" + groupList[0].getName());

       Thread[] threadList = new Thread[groupList[0].activeCount()];
       groupList[0].enumerate(threadList);
        System.out.println(threadList[0].getName());

    }
}

输出:

 


SimpleDateFormat 非线程安全

SimpleDateFormat 负责日期的转换与格式化,它不是线程安全的。因此在多线程环境下,容易造成数据转换及处理的不准确。

public class MyThread extends Thread {
    private SimpleDateFormat sdf;
    private String dateStr;

    public MyThread(SimpleDateFormat sdf, String dateStr) {
        this.sdf = sdf;
        this.dateStr = dateStr;
    }

    @Override
    public void run() {
        try {
            Date date = sdf.parse(dateStr);
            String newDateStr = sdf.format(date).toString();
            if(!newDateStr.equals(dateStr)) {
                System.out.println(this.getName() + "报错:日期字符串 " + dateStr +
                        "转换成新的日期字符串 " + newDateStr);
            }
        }catch (ParseException e) {
            System.out.println("报错");
            e.printStackTrace();
        }
    }
}

public class Run {
    public static void main(String[] args) {
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
        String[] dateStrArray = new String[] {"2000-01-01", "2000-01-01", "2100-11-01", "2021-01-23",
                "2123-12-22"};
        MyThread[] threads = new MyThread[5];
        for(int i = 0; i < 5; i++) {
            threads[i] = new MyThread(sdf, dateStrArray[i]);
            threads[i].start();
        }
    }
}

输出:

Exception in thread "Thread-2" Exception in thread "Thread-4" Exception in thread "Thread-1" java.lang.NumberFormatException: multiple points
    at sun.misc.FloatingDecimal.readJavaFormatString(FloatingDecimal.java:1890)
    at sun.misc.FloatingDecimal.parseDouble(FloatingDecimal.java:110)
    at java.lang.Double.parseDouble(Double.java:538)
    at java.text.DigitList.getDouble(DigitList.java:169)
    at java.text.DecimalFormat.parse(DecimalFormat.java:2056)
    at java.text.SimpleDateFormat.subParse(SimpleDateFormat.java:1869)
    at java.text.SimpleDateFormat.parse(SimpleDateFormat.java:1514)
    at java.text.DateFormat.parse(DateFormat.java:364)
    at formatError.MyThread.run(MyThread.java:19)
java.lang.NumberFormatException: multiple points
    at sun.misc.FloatingDecimal.readJavaFormatString(FloatingDecimal.java:1890)
    at sun.misc.FloatingDecimal.parseDouble(FloatingDecimal.java:110)
    at java.lang.Double.parseDouble(Double.java:538)
    at java.text.DigitList.getDouble(DigitList.java:169)
    at java.text.DecimalFormat.parse(DecimalFormat.java:2056)
    at java.text.SimpleDateFormat.subParse(SimpleDateFormat.java:1869)
    at java.text.SimpleDateFormat.parse(SimpleDateFormat.java:1514)
    at java.text.DateFormat.parse(DateFormat.java:364)
    at formatError.MyThread.run(MyThread.java:19)
java.lang.NumberFormatException: For input string: ""
    at java.lang.NumberFormatException.forInputString(NumberFormatException.java:65)
    at java.lang.Long.parseLong(Long.java:601)
    at java.lang.Long.parseLong(Long.java:631)
    at java.text.DigitList.getLong(DigitList.java:195)
    at java.text.DecimalFormat.parse(DecimalFormat.java:2051)
    at java.text.SimpleDateFormat.subParse(SimpleDateFormat.java:1869)
    at java.text.SimpleDateFormat.parse(SimpleDateFormat.java:1514)
    at java.text.DateFormat.parse(DateFormat.java:364)
    at formatError.MyThread.run(MyThread.java:19)
Thread-3报错:日期字符串 2021-01-23转换成新的日期字符串 2000-01-01

 

 

 

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
经导师精心指导并认可、获 98 分的毕业设计项目!【项目资源】:微信小程序。【项目说明】:聚焦计算机相关专业毕设及实战操练,可作课程设计与期末大作业,含全部源码,能直用于毕设,经严格调试,运行有保障!【项目服务】:有任何使用上的问题,欢迎随时与博主沟通,博主会及时解答。 经导师精心指导并认可、获 98 分的毕业设计项目!【项目资源】:微信小程序。【项目说明】:聚焦计算机相关专业毕设及实战操练,可作课程设计与期末大作业,含全部源码,能直用于毕设,经严格调试,运行有保障!【项目服务】:有任何使用上的问题,欢迎随时与博主沟通,博主会及时解答。 经导师精心指导并认可、获 98 分的毕业设计项目!【项目资源】:微信小程序。【项目说明】:聚焦计算机相关专业毕设及实战操练,可作课程设计与期末大作业,含全部源码,能直用于毕设,经严格调试,运行有保障!【项目服务】:有任何使用上的问题,欢迎随时与博主沟通,博主会及时解答。 经导师精心指导并认可、获 98 分的毕业设计项目!【项目资源】:微信小程序。【项目说明】:聚焦计算机相关专业毕设及实战操练,可作课程设计与期末大作业,含全部源码,能直用于毕设,经严格调试,运行有保障!【项目服务】:有任何使用上的问题,欢迎随时与博主沟通,博主会及时解答。
1. 智慧监狱概述 智慧监狱的建设背景基于监狱信息化的发展历程,从最初的数字化监狱到信息化监狱,最终发展到智慧监狱。智慧监狱强调管理的精细化、监管的一体化、改造的科学化以及办公的无纸化。政策上,自2017年以来,司法部连续发布了多项指导性文件,推动智慧监狱的建设。 2. 内在需求与挑战 智慧监狱的内在需求包括数据应用与共享的不足、安防系统的单一功能、IT架构的复杂性、信息安全建设的薄弱以及IT运维的人工依赖。这些挑战要求监狱系统进行改革,以实现数据的深度利用和业务的智能化。 3. 技术架构与设计 智慧监狱的技术架构包括统一门户、信息安全、综合运维、安防集成平台和大数据平台。设计上,智慧监狱采用云计算、物联网、大数据和人工智能等技术,实现资源的动态分配、业务的快速部署和安全的主动防护。 4. 数据治理与应用 监狱数据应用现状面临数据分散和共享不足的问题。智慧监狱通过构建数据共享交换体系、数据治理工具及服务,以及基于数据仓库的数据分析模型,提升了数据的利用效率和决策支持能力。 5. 安全与运维 智慧监狱的信息安全建设涵盖了大数据应用、安全管理区、业务区等多个层面,确保了数据的安全和系统的稳定运行。同时,综合运维平台的建立,实现了IT系统的统一管理和自动化运维,提高了运维效率和系统的可靠性。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值