当前位置: 首页 > news >正文

济宁高端网站建设可画简历模板官网

济宁高端网站建设,可画简历模板官网,网站建设公司怎么运营,推广方式营销方案目录 1、一个线程的生命周期 2、创建一个进程 2.1 Thread 方法 2.2 通过Runnable接口 2.3 通过继承Thread类本身 2.4 通过Callable和 Future创建进程 2.5 创建线程的三种方式的对比 3、线程的状态 4、线程同步 4.1 同步代码块 4.2 同步方法 5、使用wait和notify 6…目录 1、一个线程的生命周期 2、创建一个进程 2.1 Thread 方法 2.2 通过Runnable接口 2.3 通过继承Thread类本身 2.4 通过Callable和 Future创建进程 2.5 创建线程的三种方式的对比 3、线程的状态 4、线程同步 4.1 同步代码块 4.2 同步方法 5、使用wait和notify 6、线程死锁 7、ThreadLocal 7.1 使用场景 7.2 使用说明 Java 给多线程编程提供了内置的支持。 一条线程指的是进程中一个单一顺序的控制流一个进程中可以包含一个或多个线程每条线程并行执行不同的任务。 1、一个线程的生命周期 线程是一个动态执行的过程它也有一个从产生到死亡的过程。 下图显示了一个线程完整的生命周期。 新建状态:使用 new 关键字和 Thread 类或其子类建立一个线程对象后该线程对象就处于新建状态。它保持这个状态直到程序 start() 这个线程。就绪状态:当线程对象调用了start()方法之后该线程就进入就绪状态。就绪状态的线程处于就绪队列中要等待JVM里线程调度器的调度。运行状态:如果就绪状态的线程获取 CPU 资源就可以执行 run()此时线程便处于运行状态。处于运行状态的线程最为复杂它可以变为阻塞状态、就绪状态和死亡状态。阻塞状态:如果一个线程执行了sleep睡眠、suspend挂起等方法失去所占用资源之后该线程就从运行状态进入阻塞状态。在睡眠时间已到或获得设备资源后可以重新进入就绪状态。可以分为三种 等待阻塞运行状态中的线程执行 wait() 方法使线程进入到等待阻塞状态。同步阻塞线程在获取 synchronized 同步锁失败(因为同步锁被其他线程占用)。其他阻塞通过调用线程的 sleep() 或 join() 发出了 I/O 请求时线程就会进入到阻塞状态。当sleep() 状态超时join() 等待线程终止或超时或者 I/O 处理完毕线程重新转入就绪状态。 死亡状态:一个运行状态的线程完成任务或者其他终止条件发生时该线程就切换到终止状态。 2、创建一个进程 Java提供了三种创建线程的方法 2.1 Thread 方法 下表列出了Thread类的一些重要方法 序号 方法描述 1 public void start() 使该线程开始执行Java 虚拟机调用该线程的 run 方法。 2 public void run() 如果该线程是使用独立的 Runnable 运行对象构造的则调用该 Runnable 对象的 run 方法否则该方法不执行任何操作并返回。 3 public final void setName(String name) 改变线程名称使之与参数 name 相同。 4 public final void setPriority(int priority) 更改线程的优先级。 5 public final void setDaemon(boolean on) 将该线程标记为守护线程或用户线程。 6 public final void join(long millisec) 等待该线程终止的时间最长为 millis 毫秒。 7 public void interrupt() 中断线程。 8 public final boolean isAlive() 测试线程是否处于活动状态。 上述方法是被 Thread 对象调用的下面表格的方法是 Thread 类的静态方法。 序号 方法描述 1 public static void yield() 暂停当前正在执行的线程对象并执行其他线程。 2 public static void sleep(long millisec) 在指定的毫秒数内让当前正在执行的线程休眠暂停执行此操作受到系统计时器和调度程序精度和准确性的影响。 3 public static boolean holdsLock(Object x) 当且仅当当前线程在指定的对象上保持监视器锁时才返回 true。 4 public static Thread currentThread() 返回对当前正在执行的线程对象的引用。 5 public static void dumpStack() 将当前线程的堆栈跟踪打印至标准错误流。 2.2 通过Runnable接口 创建一个线程最简单的方法是创建一个实现 Runnable 接口的类。 为了实现 Runnable一个类只需要执行一个方法调用 run()声明如下 class RunnableDemo implements Runnable {private Thread t;private String threadName;RunnableDemo( String name) {threadName name;System.out.println(Creating threadName );}public void run() {System.out.println(Running threadName );try {for(int i 4; i 0; i--) {System.out.println(Thread: threadName , i);// 让线程睡眠一会Thread.sleep(50);}}catch (InterruptedException e) {System.out.println(Thread threadName interrupted.);}System.out.println(Thread threadName exiting.);}public void start () {System.out.println(Starting threadName );if (t null) {t new Thread (this, threadName);t.start ();}} }public class TestThread {public static void main(String args[]) {RunnableDemo R1 new RunnableDemo( Thread-1);R1.start();RunnableDemo R2 new RunnableDemo( Thread-2);R2.start();} } 程序运行结果 Creating Thread-1 Starting Thread-1 Creating Thread-2 Starting Thread-2 Running Thread-1 Thread: Thread-1, 4 Running Thread-2 Thread: Thread-2, 4 Thread: Thread-1, 3 Thread: Thread-2, 3 Thread: Thread-1, 2 Thread: Thread-2, 2 Thread: Thread-1, 1 Thread: Thread-2, 1 Thread Thread-1 exiting. Thread Thread-2 exiting.2.3 通过继承Thread类本身 创建一个线程的第二种方法是创建一个新的类该类继承 Thread 类然后创建一个该类的实例。 继承类必须重写 run() 方法该方法是新线程的入口点。它也必须调用 start() 方法才能执行。 该方法尽管被列为一种多线程实现方式但是本质上也是实现了 Runnable 接口的一个实例。 class ThreadDemo extends Thread {private Thread t;private String threadName;ThreadDemo( String name) {threadName name;System.out.println(Creating threadName );}public void run() {System.out.println(Running threadName );try {for(int i 4; i 0; i--) {System.out.println(Thread: threadName , i);// 让线程睡眠一会Thread.sleep(50);}}catch (InterruptedException e) {System.out.println(Thread threadName interrupted.);}System.out.println(Thread threadName exiting.);}public void start () {System.out.println(Starting threadName );if (t null) {t new Thread (this, threadName);t.start ();}} }public class TestThread {public static void main(String args[]) {ThreadDemo T1 new ThreadDemo( Thread-1);T1.start();ThreadDemo T2 new ThreadDemo( Thread-2);T2.start();} } 编译以上程序运行结果如下 Creating Thread-1 Starting Thread-1 Creating Thread-2 Starting Thread-2 Running Thread-1 Thread: Thread-1, 4 Running Thread-2 Thread: Thread-2, 4 Thread: Thread-1, 3 Thread: Thread-2, 3 Thread: Thread-1, 2 Thread: Thread-2, 2 Thread: Thread-1, 1 Thread: Thread-2, 1 Thread Thread-1 exiting. Thread Thread-2 exiting.2.4 通过Callable和 Future创建进程 1. 创建 Callable 接口的实现类并实现 call() 方法该 call() 方法将作为线程执行体并且有返回值。2. 创建 Callable 实现类的实例使用 FutureTask 类来包装 Callable 对象该 FutureTask 对象封装了该 Callable 对象的 call() 方法的返回值。3. 使用 FutureTask 对象作为 Thread 对象的 target 创建并启动新线程。4. 调用 FutureTask 对象的 get() 方法来获得子线程执行结束后的返回值。 public class CallableThreadTest implements CallableInteger {public static void main(String[] args) { CallableThreadTest ctt new CallableThreadTest(); FutureTaskInteger ft new FutureTask(ctt); for(int i 0;i 100;i) { System.out.println(Thread.currentThread().getName() 的循环变量i的值i); if(i20) { new Thread(ft,有返回值的线程).start(); } } try { System.out.println(子线程的返回值ft.get()); } catch (InterruptedException e) { e.printStackTrace(); } catch (ExecutionException e) { e.printStackTrace(); } }Override public Integer call() throws Exception { int i 0; for(;i100;i) { System.out.println(Thread.currentThread().getName() i); } return i; } } 2.5 创建线程的三种方式的对比 采用实现 Runnable、Callable 接口的方式创建多线程时线程类只是实现了 Runnable 接口或 Callable 接口还可以继承其他类。使用继承 Thread 类的方式创建多线程时编写简单如果需要访问当前线程则无需使用 Thread.currentThread() 方法直接使用 this 即可获得当前线程。 3、线程的状态 在Java程序中一个线程对象只能调用一次start()方法启动新线程并在新线程中执行run()方法。一旦run()方法执行完毕线程就结束了。因此Java线程的状态有以下几种 New新创建的线程尚未执行Runnable运行中的线程正在执行run()方法的Java代码Blocked运行中的线程因为某些操作被阻塞而挂起Waiting运行中的线程因为某些操作在等待中Timed Waiting运行中的线程因为执行sleep()方法正在计时等待Terminated线程已终止因为run()方法执行完毕。 用一个状态转移图表示如下 ┌─────────────┐│ New │└─────────────┘│▼ ┌ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┐┌─────────────┐ ┌─────────────┐ ││ Runnable │ │ Blocked ││└─────────────┘ └─────────────┘ │┌─────────────┐ ┌─────────────┐││ Waiting │ │Timed Waiting│ │└─────────────┘ └─────────────┘│─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─│▼┌─────────────┐│ Terminated │└─────────────┘ 当线程启动后它可以在Runnable、Blocked、Waiting和Timed Waiting这几个状态之间切换直到最后变成Terminated状态线程终止。 线程终止的原因有 线程正常终止run()方法执行到return语句返回线程意外终止run()方法因为未捕获的异常导致线程终止对某个线程的Thread实例调用stop()方法强制终止强烈不推荐使用。 一个线程还可以等待另一个线程直到其运行结束。例如main线程在启动t线程后可以通过t.join()等待t线程结束后再继续运行 public class Main {public static void main(String[] args) throws InterruptedException {Thread t new Thread(() - {System.out.println(hello);});System.out.println(start);t.start();t.join();System.out.println(end);} } 当main线程对线程对象t调用join()方法时主线程将等待变量t表示的线程运行结束即join就是指等待该线程结束然后才继续往下执行自身线程。所以上述代码打印顺序可以肯定是main线程先打印startt线程再打印hellomain线程最后再打印end。 如果t线程已经结束对实例t调用join()会立刻返回。此外join(long)的重载方法也可以指定一个等待时间超过等待时间后就不再继续等待。 4、线程同步 如果多个线程同时读写共享变量会出现数据不一致的问题。 一个例子 public class Main {public static void main(String[] args) throws Exception {var add new AddThread();var dec new DecThread();add.start();dec.start();add.join();dec.join();System.out.println(Counter.count);} }class Counter {public static int count 0; }class AddThread extends Thread {public void run() {for (int i0; i10000; i) { Counter.count 1; }} }class DecThread extends Thread {public void run() {for (int i0; i10000; i) { Counter.count - 1; }} }上面的代码很简单两个线程同时对一个int变量进行操作一个加10000次一个减10000次最后结果应该是0但是每次运行结果实际上都是不一样的。 这是因为对变量进行读取和写入时结果要正确必须保证是原子操作。原子操作是指不能被中断的一个或一系列操作。 例如对于语句 n n 1; 看上去是一行语句实际上对应了3条指令 ILOAD IADD ISTORE 我们假设n的值是100如果两个线程同时执行n n 1得到的结果很可能不是102而是101原因在于 ┌───────┐ ┌───────┐ │Thread1│ │Thread2│ └───┬───┘ └───┬───┘│ ││ILOAD (100) ││ │ILOAD (100)│ │IADD│ │ISTORE (101)│IADD ││ISTORE (101)│▼ ▼ 如果线程1在执行ILOAD后被操作系统中断此刻如果线程2被调度执行它执行ILOAD后获取的值仍然是100最终结果被两个线程的ISTORE写入后变成了101而不是期待的102。 这说明多线程模型下要保证逻辑正确对共享变量进行读写时必须保证一组指令以原子方式执行即某一个线程执行时其他线程必须等待 ┌───────┐ ┌───────┐ │Thread1│ │Thread2│ └───┬───┘ └───┬───┘│ ││-- lock -- ││ILOAD (100) ││IADD ││ISTORE (101) ││-- unlock -- ││ │-- lock --│ │ILOAD (101)│ │IADD│ │ISTORE (102)│ │-- unlock --▼ ▼ 4.1 同步代码块   保证一段代码的原子性就是通过加锁和解锁实现的。Java程序使用synchronized关键字对一个对象进行加锁 synchronized(lock) {n n 1; } synchronized保证了代码块在任意时刻最多只有一个线程能执行。我们把上面的代码用synchronized改写如下 public class Main {public static void main(String[] args) throws Exception {var add new AddThread();var dec new DecThread();add.start();dec.start();add.join();dec.join();System.out.println(Counter.count);} }class Counter {public static final Object lock new Object();public static int count 0; }class AddThread extends Thread {public void run() {for (int i0; i10000; i) {synchronized(Counter.lock) {Counter.count 1;}}} }class DecThread extends Thread {public void run() {for (int i0; i10000; i) {synchronized(Counter.lock) {Counter.count - 1;}}} }使用synchronized解决了多线程同步访问共享变量的正确性问题。但是它的缺点是带来了性能下降。因为synchronized代码块无法并发执行。此外加锁和解锁需要消耗一定的时间所以synchronized会降低程序的执行效率。 我们来概括一下如何使用synchronized 找出修改共享变量的线程代码块选择一个共享实例作为锁使用synchronized(lockObject) { ... }。 在使用synchronized的时候不必担心抛出异常。因为无论是否有异常都会在synchronized结束处正确释放锁 4.2 同步方法 我们知道Java程序依靠synchronized对线程进行同步使用synchronized的时候锁住的是哪个对象非常重要。 让线程自己选择锁对象往往会使得代码逻辑混乱也不利于封装。更好的方法是把synchronized逻辑封装起来。例如我们编写一个计数器如下 public class Counter {private int count 0;public void add(int n) {synchronized(this) {count n;}}public void dec(int n) {synchronized(this) {count - n;}}public int get() {return count;} } 这样一来线程调用add()、dec()方法时它不必关心同步逻辑因为synchronized代码块在add()、dec()方法内部。并且我们注意到synchronized锁住的对象是this即当前实例这又使得创建多个Counter实例的时候它们之间互不影响可以并发执行 var c1 Counter(); var c2 Counter();// 对c1进行操作的线程: new Thread(() - {c1.add(); }).start(); new Thread(() - {c1.dec(); }).start();// 对c2进行操作的线程: new Thread(() - {c2.add(); }).start(); new Thread(() - {c2.dec(); }).start(); 现在对于Counter类多线程可以正确调用。 当我们锁住的是this实例时实际上可以用synchronized修饰这个方法。下面两种写法是等价的 public void add(int n) { synchronized(this) { // 锁住thiscount n; } // 解锁 } public synchronized void add(int n) { // 锁住thiscount n; } // 解锁 因此用synchronized修饰的方法就是同步方法它表示整个方法都必须用this实例加锁。 5、使用wait和notify 在Java程序中synchronized解决了多线程竞争的问题。例如对于一个任务管理器多个线程同时往队列中添加任务可以用synchronized加锁 class TaskQueue {QueueString queue new LinkedList();public synchronized void addTask(String s) {this.queue.add(s);} } 但是synchronized并没有解决多线程协调的问题。 仍然以上面的TaskQueue为例我们再编写一个getTask()方法取出队列的第一个任务 class TaskQueue {QueueString queue new LinkedList();public synchronized void addTask(String s) {this.queue.add(s);}public synchronized String getTask() {while (queue.isEmpty()) {}return queue.remove();} } 上述代码看上去没有问题getTask()内部先判断队列是否为空如果为空就循环等待直到另一个线程往队列中放入了一个任务while()循环退出就可以返回队列的元素了。 但实际上while()循环永远不会退出。因为线程在执行while()循环时已经在getTask()入口获取了this锁其他线程根本无法调用addTask()因为addTask()执行条件也是获取this锁。 因此执行上述代码线程会在getTask()中因为死循环而100%占用CPU资源。 如果深入思考一下我们想要的执行效果是 线程1可以调用addTask()不断往队列中添加任务线程2可以调用getTask()从队列中获取任务。如果队列为空则getTask()应该等待直到队列中至少有一个任务时再返回。 因此多线程协调运行的原则就是当条件不满足时线程进入等待状态当条件满足时线程被唤醒继续执行任务。 对于上述TaskQueue我们先改造getTask()方法在条件不满足时线程进入等待状态 public synchronized String getTask() {while (queue.isEmpty()) {this.wait();}return queue.remove(); } 当一个线程执行到getTask()方法内部的while循环时它必定已经获取到了this锁此时线程执行while条件判断如果条件成立队列为空线程将执行this.wait()进入等待状态。 这里的关键是wait()方法必须在当前获取的锁对象上调用这里获取的是this锁因此调用this.wait()。 调用wait()方法后线程进入等待状态wait()方法不会返回直到将来某个时刻线程从等待状态被其他线程唤醒后wait()方法才会返回然后继续执行下一条语句。 有些仔细的童鞋会指出即使线程在getTask()内部等待其他线程如果拿不到this锁照样无法执行addTask()肿么办 这个问题的关键就在于wait()方法的执行机制非常复杂。首先它不是一个普通的Java方法而是定义在Object类的一个native方法也就是由JVM的C代码实现的。其次必须在synchronized块中才能调用wait()方法因为wait()方法调用时会释放线程获得的锁wait()方法返回后线程又会重新试图获得锁。 因此只能在锁对象上调用wait()方法。因为在getTask()中我们获得了this锁因此只能在this对象上调用wait()方法 public synchronized String getTask() {while (queue.isEmpty()) {// 释放this锁:this.wait();// 重新获取this锁}return queue.remove(); } 当一个线程在this.wait()等待时它就会释放this锁从而使得其他线程能够在addTask()方法获得this锁。 现在我们面临第二个问题如何让等待的线程被重新唤醒然后从wait()方法返回答案是在相同的锁对象上调用notify()方法。我们修改addTask()如下 public synchronized void addTask(String s) {this.queue.add(s);this.notify(); // 唤醒在this锁等待的线程 } 注意到在往队列中添加了任务后线程立刻对this锁对象调用notify()方法这个方法会唤醒一个正在this锁等待的线程就是在getTask()中位于this.wait()的线程从而使得等待线程从this.wait()方法返回。 我们来看一个完整的例子 public class Main {public static void main(String[] args) throws InterruptedException {var q new TaskQueue();var ts new ArrayListThread();for (int i0; i5; i) {var t new Thread() {public void run() {// 执行task:while (true) {try {String s q.getTask();System.out.println(execute task: s);} catch (InterruptedException e) {return;}}}};t.start();ts.add(t);}var add new Thread(() - {for (int i0; i10; i) {// 放入task:String s t- Math.random();System.out.println(add task: s);q.addTask(s);try { Thread.sleep(100); } catch(InterruptedException e) {}}});add.start();add.join();Thread.sleep(100);for (var t : ts) {t.interrupt();}} }class TaskQueue {QueueString queue new LinkedList();public synchronized void addTask(String s) {this.queue.add(s);this.notifyAll();}public synchronized String getTask() throws InterruptedException {while (queue.isEmpty()) {this.wait();}return queue.remove();} }这个例子中我们重点关注addTask()方法内部调用了this.notifyAll()而不是this.notify()使用notifyAll()将唤醒所有当前正在this锁等待的线程而notify()只会唤醒其中一个具体哪个依赖操作系统有一定的随机性。这是因为可能有多个线程正在getTask()方法内部的wait()中等待使用notifyAll()将一次性全部唤醒。通常来说notifyAll()更安全。有些时候如果我们的代码逻辑考虑不周用notify()会导致只唤醒了一个线程而其他线程可能永远等待下去醒不过来了。 但是注意到wait()方法返回时需要重新获得this锁。假设当前有3个线程被唤醒唤醒后首先要等待执行addTask()的线程结束此方法后才能释放this锁随后这3个线程中只能有一个获取到this锁剩下两个将继续等待。 再注意到我们在while()循环中调用wait()而不是if语句 public synchronized String getTask() throws InterruptedException {if (queue.isEmpty()) {this.wait();}return queue.remove(); } 这种写法实际上是错误的因为线程被唤醒时需要再次获取this锁。多个线程被唤醒后只有一个线程能获取this锁此刻该线程执行queue.remove()可以获取到队列的元素然而剩下的线程如果获取this锁后执行queue.remove()此刻队列可能已经没有任何元素了所以要始终在while循环中wait()并且每次被唤醒后拿到this锁就必须再次判断 while (queue.isEmpty()) {this.wait(); } 所以正确编写多线程代码是非常困难的需要仔细考虑的条件非常多任何一个地方考虑不周都会导致多线程运行时不正常。 6、线程死锁 Java的线程锁是可重入的锁。 什么是可重入的锁我们还是来看例子 public class Counter {private int count 0;public synchronized void add(int n) {if (n 0) {dec(-n);} else {count n;}}public synchronized void dec(int n) {count n;} } 观察synchronized修饰的add()方法一旦线程执行到add()方法内部说明它已经获取了当前实例的this锁。如果传入的n 0将在add()方法内部调用dec()方法。由于dec()方法也需要获取this锁现在问题来了 对同一个线程能否在获取到锁以后继续获取同一个锁 答案是肯定的。JVM允许同一个线程重复获取同一个锁这种能被同一个线程反复获取的锁就叫做可重入锁。 由于Java的线程锁是可重入锁所以获取锁的时候不但要判断是否是第一次获取还要记录这是第几次获取。每获取一次锁记录1每退出synchronized块记录-1减到0的时候才会真正释放锁。 死锁 一个线程可以获取一个锁后再继续获取另一个锁。例如 public void add(int m) { synchronized(lockA) { // 获得lockA的锁this.value m;synchronized(lockB) { // 获得lockB的锁this.another m;} // 释放lockB的锁 } // 释放lockA的锁 }public void dec(int m) {synchronized(lockB) { // 获得lockB的锁this.another - m;synchronized(lockA) { // 获得lockA的锁this.value - m;} // 释放lockA的锁} // 释放lockB的锁 } 在获取多个锁的时候不同线程获取多个不同对象的锁可能导致死锁。对于上述代码线程1和线程2如果分别执行add()和dec()方法时 线程1进入add()获得lockA线程2进入dec()获得lockB。 随后 线程1准备获得lockB失败等待中线程2准备获得lockA失败等待中。 此时两个线程各自持有不同的锁然后各自试图获取对方手里的锁造成了双方无限等待下去这就是死锁。 死锁发生后没有任何机制能解除死锁只能强制结束JVM进程。 因此在编写多线程应用时要特别注意防止死锁。因为死锁一旦形成就只能强制结束进程。 那么我们应该如何避免死锁呢答案是线程获取锁的顺序要一致。即严格按照先获取lockA再获取lockB的顺序改写dec()方法如下 public void dec(int m) { synchronized(lockA) { // 获得lockA的锁this.value - m;synchronized(lockB) { // 获得lockB的锁this.another - m;} // 释放lockB的锁 } // 释放lockA的锁 } 7、ThreadLocal ThreadLocal 中填充的的是当前线程的变量该变量对其他线程而言是封闭且隔离的ThreadLocal 为变量在每个线程中创建了一个副本这样每个线程都可以访问自己内部的副本变量。 7.1 使用场景 在进行对象跨层传递的时候使用ThreadLocal可以避免多次传递打破层次间的约束。线程间数据隔离进行事务操作用于存储线程事务信息。数据库连接Session会话管理。 7.2 使用说明 我们可以在代码中调用Thread.currentThread()获取当前线程。例如打印日志时可以同时打印出当前线程的名字   public class Main {public static void main(String[] args) throws Exception {log(start main...);new Thread(() - {log(run task...);}).start();new Thread(() - {log(print...);}).start();log(end main.);}static void log(String s) {System.out.println(Thread.currentThread().getName() : s);} }对于多任务Java标准库提供的线程池可以方便地执行这些任务同时复用线程。Web应用程序就是典型的多任务应用每个用户请求页面时我们都会创建一个任务类似 public void process(User user) {checkPermission();doWork();saveStatus();sendResponse(); } 然后通过线程池去执行这些任务。 观察process()方法它内部需要调用若干其他方法同时我们遇到一个问题如何在一个线程内传递状态 这种在一个线程中横跨若干方法调用需要传递的对象我们通常称之为上下文Context它是一种状态可以是用户身份、任务信息等。 给每个方法增加一个context参数非常麻烦而且有些时候如果调用链有无法修改源码的第三方库User对象就传不进去了。 Java标准库提供了一个特殊的ThreadLocal它可以在一个线程中传递同一个对象。 ThreadLocal实例通常总是以静态字段初始化如下 static ThreadLocalUser threadLocalUser new ThreadLocal(); 它的典型使用方式如下 void processUser(user) {try {threadLocalUser.set(user);step1();step2();} finally {threadLocalUser.remove();} } 通过设置一个User实例关联到ThreadLocal中在移除之前所有方法都可以随时获取到该User实例 void step1() {User u threadLocalUser.get();log();printUser(); }void log() {User u threadLocalUser.get();println(u.name); }void step2() {User u threadLocalUser.get();checkUser(u.id); } 注意到普通的方法调用一定是同一个线程执行的所以step1()、step2()以及log()方法内threadLocalUser.get()获取的User对象是同一个实例。 实际上可以把ThreadLocal看成一个全局MapThread, Object每个线程获取ThreadLocal变量时总是使用Thread自身作为key Object threadLocalValue threadLocalMap.get(Thread.currentThread()); 因此ThreadLocal相当于给每个线程都开辟了一个独立的存储空间各个线程的ThreadLocal关联的实例互不干扰。 最后特别注意ThreadLocal一定要在finally中清除 try {threadLocalUser.set(user);... } finally {threadLocalUser.remove(); } 这是因为当前线程执行完相关代码后很可能会被重新放入线程池中如果ThreadLocal没有被清除该线程执行其他代码时会把上一次的状态带进去。 为了保证能释放ThreadLocal关联的实例我们可以通过AutoCloseable接口配合try (resource) {...}结构让编译器自动为我们关闭。例如一个保存了当前用户名的ThreadLocal可以封装为一个UserContext对象 public class UserContext implements AutoCloseable {static final ThreadLocalString ctx new ThreadLocal();public UserContext(String user) {ctx.set(user);}public static String currentUser() {return ctx.get();}Overridepublic void close() {ctx.remove();} } 使用的时候我们借助try (resource) {...}结构可以这么写 try (var ctx new UserContext(Bob)) {// 可任意调用UserContext.currentUser():String currentUser UserContext.currentUser(); } // 在此自动调用UserContext.close()方法释放ThreadLocal关联对象 这样就在UserContext中完全封装了ThreadLocal外部代码在try (resource) {...}内部可以随时调用UserContext.currentUser()获取当前线程绑定的用户名。
文章转载自:
http://www.morning.brkc.cn.gov.cn.brkc.cn
http://www.morning.drnjn.cn.gov.cn.drnjn.cn
http://www.morning.httpm.cn.gov.cn.httpm.cn
http://www.morning.bpmtg.cn.gov.cn.bpmtg.cn
http://www.morning.tgczj.cn.gov.cn.tgczj.cn
http://www.morning.gzzncl.cn.gov.cn.gzzncl.cn
http://www.morning.ppqzb.cn.gov.cn.ppqzb.cn
http://www.morning.mspqw.cn.gov.cn.mspqw.cn
http://www.morning.cnyqj.cn.gov.cn.cnyqj.cn
http://www.morning.jpfpc.cn.gov.cn.jpfpc.cn
http://www.morning.pinngee.com.gov.cn.pinngee.com
http://www.morning.rxfbf.cn.gov.cn.rxfbf.cn
http://www.morning.tqwcm.cn.gov.cn.tqwcm.cn
http://www.morning.zdxinxi.com.gov.cn.zdxinxi.com
http://www.morning.rdxp.cn.gov.cn.rdxp.cn
http://www.morning.tkcct.cn.gov.cn.tkcct.cn
http://www.morning.nbybb.cn.gov.cn.nbybb.cn
http://www.morning.ydfr.cn.gov.cn.ydfr.cn
http://www.morning.rlhgx.cn.gov.cn.rlhgx.cn
http://www.morning.jwqqd.cn.gov.cn.jwqqd.cn
http://www.morning.xiaobaixinyong.cn.gov.cn.xiaobaixinyong.cn
http://www.morning.24vy.com.gov.cn.24vy.com
http://www.morning.dxhnm.cn.gov.cn.dxhnm.cn
http://www.morning.txkrc.cn.gov.cn.txkrc.cn
http://www.morning.twgzq.cn.gov.cn.twgzq.cn
http://www.morning.ggfdq.cn.gov.cn.ggfdq.cn
http://www.morning.hlfgm.cn.gov.cn.hlfgm.cn
http://www.morning.tclqf.cn.gov.cn.tclqf.cn
http://www.morning.hmdn.cn.gov.cn.hmdn.cn
http://www.morning.gjqgz.cn.gov.cn.gjqgz.cn
http://www.morning.ckzjl.cn.gov.cn.ckzjl.cn
http://www.morning.ryzgp.cn.gov.cn.ryzgp.cn
http://www.morning.yggdq.cn.gov.cn.yggdq.cn
http://www.morning.zkgpg.cn.gov.cn.zkgpg.cn
http://www.morning.rmtmk.cn.gov.cn.rmtmk.cn
http://www.morning.pbmg.cn.gov.cn.pbmg.cn
http://www.morning.dpflt.cn.gov.cn.dpflt.cn
http://www.morning.rkdzm.cn.gov.cn.rkdzm.cn
http://www.morning.rszbj.cn.gov.cn.rszbj.cn
http://www.morning.mdgb.cn.gov.cn.mdgb.cn
http://www.morning.phechi.com.gov.cn.phechi.com
http://www.morning.brbmf.cn.gov.cn.brbmf.cn
http://www.morning.jcxgr.cn.gov.cn.jcxgr.cn
http://www.morning.krkwh.cn.gov.cn.krkwh.cn
http://www.morning.tnthd.cn.gov.cn.tnthd.cn
http://www.morning.wwwghs.com.gov.cn.wwwghs.com
http://www.morning.tkcct.cn.gov.cn.tkcct.cn
http://www.morning.swzpx.cn.gov.cn.swzpx.cn
http://www.morning.pzjfz.cn.gov.cn.pzjfz.cn
http://www.morning.nzlsm.cn.gov.cn.nzlsm.cn
http://www.morning.nbnpb.cn.gov.cn.nbnpb.cn
http://www.morning.hypng.cn.gov.cn.hypng.cn
http://www.morning.rkkh.cn.gov.cn.rkkh.cn
http://www.morning.bhmnp.cn.gov.cn.bhmnp.cn
http://www.morning.kdpal.cn.gov.cn.kdpal.cn
http://www.morning.rwlnk.cn.gov.cn.rwlnk.cn
http://www.morning.tdmr.cn.gov.cn.tdmr.cn
http://www.morning.bsqkt.cn.gov.cn.bsqkt.cn
http://www.morning.cttgj.cn.gov.cn.cttgj.cn
http://www.morning.ndngj.cn.gov.cn.ndngj.cn
http://www.morning.hbywj.cn.gov.cn.hbywj.cn
http://www.morning.qzqfq.cn.gov.cn.qzqfq.cn
http://www.morning.dwmtk.cn.gov.cn.dwmtk.cn
http://www.morning.hhpbj.cn.gov.cn.hhpbj.cn
http://www.morning.xtqr.cn.gov.cn.xtqr.cn
http://www.morning.fyxtn.cn.gov.cn.fyxtn.cn
http://www.morning.srgwr.cn.gov.cn.srgwr.cn
http://www.morning.kpxnz.cn.gov.cn.kpxnz.cn
http://www.morning.skrh.cn.gov.cn.skrh.cn
http://www.morning.xwgbr.cn.gov.cn.xwgbr.cn
http://www.morning.rythy.cn.gov.cn.rythy.cn
http://www.morning.mhnb.cn.gov.cn.mhnb.cn
http://www.morning.lxqkt.cn.gov.cn.lxqkt.cn
http://www.morning.kntbk.cn.gov.cn.kntbk.cn
http://www.morning.rsbqq.cn.gov.cn.rsbqq.cn
http://www.morning.zplzj.cn.gov.cn.zplzj.cn
http://www.morning.bxqpl.cn.gov.cn.bxqpl.cn
http://www.morning.khxyx.cn.gov.cn.khxyx.cn
http://www.morning.fnzbx.cn.gov.cn.fnzbx.cn
http://www.morning.ysdwq.cn.gov.cn.ysdwq.cn
http://www.tj-hxxt.cn/news/249444.html

相关文章:

  • 网站建设的关键点wordpress获取分类下文章列表
  • 临沂网站建设团队动漫设计软件
  • 灰色调网站软文写作要求
  • 合肥网站建设sina机械加工网站模板
  • 装修设计装饰公司优化公司治理
  • 抚州企业网站做优化制作个人网站实例
  • 正规的网站制作搜索关键词排名
  • 琼海市规划建设局网站哈尔滨网页设计推广
  • 个人如何做免费网站少儿编程线下培训机构排名前十
  • 无锡网站制作哪里有有没有做网站的
  • 我做的网站不知道网站怎么办网站建设的技能有哪些方面
  • 网站文章更新注意什么wordpress调用文章的tag
  • 现在.net做网站的多吗黔西网站建设
  • 做网站推广广告韩国小清新网站模板
  • 网站建设定价户县网站建设
  • 网站开发东莞如何创建一个网站
  • 杭州滨江区抖音seo行情windows优化大师有哪些功能
  • 合肥工程建设交易中心网站想用vs做网站 学什么
  • 简单的网站设计开发网站建设基本流程包括哪几个步骤
  • 新网站建设的感想四川网站建设找哪家
  • wordpress 网站运行时间咸阳做网站开发公司哪家好
  • 涉县企业做网站推广四川住房和建设厅网站
  • 网站的备案号百度知道个人中心
  • 如何用文档做网站用易语言做抢购网站软件
  • 帝国做的网站石岩网站建设 0755
  • 选择电商网站建设wordpress模板站如何安装
  • 做网站的开场白常州企业自助建站系统
  • 整站排名服务推广策略研究
  • 南昌知名的网站建设公司100大看免费行情的软件
  • 太仓网站制作书生wordpress在哪修改代码