精华内容
下载资源
问答
  • 当你的方法需要用到本方法没有的数据时就需要参数,写在方法声明的小...明白了为什么封方法,这个方法做什么了,就自然知道该添加什么参数了,知道了方法的目的是什么,就自然知道了,返回值是什么。作为初学者,还是

    当你的方法需要用到本方法没有的数据时就需要参数,写在方法声明的小括号里,这个参数叫形式参数,将来调用者调用这个方法的时候,需要按照你定义的小括号里面的参数列表去传递数据,调用者传进方法里的数据就是实际参数。 调用这个方法会返回数据的时候就要有返回值,如果只是打印或者不返回数据,就不需要定义返回值。

    得形成思想,为什么要封这个方法?等这个方法达到的目的是什么?这个方法做什么?明白了为什么封方法,这个方法做什么了,就自然知道该添加什么参数了,知道了方法的目的是什么,就自然知道了,返回值是什么。作为初学者,还是先一步一步来,先用,先敲。

    展开全文
  • 该系列不是讨论关键字的用法或用处,是推测为什么会有这样的设计和规范,是大脑训练工厂 在推理中,我们可以使用一种悖论的技巧,就是说如果某种假设成立,则现实中发生的现象将不可能存在,如果要求现实中的现象...

    该系列不是讨论关键字的用法或用处,是推测为什么会有这样的设计和规范,是大脑训练工厂

    在推理中,我们可以使用一种叫悖论的技巧,就是说如果某种假设成立,则现实中发生的现象将不可能存在,如果要求现实中的现象合理,就必须重新设立假设。这种模式其实质是一种选言推理。

    我们先了解什么是断言推理: 选言推理是根据选言命题的逻辑性质而进行的推理。选言命题有相容与不相容之分,相应地,选言推理分为相容选言推理和不相容选言推理两种。

    相容选言推理就是以相容选言命题为前提,根据相容选言命题的逻辑性质进行的推理。相容选言推理有两条规则:

    n 规则1:否定一部分选言支,就要肯定另一部分选言支

    n 规则2:肯定一部分选言支,不能否定另一部分选言支

    典型的案例是: A是程序员或者是作家,A不是作家,则A是程序员。很重要的是规则2,如果我们这样判断就是错的:A是程序员或者是作家,A是作家,则A不是程序员。这样违法了第二规则,因为A可能既是程序员也是作家。

    不相容选言推理就是以不相容选言命题为前提,根据不相容选言命题的逻辑性质进行的推理。 不相容选言推理有两条规则:

    n 规则1:否定一部分选言支,就要肯定另一部分选言支

    n 规则2:肯定一部分选言支,就要否定另一部分选言支

    典型的案例是:要么A是冠军,要么B是冠军。A不是冠军,则B必定是冠军。

    现在我们来推理我们本次的主题:函数的重载竟然不包含返回值!这个现象让很多人百思不得其解,本节我们将向读者说明为什么重载不能包含返回值。

    重载:每个类型成员都有一个唯一的签名。方法签名由方法名称和一个参数列表(方法的参数的顺序和类型)组成。只要签名不同,就可以在一种类型内定义具有相同名称的多种方法。当定义两种或多种具有相同名称的方法时,就称作重载。不过重载的策略中不包含返回值。

    我们可以设计一系列方法名称相同,但参数列表不相同的函数,为类的使用者提供非常丰富的功能组合。我们可以想像下,如果重载允许有返回值,我们不就可以为用户提供更加丰富的组合吗?那为什么C#不允许在重载策略中包含返回值呢?

    原因在于有一种函数叫:构造函数!构造函数是具有与类相同的名称但没有返回值的函数。

    现在我们定义选言命题为:要么重载能实现在所有函数规则上,要么构造函数不是函数。

    现在我们就遇到了由选言命题引发的悖论了:如果重载策略包含了返回值,那么构造函数就不能完整的实现重载策略。如果要求构造函数必须有返回值,则显然和事实不符。如果要求构造函数也能完整的实现重载策略,则重载策略必须不能包含返回值。

    你看,为了解决这个悖论,我们对上述的选言命题只能作选言命题,即结果是:重载必须实现在所有函数规则上。那么为了满足所有函数的规则,那么重载只能放弃包含返回值了。


    本文转自shyleoking 51CTO博客,原文链接:http://blog.51cto.com/shyleoking/803156


    展开全文
  • 1.什么是对象,为什么需要对象? 对象是类的一个实例(对象不是找个女朋友),有状态和行为。万物皆对象! 2.什么是函数,为什么需要函数? 习惯把函数也成方法,都是一个意思;函数是具备特定功能的一段代码块...

    1.什么是对象,为什么需要对象?

    对象是类的一个实例(对象不是找个女朋友),有状态和行为。万物皆对象!

    2.什么是函数,为什么需要函数?

    习惯把函数也叫成方法,都是一个意思;函数是具备特定功能的一段代码块,解决了重复性代码的问题。
    目的是为了提高程序的复用性和可读性。

    3.什么是函数的返回值,什么是函数的参数?

    函数返回值就是你所调用的函数返回给你的值。
    形参:用来接收调用该方法时传递的参数。只有在被调用的时候才分配内存空间,一旦调用结束,就释放内存空间。因此仅仅在方法内有效。
    实参:传递给被调用方法的值,预先创建并赋予确定值。

     

    4.什么时候加返回值,什么时候加参数

    package Test01;
    public class Test01 {
    public static void main(String[] args) {
    Test01 test = new Test01();
    //无参无返回
    test.test1();
    //有参无返回
    test.test2("java");
    //无参有返回
    String test3 = test.test3();
    System.out.println(test3);
    //有参数有返回
    String test4 = test.test4("java");
    System.out.println(test4);
    }
    //无参无返回
    public void test1() {
    System.out.println("学:java!");
    }
    //有参无返回
    public void test2(String name) {
    System.out.println("学习:"+name+"!");
    }
    //无参有返回
    public String test3() {
    return "欢迎学习java";
    }
    //有参有返回
    public String test4(String name) {
    return "欢迎大家一起学习"+name+"!";
    }

    }

    注意:

    有返回值必须return

    详情请参考

    http://www.cnblogs.com/JinQing/p/6237013.html

    转载于:https://www.cnblogs.com/dmdy/p/10914896.html

    展开全文
  • 线程简介什么是线程现代操作系统调度的最小单元是线程,也轻量级进程(Light Weight Process),在一个进程里可以创建多个线程,这些线程都拥有各自的计数器、堆栈和局部变量等属性,并且能够访问共享的内存变量。...

    e9ccafe7b433be0fc370cb0ca0151367.png

    线程简介

    什么是线程

    现代操作系统调度的最小单元是线程,也叫轻量级进程(Light Weight Process),在一个进程里可以创建多个线程,这些线程都拥有各自的计数器、堆栈和局部变量等属性,并且能够访问共享的内存变量。

    线程生命周期

    8fea4a3faeab0c02e33d1f28126976c0.png

    java.lang.Thread.State 中定义了 6 种不同的线程状态,在给定的一个时刻,线程只能处于其中的一个状态。

    以下是各状态的说明,以及状态间的联系:

    • 开始(New) - 还没有调用 start() 方法的线程处于此状态。
    • 可运行(Runnable) - 已经调用了 start() 方法的线程状态。此状态意味着,线程已经准备好了,一旦被线程调度器分配了 CPU 时间片,就可以运行线程。
    • 阻塞(Blocked) - 阻塞状态。线程阻塞的线程状态等待监视器锁定。处于阻塞状态的线程正在等待监视器锁定,以便在调用 Object.wait() 之后输入同步块/方法或重新输入同步块/方法。
    • 等待(Waiting) - 等待状态。一个线程处于等待状态,是由于执行了 3 个方法中的任意方法:
      • Object.wait()
      • Thread.join()
      • LockSupport.park()
    • 定时等待(Timed waiting) - 等待指定时间的状态。一个线程处于定时等待状态,是由于执行了以下方法中的任意方法:
      • Thread.sleep(sleeptime)
      • Object.wait(timeout)
      • Thread.join(timeout)
      • LockSupport.parkNanos(timeout)
      • LockSupport.parkUntil(timeout)
    • 终止(Terminated) - 线程 run() 方法执行结束,或者因异常退出了 run() 方法,则该线程结束生命周期。死亡的线程不可再次复生。

    启动和终止线程

    构造线程

    构造线程主要有三种方式

    • 继承 Thread
    • 实现 Runnable 接口
    • 实现 Callable 接口

    继承 Thread 类

    通过继承 Thread 类构造线程的步骤:

    • 定义 Thread 类的子类,并重写该类的 run() 方法,该 run() 方法的方法体就代表了线程要完成的任务。因此把 run() 方法称为执行体。
    • 创建 Thread 子类的实例,即创建了线程对象。
    • 调用线程对象的 start() 方法来启动该线程。

    示例:

    public class ThreadDemo02 {
    
        public static void main(String[] args) {
            Thread02 mt1 = new Thread02("线程A "); // 实例化对象
            Thread02 mt2 = new Thread02("线程B "); // 实例化对象
            mt1.start(); // 调用线程主体
            mt2.start(); // 调用线程主体
        }
    
        static class Thread02 extends Thread {
    
            private int ticket = 5;
    
            Thread02(String name) {
                super(name);
            }
    
            @Override
            public void run() {
                for (int i = 0; i < 100; i++) {
                    if (this.ticket > 0) {
                        System.out.println(this.getName() + " 卖票:ticket = " + ticket--);
                    }
                }
            }
        }
    }

    实现 Runnable 接口

    通过实现 Runnable 接口构造线程的步骤:

    • 定义 Runnable 接口的实现类,并重写该接口的 run() 方法,该 run() 方法的方法体同样是该线程的线程执行体。
    • 创建 Runnable 实现类的实例,并依此实例作为 Thread 的 target 来创建 Thread 对象,该 Thread 对象才是真正的线程对象。
    • 调用线程对象的 start() 方法来启动该线程。

    示例:

    public class RunnableDemo {
    
        public static void main(String[] args) {
            MyThread t = new MyThread("Runnable 线程"); // 实例化对象
            new Thread(t).run(); // 调用线程主体
            new Thread(t).run(); // 调用线程主体
            new Thread(t).run(); // 调用线程主体
        }
    
        static class MyThread implements Runnable {
    
            private int ticket = 5;
            private String name;
    
            MyThread(String name) {
                this.name = name;
            }
    
            @Override
            public void run() {
                for (int i = 0; i < 100; i++) {
                    if (this.ticket > 0) {
                        System.out.println(this.name + " 卖票:ticket = " + ticket--);
                    }
                }
            }
        }
    }

    实现 Callable 接口

    通过实现 Callable 接口构造线程的步骤:

    • 创建 Callable 接口的实现类,并实现 call() 方法,该 call() 方法将作为线程执行体,并且有返回值。
    • 创建 Callable 实现类的实例,使用 FutureTask 类来包装 Callable 对象,该 FutureTask 对象封装了该 Callable 对象的 call() 方法的返回值。
    • 使用 FutureTask 对象作为 Thread 对象的 target 创建并启动新线程。
    • 调用 FutureTask 对象的 get() 方法来获得子线程执行结束后的返回值。

    示例:

    public class CallableAndFutureDemo {
    
        public static void main(String[] args) {
            Callable<Integer> callable = () -> new Random().nextInt(100);
            FutureTask<Integer> future = new FutureTask<>(callable);
            new Thread(future).start();
            try {
                Thread.sleep(1000);// 可能做一些事情
                System.out.println(future.get());
            } catch (InterruptedException | ExecutionException e) {
                e.printStackTrace();
            }
        }
    }

    三种创建线程方式对比

    • 实现 Runnable 接口优于继承 Thread 类,因为实现接口方式更便于扩展类。
    • 实现 Runnable 接口的线程没有返回值;而实现 Callable 接口的线程有返回值

    中断线程

    当一个线程运行时,另一个线程可以直接通过 interrupt() 方法中断其运行状态。

    public class ThreadInterruptDemo {
    
        public static void main(String[] args) {
            MyThread mt = new MyThread(); // 实例化Runnable子类对象
            Thread t = new Thread(mt, "线程"); // 实例化Thread对象
            t.start(); // 启动线程
            try {
                Thread.sleep(2000); // 线程休眠2秒
            } catch (InterruptedException e) {
                System.out.println("3、休眠被终止");
            }
            t.interrupt(); // 中断线程执行
        }
    
        static class MyThread implements Runnable {
    
            @Override
            public void run() {
                System.out.println("1、进入run()方法");
                try {
                    Thread.sleep(10000); // 线程休眠10秒
                    System.out.println("2、已经完成了休眠");
                } catch (InterruptedException e) {
                    System.out.println("3、休眠被终止");
                    return; // 返回调用处
                }
                System.out.println("4、run()方法正常结束");
            }
        }
    }

    终止线程

    Thread 中的 stop 方法有缺陷,已废弃。

    安全地终止线程有两种方法:

    1. 中断状态是线程的一个标识位,而中断操作是一种简便的线程间交互方式,而这种交互方式最适合用来取消或停止任务。
    2. 还可以利用一个 boolean 变量来控制是否需要停止任务并终止该线程。
    public class ThreadStopDemo03 {
    
        public static void main(String[] args) throws Exception {
            MyTask one = new MyTask();
            Thread countThread = new Thread(one, "CountThread");
            countThread.start();
            // 睡眠1秒,main线程对CountThread进行中断,使CountThread能够感知中断而结束
            TimeUnit.SECONDS.sleep(1);
            countThread.interrupt();
            MyTask two = new MyTask();
            countThread = new Thread(two, "CountThread");
            countThread.start();
            // 睡眠1秒,main线程对Runner two进行取消,使CountThread能够感知on为false而结束
            TimeUnit.SECONDS.sleep(1);
            two.cancel();
        }
    
        private static class MyTask implements Runnable {
    
            private long i;
            private volatile boolean on = true;
    
            @Override
            public void run() {
                while (on && !Thread.currentThread().isInterrupted()) {
                    i++;
                }
                System.out.println("Count i = " + i);
            }
    
            void cancel() {
                on = false;
            }
        }
    }

    Thread 中的重要方法

    • run - 线程的执行实体。
    • start - 线程的启动方法。
    • setNamegetName - 可以通过 setName()、 getName() 来设置、获取线程名称。
    • setPrioritygetPriority - 在 Java 中,所有线程在运行前都会保持在就绪状态,那么此时,哪个线程优先级高,哪个线程就有可能被先执行。可以通过 setPriority、getPriority 来设置、获取线程优先级。
    • setDaemonisDaemon - 可以使用 setDaemon() 方法设置线程为守护线程;可以使用 isDaemon() 方法判断线程是否为守护线程。
    • isAlive - 可以通过 isAlive 来判断线程是否启动。
    • interrupt - 当一个线程运行时,另一个线程可以直接通过 interrupt() 方法中断其运行状态。
    • join - 使用 join() 方法让一个线程强制运行,线程强制运行期间,其他线程无法运行,必须等待此线程完成之后才可以继续执行。
    • Thread.sleep - 使用 Thread.sleep() 方法即可实现休眠。
    • Thread.yield - 可以使用 Thread.yield() 方法将一个线程的操作暂时让给其他线程执行。

    设置/获取线程名称

    在 Thread 类中可以通过 setName()getName() 来设置、获取线程名称。

    public class ThreadNameDemo {
    
        public static void main(String[] args) {
            MyThread mt = new MyThread(); // 实例化Runnable子类对象
            new Thread(mt).start(); // 系统自动设置线程名称
            new Thread(mt, "线程-A").start(); // 手工设置线程名称
            Thread t = new Thread(mt); // 手工设置线程名称
            t.setName("线程-B");
            t.start();
        }
    
        static class MyThread implements Runnable {
    
            @Override
            public void run() {
                for (int i = 0; i < 3; i++) {
                    System.out.println(Thread.currentThread().getName() + "运行,i = " + i); // 取得当前线程的名字
                }
            }
        }
    }

    判断线程是否启动

    在 Thread 类中可以通过 isAlive() 来判断线程是否启动。

    public class ThreadAliveDemo {
    
        public static void main(String[] args) {
            MyThread mt = new MyThread(); // 实例化Runnable子类对象
            Thread t = new Thread(mt, "线程"); // 实例化Thread对象
            System.out.println("线程开始执行之前 --> " + t.isAlive()); // 判断是否启动
            t.start(); // 启动线程
            System.out.println("线程开始执行之后 --> " + t.isAlive()); // 判断是否启动
            for (int i = 0; i < 3; i++) {
                System.out.println(" main运行 --> " + i);
            }
            // 以下的输出结果不确定
            System.out.println("代码执行之后 --> " + t.isAlive()); // 判断是否启动
    
        }
    
        static class MyThread implements Runnable {
    
            @Override
            public void run() {
                for (int i = 0; i < 3; i++) {
                    System.out.println(Thread.currentThread().getName() + "运行,i = " + i);
                }
            }
        }
    }

    守护线程

    在 Java 程序中,只要前台有一个线程在运行,则整个 Java 进程就不会消失,所以此时可以设置一个守护线程,这样即使 Java 进程结束了,此守护线程依然会继续执行。可以使用 setDaemon() 方法设置线程为守护线程;可以使用 isDaemon() 方法判断线程是否为守护线程。

    public class ThreadDaemonDemo {
    
        public static void main(String[] args) {
            Thread t = new Thread(new MyThread(), "线程");
            t.setDaemon(true); // 此线程在后台运行
            System.out.println("线程 t 是否是守护进程:" + t.isDaemon());
            t.start(); // 启动线程
        }
    
        static class MyThread implements Runnable {
    
            @Override
            public void run() {
                while (true) {
                    System.out.println(Thread.currentThread().getName() + "在运行。");
                }
            }
        }
    }

    设置/获取线程优先级

    在 Java 中,所有线程在运行前都会保持在就绪状态,那么此时,哪个线程优先级高,哪个线程就有可能被先执行。

    public class ThreadPriorityDemo {
    
        public static void main(String[] args) {
            System.out.println("主方法的优先级:" + Thread.currentThread().getPriority());
            System.out.println("MAX_PRIORITY = " + Thread.MAX_PRIORITY);
            System.out.println("NORM_PRIORITY = " + Thread.NORM_PRIORITY);
            System.out.println("MIN_PRIORITY = " + Thread.MIN_PRIORITY);
    
            Thread t1 = new Thread(new MyThread(), "线程A"); // 实例化线程对象
            Thread t2 = new Thread(new MyThread(), "线程B"); // 实例化线程对象
            Thread t3 = new Thread(new MyThread(), "线程C"); // 实例化线程对象
            t1.setPriority(Thread.MIN_PRIORITY); // 优先级最低
            t2.setPriority(Thread.MAX_PRIORITY); // 优先级最低
            t3.setPriority(Thread.NORM_PRIORITY); // 优先级最低
            t1.start(); // 启动线程
            t2.start(); // 启动线程
            t3.start(); // 启动线程
        }
    
        static class MyThread implements Runnable {
    
            @Override
            public void run() {
                for (int i = 0; i < 5; i++) {
                    try {
                        Thread.sleep(500); // 线程休眠
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    // 取得当前线程的名字
                    String out =
                        Thread.currentThread().getName() + ",优先级:" + Thread.currentThread().getPriority() + ",运行:i = " + i;
                    System.out.println(out);
                }
            }
        }
    }

    线程间通信

    wait/notify/notifyAll

    wait、notify、notifyAll 是 Object 类中的方法。

    • wait - 线程自动释放其占有的对象锁,并等待 notify。
    • notify - 唤醒一个正在 wait 当前对象锁的线程,并让它拿到对象锁。
    • notifyAll - 唤醒所有正在 wait 前对象锁的线程。

    生产者、消费者示例:

    public class ThreadWaitNotifyDemo02 {
    
        private static final int QUEUE_SIZE = 10;
        private static final PriorityQueue<Integer> queue = new PriorityQueue<>(QUEUE_SIZE);
    
        public static void main(String[] args) {
            new Producer("生产者A").start();
            new Producer("生产者B").start();
            new Consumer("消费者A").start();
            new Consumer("消费者B").start();
        }
    
        static class Consumer extends Thread {
    
            Consumer(String name) {
                super(name);
            }
    
            @Override
            public void run() {
                while (true) {
                    synchronized (queue) {
                        while (queue.size() == 0) {
                            try {
                                System.out.println("队列空,等待数据");
                                queue.wait();
                            } catch (InterruptedException e) {
                                e.printStackTrace();
                                queue.notifyAll();
                            }
                        }
                        queue.poll(); // 每次移走队首元素
                        queue.notifyAll();
                        try {
                            Thread.sleep(500);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                        System.out.println(Thread.currentThread().getName() + " 从队列取走一个元素,队列当前有:" + queue.size() + "个元素");
                    }
                }
            }
        }
    
        static class Producer extends Thread {
    
            Producer(String name) {
                super(name);
            }
    
            @Override
            public void run() {
                while (true) {
                    synchronized (queue) {
                        while (queue.size() == QUEUE_SIZE) {
                            try {
                                System.out.println("队列满,等待有空余空间");
                                queue.wait();
                            } catch (InterruptedException e) {
                                e.printStackTrace();
                                queue.notifyAll();
                            }
                        }
                        queue.offer(1); // 每次插入一个元素
                        queue.notifyAll();
                        try {
                            Thread.sleep(500);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                        System.out.println(Thread.currentThread().getName() + " 向队列取中插入一个元素,队列当前有:" + queue.size() + "个元素");
                    }
                }
            }
        }
    }

    线程的礼让

    在线程操作中,可以使用 Thread.yield() 方法将一个线程的操作暂时让给其他线程执行。

    public class ThreadYieldDemo {
    
        public static void main(String[] args) {
            MyThread t = new MyThread();
            new Thread(t, "线程A").start();
            new Thread(t, "线程B").start();
        }
    
        static class MyThread implements Runnable {
    
            @Override
            public void run() {
                for (int i = 0; i < 5; i++) {
                    try {
                        Thread.sleep(1000);
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                    System.out.println(Thread.currentThread().getName() + "运行,i = " + i);
                    if (i == 2) {
                        System.out.print("线程礼让:");
                        Thread.yield();
                    }
                }
            }
        }
    }

    线程的强制执行

    在线程操作中,可以使用 join() 方法让一个线程强制运行,线程强制运行期间,其他线程无法运行,必须等待此线程完成之后才可以继续执行。

    public class ThreadJoinDemo {
    
        public static void main(String[] args) {
            MyThread mt = new MyThread(); // 实例化Runnable子类对象
            Thread t = new Thread(mt, "mythread"); // 实例化Thread对象
            t.start(); // 启动线程
            for (int i = 0; i < 50; i++) {
                if (i > 10) {
                    try {
                        t.join(); // 线程强制运行
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                System.out.println("Main 线程运行 --> " + i);
            }
        }
    
        static class MyThread implements Runnable {
    
            @Override
            public void run() {
                for (int i = 0; i < 50; i++) {
                    System.out.println(Thread.currentThread().getName() + " 运行,i = " + i); // 取得当前线程的名字
                }
            }
        }
    }

    线程的休眠

    直接使用 Thread.sleep() 方法即可实现休眠。

    public class ThreadSleepDemo {
    
        public static void main(String[] args) {
            new Thread(new MyThread("线程A", 1000)).start();
            new Thread(new MyThread("线程A", 2000)).start();
            new Thread(new MyThread("线程A", 3000)).start();
        }
    
        static class MyThread implements Runnable {
    
            private String name;
            private int time;
    
            private MyThread(String name, int time) {
                this.name = name; // 设置线程名称
                this.time = time; // 设置休眠时间
            }
    
            @Override
            public void run() {
                try {
                    Thread.sleep(this.time); // 休眠指定的时间
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(this.name + "线程,休眠" + this.time + "毫秒。");
            }
        }
    }

    ThreadLocal

    ThreadLocal,很多地方叫做线程本地变量,也有些地方叫做线程本地存储,其实意思差不多。可能很多朋友都知道 ThreadLocal 为变量在每个线程中都创建了一个副本,那么每个线程可以访问自己内部的副本变量。

    源码

    ThreadLocal 的主要方法:

    public class ThreadLocal<T> {
        public T get() {}
    	public void remove() {}
    	public void set(T value) {}
    	public static <S> ThreadLocal<S> withInitial(Supplier<? extends S> supplier) {}
    }
    • get()方法是用来获取 ThreadLocal 在当前线程中保存的变量副本。
    • set()用来设置当前线程中变量的副本。
    • remove()用来移除当前线程中变量的副本。
    • initialValue()是一个 protected 方法,一般是用来在使用时进行重写的,它是一个延迟加载方法,下面会详细说明。

    get() 源码实现

    get 源码

    public T get() {
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);
        if (map != null) {
            ThreadLocalMap.Entry e = map.getEntry(this);
            if (e != null) {
                @SuppressWarnings("unchecked")
                T result = (T)e.value;
                return result;
            }
        }
        return setInitialValue();
    }
    1. 取得当前线程。
    2. 通过 getMap() 方法获取 ThreadLocalMap。
    3. 成功,返回 value;失败,返回 setInitialValue()。

    ThreadLocalMap 源码实现

    ThreadLocalMap 源码

    ThreadLocalMap 是 ThreadLocal 的一个内部类。

    ThreadLocalMap 的 Entry 继承了 WeakReference,并且使用 ThreadLocal 作为键值。

    setInitialValue 源码实现

    private T setInitialValue() {
        T value = initialValue();
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);
        if (map != null)
            map.set(this, value);
        else
            createMap(t, value);
        return value;
    }

    如果 map 不为空,就设置键值对;为空,再创建 Map,看一下 createMap 的实现:

    void createMap(Thread t, T firstValue) {
        t.threadLocals = new ThreadLocalMap(this, firstValue);
    }

    ThreadLocal 源码小结

    至此,可能大部分朋友已经明白了 ThreadLocal 是如何为每个线程创建变量的副本的:

    1. 在每个线程 Thread 内部有一个 ThreadLocal.ThreadLocalMap 类型的成员变量 threadLocals,这个 threadLocals 就是用来存储实际的变量副本的,键值为当前 ThreadLocal 变量,value 为变量副本(即 T 类型的变量)。
    2. 在 Thread 里面,threadLocals 为空,当通过 ThreadLocal 变量调用 get()方法或者 set()方法,就会对 Thread 类中的 threadLocals 进行初始化,并且以当前 ThreadLocal 变量为键值,以 ThreadLocal 要保存的副本变量为 value,存到 threadLocals。
    3. 在当前线程里面,如果要使用副本变量,就可以通过 get 方法在 threadLocals 里面查找。

    示例

    ThreadLocal 最常见的应用场景为用于解决数据库连接、Session 管理等问题。

    示例 - 数据库连接

    private static ThreadLocal<Connection> connectionHolder
    = new ThreadLocal<Connection>() {
    public Connection initialValue() {
        return DriverManager.getConnection(DB_URL);
    }
    };
    
    public static Connection getConnection() {
    return connectionHolder.get();
    }

    示例 - Session 管理

    private static final ThreadLocal threadSession = new ThreadLocal();
    
    public static Session getSession() throws InfrastructureException {
        Session s = (Session) threadSession.get();
        try {
            if (s == null) {
                s = getSessionFactory().openSession();
                threadSession.set(s);
            }
        } catch (HibernateException ex) {
            throw new InfrastructureException(ex);
        }
        return s;
    }

    管道输入/输出流

    管道输入/输出流和普通的文件输入/输出流或者网络输入/输出流不同之处在于,它主要用于线程之间的数据传输,而传输的媒介为内存。 管道输入/输出流主要包括了如下 4 种具体实现:PipedOutputStream、PipedInputStream、PipedReader 和 PipedWriter,前两种面向字节,而后两种面向字符。

    public class Piped {
    
        public static void main(String[] args) throws Exception {
            PipedWriter out = new PipedWriter();
            PipedReader in = new PipedReader();
            // 将输出流和输入流进行连接,否则在使用时会抛出IOException
            out.connect(in);
            Thread printThread = new Thread(new Print(in), "PrintThread");
            printThread.start();
            int receive = 0;
            try {
                while ((receive = System.in.read()) != -1) {
                    out.write(receive);
                }
            } finally {
                out.close();
            }
        }
    
        static class Print implements Runnable {
    
            private PipedReader in;
    
            Print(PipedReader in) {
                this.in = in;
            }
    
            public void run() {
                int receive = 0;
                try {
                    while ((receive = in.read()) != -1) {
                        System.out.print((char) receive);
                    }
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    FAQ

    start() 和 run() 有什么区别?可以直接调用 Thread 类的 run() 方法么?

    run() 方法是线程的执行体。

    start() 方法会启动线程,然后 JVM 会让这个线程去执行 run() 方法。

    可以直接调用 Thread 类的 run() 方法么?

    • 可以。但是如果直接调用 Thread 的 run()方法,它的行为就会和普通的方法一样。
    • 为了在新的线程中执行我们的代码,必须使用 Thread.start()方法。

    sleep()、yield()、join() 方法有什么区别?为什么 sleep()和 yield()方法是静态的?

    • yield()
      • yield() 方法可以让当前正在执行的线程暂停,但它不会阻塞该线程,它只是将该线程从 Running 状态转入 Runnable 状态。
      • 当某个线程调用了 yield() 方法暂停之后,只有优先级与当前线程相同,或者优先级比当前线程更高的处于就绪状态的线程才会获得执行的机会。
    • sleep()
      • sleep() 方法需要指定等待的时间,它可以让当前正在执行的线程在指定的时间内暂停执行,进入 Blocked 状态。
      • 该方法既可以让其他同优先级或者高优先级的线程得到执行的机会,也可以让低优先级的线程得到执行机会。
      • 但是, sleep() 方法不会释放“锁标志”,也就是说如果有 synchronized 同步块,其他线程仍然不能访问共享数据。
    • join()
      • join() 方法会使当前线程转入 Blocked 状态,等待调用 join() 方法的线程结束后才能继续执行。
    参考阅读:Java 线程中 yield 与 join 方法的区别 参考阅读:sleep(),wait(),yield()和 join()方法的区别

    为什么 sleep() 和 yield() 方法是静态的

    • Thread 类的 sleep() 和 yield() 方法将处理 Running 状态的线程。所以在其他处于非 Running 状态的线程上执行这两个方法是没有意义的。这就是为什么这些方法是静态的。它们可以在当前正在执行的线程中工作,并避免程序员错误的认为可以在其他非运行线程调用这些方法。

    Java 的线程优先级如何控制?高优先级的 Java 线程一定先执行吗?

    • Java 中的线程优先级如何控制
      • Java 中的线程优先级的范围是 [1,10],一般来说,高优先级的线程在运行时会具有优先权。可以通过 thread.setPriority(Thread.MAX_PRIORITY) 的方式设置,默认优先级为 5。
    • 高优先级的 Java 线程一定先执行吗
      • 即使设置了线程的优先级,也无法保证高优先级的线程一定先执行
      • 原因:这是因为线程优先级依赖于操作系统的支持,然而,不同的操作系统支持的线程优先级并不相同,不能很好的和 Java 中线程优先级一一对应。
      • 结论:Java 线程优先级控制并不可靠。

    什么是守护线程?为什么要用守护线程?如何创建守护线程?

    • 什么是守护线程
      • 守护线程(Daemon Thread)是在后台执行并且不会阻止 JVM 终止的线程。
      • 与守护线程(Daemon Thread)相反的,叫用户线程(User Thread),也就是非守护线程。
    • 为什么要用守护线程
      • 守护线程的优先级比较低,用于为系统中的其它对象和线程提供服务。典型的应用就是垃圾回收器。
    • 如何创建守护线程
      • 使用 thread.setDaemon(true) 可以设置 thread 线程为守护线程。
      • 注意点:
        • 正在运行的用户线程无法设置为守护线程,所以 thread.setDaemon(true) 必须在 thread.start() 之前设置,否则会抛出 llegalThreadStateException 异常;
        • 一个守护线程创建的子线程依然是守护线程。
        • 不要认为所有的应用都可以分配给 Daemon 来进行服务,比如读写操作或者计算逻辑。
    参考阅读:Java 中守护线程的总结

    为什么线程通信的方法 wait(), notify()和 notifyAll()被定义在 Object 类里?

    Java 的每个对象中都有一个锁(monitor,也可以成为监视器) 并且 wait(),notify()等方法用于等待对象的锁或者通知其他线程对象的监视器可用。在 Java 的线程中并没有可供任何对象使用的锁和同步器。这就是为什么这些方法是 Object 类的一部分,这样 Java 的每一个类都有用于线程间通信的基本方法

    为什么 wait(), notify()和 notifyAll()必须在同步方法或者同步块中被调用?

    当一个线程需要调用对象的 wait()方法的时候,这个线程必须拥有该对象的锁,接着它就会释放这个对象锁并进入等待状态直到其他线程调用这个对象上的 notify()方法。同样的,当一个线程需要调用对象的 notify()方法时,它会释放这个对象的锁,以便其他在等待的线程就可以得到这个对象锁。由于所有的这些方法都需要线程持有对象的锁,这样就只能通过同步来实现,所以他们只能在同步方法或者同步块中被调用。

    免费Java资料需要自己领取,涵盖了Java、Redis、MongoDB、MySQL、Zookeeper、Spring Cloud、Dubbo高并发分布式等教程,一共30G。

    传送门:https://mp.weixin.qq.com/s/JzddfH-7yNudmkjT0IRL8Q

    展开全文
  • 感温变色粉感温粉,也感温变色粉、温变粉,它是一种在低温时有色状态,当温度升至设定值时颜料从有色变为无色。下面,供应感温变色粉的金华利进科技带您一起了解下温变粉到底是由什么成分组成的,揭开变色的神秘...
  • php的语法中,echo和print都是输出一段... (2)echo的返回值是void,print的返回值是int (3)echo是语言结构不是函数,print在php5.3中开始作为函数使用 (4)echo语法比print的执行速度更快一些 以下是测试性能的具体...
  • 例如:下面代码定义了一个方法名 helloEducoder ,没有参数,且没有返回值的方法,执行的操作输出 welcome to educoder。 注意哦: 方法体放在一对大括号中,实现特定的操作; 方法名主要在调用这个方法时使用...
  • Python函数定义-位置参数-返回值

    千次阅读 2019-05-04 20:37:38
    1 函数介绍 函数在编程语言中就是完成特定功能的一个词句组(代码块),这组语句可以作为一个单位使用,并且给它取一个名字。可以通过函数名在程序的不同地方多次执行...1.1 为什么要使用函数 在编程中使用函数主...
  • 在移动互联网,分布式、微服务盛行的今天,现在项目绝大部分都采用的微服务框架,前后端分离方式,(题外话:前后端的工作职责越来越明确,现在的前端都称之大前端,技术栈以及生态圈都已经非常成熟;以前后端人员...
  • 在移动互联网,分布式、微服务盛行的今天,现在项目绝大部分都采用的微服务框架,前后端分离方式,(题外话:前后端的工作职责越来越明确,现在的前端都称之大前端,技术栈以及生态圈都已经非常成熟;以前后端人员...
  • Java什么叫方法

    2020-12-12 14:26:43
    1.首先方法是什么 1.解决某一件事的功能实现。方法,是一段代码块的封装,方法中的代码应围绕某一功能的实现来写,目标明确,逻辑清晰。...2.为什么有的方法有参数,有的没有? 1.榨汁机榨苹果汁需要苹果。吃饭需要食物
  • 什么叫方法.

    2019-10-07 21:58:50
    public 修饰符、 void没有返回值类型、 MethodDemo方法名称,方法名可以随便起、 ()里面参数列表、 {}里面方法体,方法体是运算或者实现某一种功能的代码 。个人理解 方法就是实现某一种功能,能够在其他...
  • 一、 什么是栈帧? 栈帧也过程活动记录,是编译器用来实现过程/函数调用的一种数据结构。 实际上,可以简单理解:栈帧就是存储在用户栈上的(当然内核栈同样适用)每一次函数调用涉及的相关信息的记录单元。 ...
  • STL(什么叫谓词)

    2020-04-01 21:06:03
    返回值类型bool的普通函数或仿函数 就谓词。 如果普通函数或仿函数 有一个参数 就一元谓词。 如果普通函数或仿函数 有二个参数 就二元谓词。 谓词是指普通函数或重载的operator()返回值是bool类型的函数对象...
  • 一般的函数返回值都是拿来用的,如果想要对返回值赋值,则返回引用类型。 所以返回引用类型就这个作用。 <br />举例:以下是何洁月视频中定义动态数组类array时,类里重载[]运算符的函数定义。只用返回引用...
  • 什么叫方法重载

    2020-07-12 17:14:35
    什么是方法重载 在工具中存在有相同类和方法名,但不同形参列表。我们称之方法重载。总结出来就是“两同一不同”即: 同一个类、相同方法名 参数列表不同:参数个数不同,参数类型不同 举例: Arrays类中重载的...
  • Oracle管理Oracle字符函数 Oracle字符函数 说明字符函数输入值字符类型返回值为字符类型或数字类型可以在sql语句中直接使用也可以在pl/sql块中使用 1ASCII(n)函数 描述:返回字符串的ascii码(当输入字符串时返回...
  • 什么叫构造方法?

    2015-12-08 21:05:00
    构造方法是一种特殊的方法,它是一个与类同名且没有返回值类型的方法。对象的创建就是通过构造方法来完成,其功能主要是完成对象的初始化。当类实例化一个对象时会自动调用构造方法。构造方法和其他方法一样也可以...
  • 一个方法,根据判断 可能返回一个int型数据, else 返回一个string型数据。... lz,因为可能返回两种不同类型的数据,那么用此函数变量赋值时,变量应该是什么类型呢? 建议lz换种语言,比如j
  • 为什么有构造方法?

    热门讨论 2018-12-25 16:07:45
    构造方法,这是做什么用的? 构造方法:又构造函数,其实就是对类进行初始化。构造方法与类同名,无返回值,也不需要void,在new时候调用。 也就是说,在类创建时,就是调用构造方法的时候了,在Cat cat1=ne...
  • 构造函数(也构造器),在对象创建的时候自动调用,一般用于完成对象的初始化工作 特点 函数名与类同名,无返回值(void都不能写),可以有参数,可以重载,可以有多个构造函数 一旦自定义了构造函数,必须用其中...
  • 今天为了一个低级错误查了一个小时,这个低级错误就是把DataInputStream的read和readInt当作一个...那为什么读byte却要返回int呢?我想这种“大材小用”的设计的最主要的考虑是-1,即正常的byte值返回为一个0-25
  • 面试官:讲区别(提示说比如说返回值呀) 一、malloc free是库函数,头文件stdlib.h中 new delete是c++中的关键字 1. malloc()函数 1.1 malloc的全称是memory allocation,中文动态内存分配。 原型:extern ...

空空如也

空空如也

1 2 3 4 5 ... 19
收藏数 370
精华内容 148
关键字:

为什么叫返回值