zoukankan      html  css  js  c++  java
  • Java API —— 多线程

    1、多线程概述
        1)进程:
            正在运行的程序,是系统进行资源分配和调用的独立单位。
            每一个进程都有它自己的内存空间和系统资源。
        2)线程:
            在同一个进程内又可以执行多个任务,而这每一个任务我就可以看出是一个线程。
        线程:是程序的执行单元,执行路径。是程序使用CPU的最基本单位。
        单线程:程序只有一条执行路径。
        多线程:程序有多条执行路径。    
        3)多线程有什么意义?
        多线程的存在,不是提高程序的执行速度。其实是为了提高应用程序的使用率。
        程序的执行其实都是在抢CPU的资源,CPU的执行权。多个进程是在抢这个资源,而其中的某一个进程如果执行路径比较多,就会有更高的几率抢到CPU的执行权。我们是不敢保证哪一个线程能够在哪个时刻抢到,所以线程的执行有随机性。
        4)并行和并发
            并发是逻辑上同时发生,指在某一个时间内同时运行多个程序。
            并行是物理上同时发生,指在某一个时间点同时运行多个程序。
        5)Java程序运行原理
            java 命令会启动 java 虚拟机,启动 JVM,等于启动了一个应用程序,也就是启动了一个进程。该进程会自动启动一个 “主线程” ,然后主线程去调用某个类的 main 方法。所以 main方法运行在主线程中。在此之前的所有程序都是单线程的。
            jvm虚拟机的启动是多线程的。因为垃圾回收线程也要先启动,否则很容易会出现内存溢出。现在的垃圾回收线程加上前面的主线程,最少启动了两个线程,所以,jvm的启动是多线程的。
     
    2、多线程实现方案
      1) Java可以去调用C/C++写好的程序来实现多线程程序。由C/C++去调用系统功能创建进程,然后由Java去调用这样的东西,然后提供一些类供我们使用。我们就可以实现多线程程序了。
      2)实现方式1:继承Thread类。将类声明为 Thread 的子类。该子类应重写 Thread 类的 run 方法。接下来可以分配并启动该子类的实例。
                A:自定义类MyThread继承Thread类。
                B:MyThread类里面重写run()
                C:创建对象
                D:启动线程
    例子1:
    自定义线程类:
    package threaddemos;
    /*
     * 该类要重写run()方法,为什么呢?
     * 不是类中的所有代码都需要被线程执行的。
     * 而这个时候,为了区分哪些代码能够被线程执行,java提供了Thread类中的run()用来包含那些被线程执行的代码。
     */
    public class MyThread extends Thread{
        @Override
        public void run(){
            for (int x = 0; x < 200; x++) {
                System.out.println(x);
            }
        }
    }

    测试类:

    package threaddemos;
    /**
     * Created by gao on 16-1-3.
     */
    public class ThreadDemo01 {
        public static void main(String[] args) {
            // 创建线程对象
            // MyThread my = new MyThread();
            // // 启动线程
            // my.run();
            // my.run();
            // 调用run()方法为什么是单线程的呢?
            // 因为run()方法直接调用其实就相当于普通的方法调用,所以你看到的是单线程的效果
            // 面试题:run()和start()的区别?
            // run():仅仅是封装被线程执行的代码,直接调用是普通方法
            // start():首先启动了线程,然后再由jvm去调用该线程的run()方法。
            // MyThread my = new MyThread();
            // my.start();
            // // IllegalThreadStateException:非法的线程状态异常
            // // 为什么呢?因为这个相当于是my线程被调用了两次。而不是两个线程启动。
            // my.start();
            // 创建两个线程对象
            MyThread my1 = new MyThread();
            MyThread my2 = new MyThread();
            my1.start();
            my2.start();
        }
    }
      3)Thread类的基本获取和设置方法
            public final String getName():返回该线程的名称。
            public final void setName(String name):改变线程名称,使之与参数 name 相同。
            其实通过构造方法也可以给线程起名字
      4)获取main方法所在的线程名称
            public static Thread currentThread():返回对当前正在执行的线程对象的引用。
    例子2:
    自定义线程:
    package threaddemos;
    public class MyThread2 extends Thread {
        public MyThread2() {
        }
        public MyThread2(String name) {
            super(name);
        }
        @Override
        public void run() {
            for (int x = 0; x < 200; x++) {
                System.out.println(getName() + "---" + x);
            }
        }
    }

     测试类:

    package threaddemos;
    /**
     * Created by gao on 16-1-3.
     */
    public class ThreadDemo02 {
        public static void main(String[] args) {
            // 创建线程对象
            //无参构造+setXxx()
    //        MyThread2 my1 = new MyThread2();
    //        MyThread2 my2 = new MyThread2();
    //        my1.setName("林青霞");
    //        my2.setName("刘意");
    //        my1.start();
    //        my2.start();
            //带参构造方法给线程起名字
            MyThread2 my1 = new MyThread2("林青霞");
            MyThread2 my2 = new MyThread2("刘意");
            my1.start();
            my2.start();
            //获取main方法所在的线程对象的名称
            //public static Thread currentThread():返回当前正在执行的线程对象
            System.out.println(Thread.currentThread().getName());
        }
    }
    /*
    名称为什么是:Thread-? 编号
    class Thread {
        private char name[];
        public Thread() {
            init(null, null, "Thread-" + nextThreadNum(), 0);
        }
        private void init(ThreadGroup g, Runnable target, String name,
                          long stackSize) {
            init(g, target, name, stackSize, null);
        }
         private void init(ThreadGroup g, Runnable target, String name,
                          long stackSize, AccessControlContext acc) {
            //大部分代码被省略了
            this.name = name.toCharArray();
        }
        public final void setName(String name) {
            this.name = name.toCharArray();
        }
        private static int threadInitNumber; //0,1,2
        private static synchronized int nextThreadNum() {
            return threadInitNumber++; //return 0,1
        }
        public final String getName() {
            return String.valueOf(name);
        }
    }
    class MyThread extends Thread {
        public MyThread() {
            super();
        }
    }
    */
      5)实现方式2:声明实现 Runnable 接口的类。该类然后实现 run 方法。然后可以分配该类的实例,在创建 Thread 时作为一个参数来传递并启动。
                A:自定义类MyRunnable实现Runnable接口
                B:重写run()方法
                C:创建MyRunnable类的对象
                D:创建Thread类的对象,并把C步骤的对象作为构造参数传递
    自定义类:
    package threaddemos;
    /**
     * Created by gao on 16-1-4.
     */
    public class MyRunnable implements Runnable {
        @Override
        public void run() {
            for (int x = 0; x < 100; x++) {
                // 由于实现接口的方式就不能直接使用Thread类的方法了,但是可以间接的使用
                System.out.println(Thread.currentThread().getName() + "---" + x);
            }
        }
    }

     测试类:

    package threaddemos;
    /**
     * Created by gao on 16-1-4.
     */
    public class MyRunnableDemo {
        public static void main(String[] args) {
            // 创建MyRunnable类的对象
            MyRunnable my = new MyRunnable();
            // 创建Thread类的对象,并把C步骤的对象作为构造参数传递
            // Thread(Runnable target)
            // Thread t1 = new Thread(my);
            // Thread t2 = new Thread(my);
            // t1.setName("林青霞");
            // t2.setName("刘意");
            // Thread(Runnable target, String name)
            Thread t1 = new Thread(my,"林青霞");
            Thread t2 = new Thread(my,"刘意");
            t1.start();
            t2.start();
        }
    }
      6)多线程两种方式的图解比较和区别:

     

    3、线程调度和线程控制
      1)线程调度
        假如我们的计算机只有一个 CPU,那么 CPU 在某一个时刻只能执行一条指令,线程只有得到 CPU时间片,也就是使用权,才可以执行指令。那么Java是
        如何对线程进行调用的呢?
        线程有两种调度模型:
          · 分时调度模型:所有线程轮流使用 CPU 的使用权,平均分配每个线程占用 CPU 的时间片.
          · 抢占式调度模型:优先让优先级高的线程使用 CPU,如果线程的优先级相同,那么会随机选择一个,优先级高的线程获取的 CPU 时间片相对多一些。
          · Java使用的是抢占式调度模型。
      2)设置和获取线程优先级
        · public final int getPriority():返回线程的优先级。
        · public final void setPriority(int newPriority):更改线程的优先级。

    package threaddemos;
    /**
     * Created by gao on 16-1-4.
     */
    /*
     * 我们的线程没有设置优先级,肯定有默认优先级。
     * 获取线程对象的优先级
     *         public final int getPriority():返回线程对象的优先级
     * 设置线程对象的优先级
     *         public final void setPriority(int newPriority):更改线程的优先级。
     *
     * 注意:
     *         线程默认优先级是5。
     *         线程优先级的范围是:1-10。
     *         线程优先级高仅仅表示线程获取的 CPU时间片的几率高,但是要在次数比较多,或者多次运行的时候才能看到比较好的效果。
     *
     * IllegalArgumentException:非法参数异常。
     * 抛出的异常表明向方法传递了一个不合法或不正确的参数。
     */
    public class ThreadPriorityDemo01 {
        public static void main(String[] args) {
            ThreadPriority tp1 = new ThreadPriority();
            ThreadPriority tp2 = new ThreadPriority();
            ThreadPriority tp3 = new ThreadPriority();
            tp1.setName("东方不败");
            tp2.setName("岳不群");
            tp3.setName("林平之");
            // 获取默认优先级
            // System.out.println(tp1.getPriority()); //5
            // System.out.println(tp2.getPriority());//5
            // System.out.println(tp3.getPriority());//5
            // 设置线程优先级(范围出错)
            // tp1.setPriority(100000);
            //设置正确的线程优先级
            tp1.setPriority(10);
            tp3.setPriority(1);
            tp1.start();
            tp2.start();
            tp3.start();
        }
    }
      3)线程控制
            · 线程休眠
                public static void sleep(long millis):在指定的毫秒数内让当前正在执行的线程休眠(暂停执行),此操作受到系统计时器和调度程序精度和准确性的影响。
            · 线程加入
                public final void join():等待该线程终止。
            · 线程礼让
                public static void yield():暂停当前正在执行的线程对象,并执行其他线程。
            · 后台线程
                public final void setDaemon(boolean on):将该线程标记为守护线程或用户线程。当正在运行的线程都是守护线程时,Java 虚拟机退出。 该方法必须在启动线程前调用。 
            · 中断线程
                public final void stop():已过时。不建议使用
                public void interrupt():中断线程
    例子1:
    自定义Thread类:
    package threaddemos;
    import java.util.Date;
    /**
     * Created by gao on 16-1-4.
     */
    public class ThreadSleep extends Thread {
        public ThreadSleep() {
        }
        @Override
        public void run() {
            for (int x = 0; x < 100; x++) {
                System.out.println(getName() + "---" + x + "日期:" + new Date());
                //睡眠,1秒钟
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    测试类:

    package threaddemos;
    /**
     * Created by gao on 16-1-4.
     */
    /*
     * 线程休眠
     *        public static void sleep(long millis)
     */
    public class ThreadSleepDemo {
        public static void main(String[] args) {
            ThreadSleep ts1 = new ThreadSleep();
            ThreadSleep ts2 = new ThreadSleep();
            ThreadSleep ts3 = new ThreadSleep();
            ts1.setName("林青霞");
            ts2.setName("林志玲");
            ts3.setName("林志颖");
            ts1.start();
            ts2.start();
            ts3.start();
            ts3.start();
        }
    }
    例子2:
    ThreadJoin类:
    package cn.itcast_04;
    public class ThreadJoin extends Thread {
        @Override
        public void run() {
            for (int x = 0; x < 100; x++) {
                System.out.println(getName() + ":" + x);
            }
        }
    }

    测试类:

    package cn.itcast_04;
    /*
     * public final void join():等待该线程终止。 
     */
    public class ThreadJoinDemo {
        public static void main(String[] args) {
            ThreadJoin tj1 = new ThreadJoin();
            ThreadJoin tj2 = new ThreadJoin();
            ThreadJoin tj3 = new ThreadJoin();
            tj1.setName("李渊");
            tj2.setName("李世民");
            tj3.setName("李元霸");
            tj1.start();
            try {
                tj1.join();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            
            tj2.start();
            tj3.start();
        }
    }
    例子3:
    ThreadYield类:
    package cn.itcast_04;
    public class ThreadYield extends Thread {
        @Override
        public void run() {
            for (int x = 0; x < 100; x++) {
                System.out.println(getName() + ":" + x);
                Thread.yield();
            }
        }
    }

     测试类:

    package cn.itcast_04;
    /*
     * public static void yield():暂停当前正在执行的线程对象,并执行其他线程。 
     * 让多个线程的执行更和谐,但是不能靠它保证一人一次。
     */
    public class ThreadYieldDemo {
        public static void main(String[] args) {
            ThreadYield ty1 = new ThreadYield();
            ThreadYield ty2 = new ThreadYield();
            ty1.setName("林青霞");
            ty2.setName("刘意");
            ty1.start();
            ty2.start();
        }
    }

    例子4:

    package cn.itcast_04;
    /*
     * public final void setDaemon(boolean on):将该线程标记为守护线程或用户线程。
     * 当正在运行的线程都是守护线程时,Java 虚拟机退出。 该方法必须在启动线程前调用。 
     * 
     * 游戏:坦克大战。
     */
    public class ThreadDaemonDemo {
        public static void main(String[] args) {
            ThreadDaemon td1 = new ThreadDaemon();
            ThreadDaemon td2 = new ThreadDaemon();
            td1.setName("关羽");
            td2.setName("张飞");
            // 设置收获线程
            td1.setDaemon(true);
            td2.setDaemon(true);
            td1.start();
            td2.start();
            Thread.currentThread().setName("刘备");
            for (int x = 0; x < 5; x++) {
                System.out.println(Thread.currentThread().getName() + ":" + x);
            }
        }
    }
    例子5:
    ThreadStop类:
    package cn.itcast_04;
    import java.util.Date;
    public class ThreadStop extends Thread {
        @Override
        public void run() {
            System.out.println("开始执行:" + new Date());
            // 我要休息10秒钟,亲,不要打扰我哦
            try {
                Thread.sleep(10000);
            } catch (InterruptedException e) {
                // e.printStackTrace();
                System.out.println("线程被终止了");
            }
            System.out.println("结束执行:" + new Date());
        }
    }

     测试类:

    package cn.itcast_04;
    /*
     * public final void stop():让线程停止,过时了,但是还可以使用。
     * public void interrupt():中断线程。 把线程的状态终止,并抛出一个InterruptedException。
     */
    public class ThreadStopDemo {
        public static void main(String[] args) {
            ThreadStop ts = new ThreadStop();
            ts.start();
            // 你超过三秒不醒过来,我就干死你
            try {
                Thread.sleep(3000);
                // ts.stop();
                ts.interrupt();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    4、线程生命周期

     
     
     
    5、卖电影票案例
        1)方式一:继承Thread类
    自定义类:
    package threaddemos;
    /**
     * Created by gao on 16-1-4.
     */
    public class SellTicket extends Thread {
        // 定义100张票
        // private int tickets = 100;
        // 为了让多个线程对象共享这100张票,我们其实应该用静态修饰
        private static int tickets = 100;
        @Override
        public void run() {
            // 定义100张票
            // 每个线程进来都会走这里,这样的话,每个线程对象相当于买的是自己的那100张票,这不合理,所以应该定义到外面
            // int tickets = 100;
            // 是为了模拟一直有票
            while (true) {
                if (tickets > 0) {
                    System.out.println(getName() + "---" + "正在出售第" + (tickets--) + "张票");
                }
            }
        }
    }

    测试类:

    package threaddemos;
    /**
     * Created by gao on 16-1-4.
     */
    /*
     * 某电影院目前正在上映贺岁大片(红高粱,少林寺传奇藏经阁),共有100张票,而它有3个售票窗口售票,请设计一个程序模拟该电影院售票。
     * 继承Thread类来实现。
     */
    public class SellTicketDemo {
        public static void main(String[] args) {
            // 创建三个线程对象
            SellTicket st1 = new SellTicket();
            SellTicket st2 = new SellTicket();
            SellTicket st3 = new SellTicket();
            // 给线程对象起名字
            st1.setName("窗口1");
            st2.setName("窗口2");
            st3.setName("窗口3");
            // 启动线程
            st1.start();
            st2.start();
            st3.start();
        }
    }

         2)方式二:实现Runnable接口

    自定义类:
    package threaddemos;
    /**
     * Created by gao on 16-1-4.
     */
    public class SellTicket2 implements Runnable {
        private int tickets = 200;
        @Override
        public void run() {
            while (true) {
                if (tickets > 0) {
                    System.out.println(Thread.currentThread().getName() + "---" + "正在出售第" + (tickets--) + "张票");
                }
            }
        }
    }

    测试类:

    package threaddemos;
    /**
     * Created by gao on 16-1-4.
     */
    /*
     *  实现Runnable接口的方式实现
     */
    public class SellTicketDemo2 {
        public static void main(String[] args) {
            // 创建资源对象
            SellTicket2 st = new SellTicket2();
            // 创建三个线程对象
           Thread t1 = new Thread(st,"窗口1");
           Thread t2 = new Thread(st,"窗口2");
           Thread t3 = new Thread(st,"窗口3");
            // 启动线程
            t1.start();
            t2.start();
            t3.start();
        }
    }
     
    6、线程同步
        1)出现的问题:
            · 相同的票出现多次:CPU的一次操作必须是原子性的
            · 还出现了负数的票:随机性和延迟导致的
        2)解决线程安全问题的基本思想
            · 首先想为什么出现问题?(也是我们判断是否有问题的标准)
                A:是否是多线程环境
                B:是否有共享数据
                C:是否有多条语句操作共享数据
            · 如何解决多线程安全问题呢?
                · 基本思想:让程序没有安全问题的环境。
                · 怎么实现呢?
                    · 把多个语句操作共享数据的代码给锁起来,让任意时刻只能有一个线程执行即可。
        3)解决线程安全问题实现1:同步代码块
            格式:
        synchronized(对象){需要同步的代码;}
            同步可以解决安全问题的根本原因就在那个对象上。该对象如同锁的功能。
            同步代码块是把多条语句操作共享数据的代码的部分给包起来。
    package threaddemos;
    /**
     * Created by gao on 16-1-4.
     */
    public class SellTicket3 implements Runnable {
        // 定义100张票
        private int tickets = 50;
        //创建锁对象
        private Object obj = new Object();
        @Override
        public void run() {
            while (true) {
                synchronized (obj) {
                    if (tickets > 0) {
                        try {
                            Thread.sleep(100);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                        System.out.println(Thread.currentThread().getName()
                                + "正在出售第" + (tickets--) + "张票");
                    }
                }
            }
        }
    //    @Override
    //    public void run() {
    //
    //        while (true) {
    //            if (tickets > 0) {
    //                System.out.println(Thread.currentThread().getName() + "---" + "正在出售第" + (tickets--) + "张票");
    //            }
    //        }
    //    }
    }
    测试类:
    package threaddemos;
    /**
     * Created by gao on 16-1-4.
     */
    /*
     *  实现Runnable接口的方式实现
     */
    public class SellTicketDemo3 {
        public static void main(String[] args) {
            // 创建资源对象
            SellTicket3 st = new SellTicket3();
            // 创建三个线程对象
           Thread t1 = new Thread(st,"窗口1");
           Thread t2 = new Thread(st,"窗口2");
           Thread t3 = new Thread(st,"窗口3");
            // 启动线程
            t1.start();
            t2.start();
            t3.start();
        }
    }

       4)同步的特点

            · 同步的前提
                · 多个线程
                · 多个线程使用的是同一个锁对象
            · 同步的好处
                · 同步的出现解决了多线程的安全问题。
            · 同步的弊端
                · 当线程相当多时,因为每个线程都会去判断同步上的锁,这是很耗费资源的,无形中会降低程序的运行效率。
     
        5)解决线程安全问题实现2
    package threaddemos;
    /**
     * Created by gao on 16-1-4.
     */
    public class SellTicket4 implements Runnable {
        // 定义100张票
        private static int tickets = 100;
        // 定义同一把锁
        private Object obj = new Object();
        private Demo d = new Demo();
        private int x = 0;
        //同步代码块用obj做锁
    //    @Override
    //    public void run() {
    //        while (true) {
    //            synchronized (obj) {
    //                if (tickets > 0) {
    //                    try {
    //                        Thread.sleep(100);
    //                    } catch (InterruptedException e) {
    //                        e.printStackTrace();
    //                    }
    //                    System.out.println(Thread.currentThread().getName()
    //                            + "正在出售第" + (tickets--) + "张票 ");
    //                }
    //            }
    //        }
    //    }
        //同步代码块用任意对象做锁
    //    @Override
    //    public void run() {
    //        while (true) {
    //            synchronized (d) {
    //                if (tickets > 0) {
    //                    try {
    //                        Thread.sleep(100);
    //                    } catch (InterruptedException e) {
    //                        e.printStackTrace();
    //                    }
    //                    System.out.println(Thread.currentThread().getName()
    //                            + "正在出售第" + (tickets--) + "张票 ");
    //                }
    //            }
    //        }
    //    }
        @Override
        public void run() {
            while (true) {
                if(x%2==0){
                    //加载class文件当锁
                    synchronized (SellTicket.class) {
                        if (tickets > 0) {
                            try {
                                Thread.sleep(100);
                            } catch (InterruptedException e) {
                                e.printStackTrace();
                            }
                            System.out.println(Thread.currentThread().getName()
                                    + "正在出售第" + (tickets--) + "张票 ");
                        }
                    }
                }else {
    //                synchronized (d) {
    //                    if (tickets > 0) {
    //                        try {
    //                            Thread.sleep(100);
    //                        } catch (InterruptedException e) {
    //                            e.printStackTrace();
    //                        }
    //                        System.out.println(Thread.currentThread().getName()
    //                                + "正在出售第" + (tickets--) + "张票 ");
    //                    }
    //                }
                    sellTicket();
                }
                x++;
            }
        }
    //    private void sellTicket() {
    //        synchronized (d) {
    //            if (tickets > 0) {
    //            try {
    //                    Thread.sleep(100);
    //            } catch (InterruptedException e) {
    //                    e.printStackTrace();
    //            }
    //            System.out.println(Thread.currentThread().getName()
    //                        + "正在出售第" + (tickets--) + "张票 ");
    //            }
    //        }
    //    }
        //如果一个方法一进去就看到了代码被同步了,那么我就再想能不能把这个同步加在方法上呢?
    //     private synchronized void sellTicket() {
    //            if (tickets > 0) {
    //            try {
    //                    Thread.sleep(100);
    //            } catch (InterruptedException e) {
    //                    e.printStackTrace();
    //            }
    //            System.out.println(Thread.currentThread().getName()
    //                        + "正在出售第" + (tickets--) + "张票 ");
    //            }
    //    }
        private static synchronized void sellTicket() {
            if (tickets > 0) {
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(Thread.currentThread().getName()
                        + "正在出售第" + (tickets--) + "张票 ");
            }
        }
    }
    class Demo {
    }

    测试类:

    package threaddemos;
    /**
     * Created by gao on 16-1-4.
     */
    /*
     *  实现Runnable接口的方式实现
     */
        /*
     * A:同步代码块的锁对象是谁呢?
     *         任意对象。
     *
     * B:同步方法的格式及锁对象问题?
     *         把同步关键字加在方法上。
     *
     *         同步方法是谁呢?
     *             this
     *
     * C:静态方法及锁对象问题?
     *         静态方法的锁对象是谁呢?
     *             类的字节码文件对象。
     */
    public class SellTicketDemo4 {
        public static void main(String[] args) {
            // 创建资源对象
            SellTicket3 st = new SellTicket3();
            // 创建三个线程对象
           Thread t1 = new Thread(st,"窗口1");
           Thread t2 = new Thread(st,"窗口2");
           Thread t3 = new Thread(st,"窗口3");
            // 启动线程
            t1.start();
            t2.start();
            t3.start();
        }
    }

        6)以往的线程安全类

    package cn.itcast_12;
    import java.util.ArrayList;
    import java.util.Collections;
    import java.util.Hashtable;
    import java.util.List;
    import java.util.Vector;
    public class ThreadDemo {
        public static void main(String[] args) {
            // 线程安全的类
            StringBuffer sb = new StringBuffer();
            Vector<String> v = new Vector<String>();
            Hashtable<String, String> h = new Hashtable<String, String>();
            // Vector是线程安全的时候才去考虑使用的,但是不会用Vector,而是用下面的。
            // public static <T> List<T> synchronizedList(List<T> list)
            List<String> list1 = new ArrayList<String>();// 线程不安全
            List<String> list2 = Collections
                    .synchronizedList(new ArrayList<String>()); // 线程安全
        }
    }
     
     
     
     
  • 相关阅读:
    odoo开发笔记 -- 新建模块扩展原模块增加菜单示例
    div内部div居中
    Css中!important的用法
    SQLServer日期格式转换
    jquery中innerheight outerHeight()与height()的区别
    简单明了区分escape、encodeURI和encodeURIComponent
    PDF预览之PDFObject.js总结
    PDFObject.js,在页面显示PDF文件
    System.IO.Directory.GetCurrentDirectory与System.Windows.Forms.Application.StartupPath的用法
    angular 模块 @NgModule的使用及理解
  • 原文地址:https://www.cnblogs.com/yangyquin/p/5098179.html
Copyright © 2011-2022 走看看