`
xmong
  • 浏览: 259265 次
  • 性别: Icon_minigender_1
  • 来自: 深圳
社区版块
存档分类
最新评论
阅读更多
Java多线程



目录
1 线程的概念 1
1.1 线程的概念 1
1.2 进程与线程 1
2 线程的创建 2
2.1 JAVA创建线程 2
2.1.1 实现Runnable接口 2
2.1.2 扩展Thread类 4
2.2 JAVA线程的实例化,启动,执行 6
2.2.1 Java线程的实例化 6
2.2.1 Java线程的启动 7
2.2.2 Java线程的执行 7
3 线程的状态 7
3.1 JAVA线程的状态 7
3.2 改变线程状态的方法 8
3.2.1 sleep() 9
3.2.2 wait(),notify(),notifyAll() 9
3.2.3 yield() 9
3.2.4 join() 10
3.2.5 interrupt() 10




1 线程的概念
1.1 线程的概念
线程:是"进程"中某个单一顺序的执行流。是程序执行流的最小单元,一个标准的线程由线程ID,当前指令指针(PC),寄存器集合和堆栈组成。另外,线程是进程中的一个实体,是被系统独立调度和分派的基本单位,线程自己不拥有系统资源,只拥有一点在运行中必不可少的资源,但它可与同属一个进程的其它线程共享进程所拥有的全部资源。一个线程可以创建和撤消另一个线程,同一进程中的多个线程之间可以并发执行。由于线程之间的相互制约,致使线程在运行中呈现出间断性。一个程序都至少有一个线程,若程序只有一个线程,那就是程序本身。
线程是程序中一个单一的顺序控制流程。在单个程序中同时运行多个线程完成不同的工作,称为多线程。
Java中,每个线程都有一个调用栈,即使不在程序中创建任何新的线程,线程也在后台运行着。一个Java应用总是从main()方法开始运行,mian()方法运行在一个线程内,它被称为主线程。一旦创建一个新的线程,就产生一个新的调用栈。

1.2 进程与线程
现代操作系统都支持多任务处理,多任务处理有两种截然不同的类型:基于进程和基于线程的。
基于进程的多任务处理:进程(Process)本质上是一个运行的程序。因此。基于进程的多任务处理就是系统可以同时运行多个应用程序。如系统上可以同时运行文本编辑器和word文档编辑器。
基于线程的多任务处理:线程(thread)本质是进程的一个执行流,是最小的执行单位。这就意味着一个程序可以同时执行多个任务功能。如一个文本编辑器可以在打印的同时格式化文本。
在多任务处理中多线程与多进程相比:进程是重量级任务,需要分配自己独立的地址空间和资源,进程之间的通信是昂贵的,受限的。线程是轻量级任务,线程之间共享相同的地址空间并其共同分享一个进程,线程间的通信时容易的,成本比较低。
多线程可以提高程序对CPU最大利用率:多线程可以高效使用CPU空闲时间,使得CPU空闲时间保持最低。如网络的数据传输速率远低于计算机的处理能力,本地文件系统资源的读写速度远低于CPU的处理能力,在这些数据传输和文件读写的过程中CPU空闲时间能够让其他线程利用起来。多线程能够使得CPU空闲时间得到充分的利用。

2 线程的创建
2.1 Java创建线程
Java提供了两种方法用来创建线程:
(1)实现Runnable接口
(2)扩展Tread类

2.1.1 实现Runnable接口
(1) 创建线程类
package com.thread;
/**
 * 实现Runnable接口创建线程类
 * @author xmong
 *
 */
public class TestThreadImpl implements Runnable{
	
	/**
	 * 重写Runnable接口的run方法
	 * 当线程启动后会执行该方法
	 */
	@Override
	public void run() {
		for (int i = 0; i < 5; i++) {
			try {
				Thread.sleep((long)(Math.random()*1000));
				System.out.println(Thread.currentThread().getName()+" : "+i);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
	}
}


(2) 测试线程
package com.thread;
/**
 * 测试多线程
 * @author xmong
 *
 */
public class TestThread {

	/**
	 * 创建两个线程实例
	 * 测试实现Runnable接口的线程
	 */
	public void testThreadImpl(){
		TestThreadImpl tl1 = new TestThreadImpl();
		TestThreadImpl tl2 = new TestThreadImpl();
		
		Thread t1 = new Thread(tl1,"tl1");
		Thread t2 = new Thread(tl2,"tl2");
		
		t1.start();
		t2.start();
	}
	
	/**
	 * main方法测试
	 * @param args
	 */
	 public static void main(String[] args) {
		TestThread tt = new TestThread();
		tt.testThreadImpl();
	}
}


(3) 测试结果
tl2 : 0
tl2 : 1
tl1 : 0
tl1 : 1
tl2 : 2
tl1 : 2
tl1 : 3
tl2 : 3
tl1 : 4
tl2 : 4

2.1.2 扩展Thread类
(1) 创建线程类
package com.thread;
/**
 * 通过扩展Thread实现线程
 * @author xmong
 *
 */
public class TestThreadExt extends Thread{
	/**
	 * 构造线程
	 * @param name
	 */
	public TestThreadExt(String name){
		super(name);
	}
	
	/**
	 * 重写Thread类的run方法
	 * 当线程启动后会执行该方法
	 */
	@Override
	public void run() {
		for (int i = 0; i < 5; i++) {
			try {
				Thread.sleep((long)(Math.random()*1000));
				System.out.println(this.getName()+" : "+i);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
	}	
}


(2) 测试线程
package com.thread;
/**
 * 测试多线程
 * @author xmong
 *
 */
public class TestThread {
	/**
	 * 创建两个线程实例
	 * 测试实现Runnable接口的线程
	 */
	public void testThreadImpl(){
		TestThreadImpl tl1 = new TestThreadImpl("tl1");
		TestThreadImpl tl2 = new TestThreadImpl("tl2");
		
		Thread t1 = new Thread(tl1);
		Thread t2 = new Thread(tl2);
		
		t1.start();
		t2.start();
	}
	
	/**
	 * 创建两个线程实例
	 * 测试扩展Thread类的线程
	 */
	public void testThreadExt(){
		TestThreadExt t1 = new TestThreadExt("te1");
		TestThreadExt t2 = new TestThreadExt("te2");
		
		t1.start();
		t2.start();
	}
	
	/**
	 * main方法测试
	 * @param args
	 */
	 public static void main(String[] args) {
		TestThread tt = new TestThread();
		//tt.testThreadImpl();
		tt.testThreadExt();
	}
}


(3) 测试结果
te2 : 0
te1 : 0
te1 : 1
te2 : 1
te1 : 2
te1 : 3
te2 : 2
te2 : 3
te1 : 4
te2 : 4

2.2 Java线程的实例化,启动,执行
2.2.1 Java线程的实例化
(1)如果是实现了java.lang.Runnable接口的类,则调用Thread的构造方法:
Thread(Runnable target)
Thread(Runnable target, String name)
Thread(ThreadGroup group, Runnable target) 
Thread(ThreadGroup group, Runnable target, String name) 
Thread(ThreadGroup group, Runnable target, String name, long stackSize) 

分配新的 Thread 对象,以便将 target 作为其运行对象,将指定的 name 作为其名称,作为 group 所引用的线程组的一员,并具有指定的堆栈尺寸stackSize。
(2)如果是扩展java.lang.Thread类的线程,则直接new即可。

2.2.1 Java线程的启动
在线程的Thread对象上调用start()方法,而不要直接调用run()或者别的方法。
在调用start()方法之后,发生了一系列动作:
1、启动新的执行线程(具有新的调用栈);
2、该线程从新状态转移到可运行状态(可见线程状态图);
3、当该线程获得机会执行时,其目标run()方法将运行。
注意:对Java来说,run()方法没有任何特别之处。像main()方法一样,它只是新线程知道调用的方法名称(和签名)。因此,在Runnable上或者Thread上调用run方法是合法的。但并不启动新的线程。

2.2.2 Java线程的执行
运行中的线程总是有名字的,名字有两个来源,一个是虚拟机自己给的名字,一个是你自己的定的名字。在没有指定线程名字的情况下,虚拟机总会为线程指定名字,并且主线程的名字总是mian,非主线程的名字不确定。线程都可以设置名字,也可以获取线程的名字,连主线程也不例外。
一系列线程以某种顺序启动并不意味着将按该顺序执行。对于任何一组启动的线程来说,调度程序不能保证其执行次序,持续时间也无法保证。当线程目标run()方法结束时该线程完成,线程转为死状态。一旦线程启动,它就永远不能再重新启动。只有一个新的线程可以被启动,并且只能一次。一个可运行的线程或死线程可以被重新启动。
线程的调度是JVM的一部分,在一个CPU的机器上上,实际上一次只能运行一个线程。一次只有一个线程栈执行。JVM线程调度程序决定实际运行哪个处于可运行状态的线程。尽管是采用队列形式,事实上,把它称为可运行池而不是可运行队列。尽管我们没有无法控制线程调度程序,但可以通过别的方式来影响线程调度的方式。


3 线程的状态
public static enum Thread.State
extends Enum<Thread.State>
线程状态。线程可以处于下列状态之一:
• NEW
至今尚未启动的线程处于这种状态。

• RUNNABLE
正在 Java 虚拟机中执行的线程处于这种状态。处于可运行状态的某一线程正在 Java 虚拟机中运行,但它可能正在等待操作系统中的其他资源,比如处理器。

• BLOCKED
受阻塞并等待某个监视器锁的线程处于这种状态。处于受阻塞状态的某一线程正在等待监视器锁,以便进入一个同步的块/方法,或者在调用 Object.wait 之后再次进入同步的块/方法。

• WAITING
无限期地等待另一个线程来执行某一特定操作的线程处于这种状态。某一线程因为调用下列方法之一而处于等待状态:
1、不带超时值的 Object.wait
2、不带超时值的 Thread.join
3、LockSupport.park
 处于等待状态的线程正等待另一个线程,以执行特定操作。 例如,已经在某一对象上调用了 Object.wait() 的线程正等待另一个线程,以便在该对象上调用 Object.notify() 或 Object.notifyAll()。已经调用了 Thread.join() 的线程正在等待指定线程终止。

• TIMED_WAITING
等待另一个线程来执行取决于指定等待时间的操作的线程处于这种状态。某一线程因为调用以下带有指定正等待时间的方法之一而处于定时等待状态:
1、Thread.sleep
2、带有超时值的 Object.wait
3、带有超时值的 Thread.join
4、LockSupport.parkNanos
5、LockSupport.parkUntil

• TERMINATED
已退出的线程处于这种状态。
在给定时间点上,一个线程只能处于一种状态。这些状态是虚拟机状态,它们并没有反映所有操作系统线程状态。
获取java线程状态,可以通过Thread的getState()方法
Thread.State getState()  返回该线程的状态。

3.1 Java中线程的状态
Java中线程的状态如下图:



• 新状态:线程对象已经创建,还没有在其上调用start()方法。此时它和其它的java对象一样,仅仅在堆中被分配了内存。
• 可运行状态:当一个线程创建了以后,其他的线程调用了它的start()方法,但调度程序还没有把它选定为运行线程时线程所处的状态。处于这个状态的线程位于可运行池中,等待获得CPU的使用权。在线程运行之后或者从阻塞、等待或睡眠状态回来后,也可以返回到可运行状态。
• 运行状态:线程调度程序从可运行池中选择一个线程作为当前线程时线程所处的状态。这也是线程进入运行状态的唯一一种方式。处于这个状态的线程占用CPU,执行程序run()方法的代码。
• 等待/阻塞/睡眠状态:这是线程有资格运行时它所处的状态。实际上这个三状态组合为一种,其共同点是:线程仍旧是活的,但是当前没有条件运行。换句话说,它是可运行的,但是如果某件事件出现,他可能返回到可运行状态。
阻塞状态分为三种情况:
(1) 等待状态的线程位于对象等待池中:当线程运行时,如果执行了某个对象的wait()方法,JVM就会把线程放到这个对象的等待池中。
(2) 阻塞状态的线程位于对象锁池中:当线程处于运行状态时,试图获取某个对象的同步锁,如果该对象的同步锁已经被其他的线程占用,JVM就会把这个线程放到对象的锁池中。
(3) 睡眠状态的线程位于对象锁池中:当前线程执行了sleep()方法,当前线程就会让出CUP的使用,目的是不让当前线程独自霸占该进程所获的CPU资源,以留一定时间给其他线程执行的机会。
线程阻塞的意思是:“雇主对雇工说:你守在这里,其它人做完到你的时候你就接着干活”。
线程等待的意思是:“雇主对雇工说:你去休息,用的着你的时候我会叫你,你在接着干活”。
线程睡眠的意思是:“雇主对雇工说:你去睡觉,某个时刻过来报到,然活接着干活”。
• 死亡态:当线程的run()方法完成时就认为它死去。这个线程对象也许是活的,但是,它已经不是一个单独执行的线程。线程一旦死亡,就不能复生。 如果在一个死去的线程上调用start()方法,会抛出java.lang.IllegalThreadStateException异常。

3.2 改变线程状态的方法
从操作系统的角度讲,OS会维护一个ready queue(就绪的线程队列)。并且在某一时刻cpu只为ready queue中位于队列头部的线程服务。
在java中提供Thread类的一些方法来控制线程,常用的有sleep(),wait(),yield(),jion()等方法。
当前正在被服务的线程需要睡一会,醒来后继续被服务,这就是sleep。
当前正在被服务的线程可能觉得cpu的服务不够好,于是推出了,到等待池中休息,这就是wait。
当前正在被服务的线程可能觉得cpu的服务不够好,于是提前退出,到ready queue队列中继续排队,这就是yield。
当前正在被服务的线程被cpu服务完后需要cpu接着服务另一个线程,与是把另一个线程归并到该线程的后面候着一起服务,当然服务这个线程也要经过ready queue队列,这就是join。
线程退出最好自己实现,在运行状态中一直检验一个状态,如果这个状态为真,就一直运行,如果外界更改了这个状态变量,那么线程就停止运行。
sleep()使当前线程进入停滞状态,所以执行sleep()的线程在指定的时间内肯定不会执行;yield()只是使当前线程重新回到可执行状态,所以执行yield()的线程有可能在进入到可执行状态后马上又被执行。
sleep()可使优先级低的线程得到执行的机会,当然也可以让同优先级和高优先级的线程有执行的机会;yield()只能使同优先级的线程有执行的机会。
wait()会使线程会释放掉它所占有的“锁标志”,从而使线程所在对象中的其它synchronized数据可被别的线程使用。
wait() 和notify()因为会对对象的“锁标志”进行操作,所以它们必须在 synchronized函数或synchronized block中进行调用。如果在non-synchronized函数或non- synchronized block中进行调用,虽然能编译通过,但在运行时会发生IllegalMonitorStateException的异常。

3.2.1 sleep()
Thread中的sleep方法:
static void sleep(long millis)
          在指定的毫秒数内让当前正在执行的线程休眠(暂停执行),此操作受到系统计时器和调度程序精度和准确性的影响。
static void sleep(long millis, int nanos)
          在指定的毫秒数加指定的纳秒数内让当前正在执行的线程休眠(暂停执行),此操作受到系统计时器和调度程序精度和准确性的影响。

在指定的毫秒数内让当前正在执行的线程休眠(暂停执行),此操作受到系统计时器和调度程序精度和准确性的影响。
线程睡眠是帮助所有线程获得运行机会的最好方法。线程睡眠到期自动苏醒,并返回到可运行状态,不是运行状态。sleep()中指定的时间是线程不会运行的最短时间。因此,sleep()方法不能保证该线程睡眠到期后就开始执行。sleep()是静态方法,只能控制当前正在运行的线程。
由于sleep()方法是Thread类的方法,因此它不能改变对象的机锁。所以当在一个Synchronized方法中调用sleep()时,线程虽然休眠了,但是对象的机锁 没有被释放,其他线程仍然无法访问这个对象。sleep()方法不需要在同步的代码块中执行。但是sleep()可以通过interrupt()方法打断 线程的暂停状态,从而使线程立刻抛出InterruptedException。

3.2.2 wait(),notify(),notifyAll()
Thread从Object对象中继续了wait(),notify(),notifyAll()方法。
void wait()
          在其他线程调用此对象的 notify() 方法或 notifyAll() 方法前,导致当前线程等待。
void wait(long timeout)
          在其他线程调用此对象的 notify() 方法或 notifyAll() 方法,或者超过指定的时间量前,导致当前线程等待。
void wait(long timeout, int nanos)
          在其他线程调用此对象的 notify() 方法或 notifyAll() 方法,或者其他某个线程中断当前线程,或者已超过某个实际时间量前,导致当前线程等待。
void notify()
          唤醒在此对象监视器上等待的单个线程。
void notifyAll()
          唤醒在此对象监视器上等待的所有线程。

wait()方法则会在线程休眠的同时释放掉机锁,其他线程可以访问该对象。wait()必须在同步的代码块中执行。 当一个线程执行到wait()方法时,它就进入到一个和该对象相关的等待池中,同时失去了对象的机锁,可以允许其它的线程执行一些同步操作。但是 wait()可以通过interrupt()方法打断线程的暂停状态,从而使线程立刻抛出InterruptedException。
wait 可以让同步方法或者同步块暂时放弃对象锁,而将它暂时让给其它需要对象锁的人(这里应该是程序块,或线程)用,这意味着可在执行wait()期间调用线程 对象中的其他同步方法!在其它情况下(sleep啊,suspend啊),这是不可能的.但是注意我前面说的,只是暂时放弃对象锁,暂时给其它线程使用, 我wait所在的线程还是要把这个对象锁收回来的呀.wait什么?就是wait别人用完了还给我啊!
好,那怎么把对象锁收回来呢?
第一种方法,限定借出去的时间.在wait()中设置参数,比如wait(1000),以毫秒为单位,就表明我只借出去1秒中,一秒钟之后,我自动收回.
第二种方法,让借出去的人通知我,他用完了,要还给我了.这时,我马上就收回来.哎,假如我设了1小时之后收回,别人只用了半小时就完了,那怎么办呢?靠!当然用完了就收回了,还管我设的是多长时间啊.
那么别人怎么通知我呢?相信大家都可以想到了,notify(),这就是最后一句话"而且只有在一个notify()或notifyAll()发生变化的时候,线程才会被唤醒"的意思了. notify()唤醒在此对象监视器上等待的单个线程。当它被一个notify()方法唤醒时,等待池中的线程就被放到了锁池中。该线程将等待从锁池中获得机锁,然后回到wait()前的中断现场。 notifyAll()唤醒在此对象监视器上等待的所有线程。

3.2.3 yield()
Thread中的yield方法
static void yield()
          暂停当前正在执行的线程对象,并执行其他线程。

Yield()方法是停止当前线程,让同等优先权的线程运行。如果没有同等优先权的线程,那么Yield()方法将不会起作用。
yield()从未导致线程转到等待/睡眠/阻塞状态。在大多数情况下,yield()将导致线程从运行状态转到可运行状态,但有可能没有效果,因为让步的线程还有可能被线程调度程序再次选中

3.2.4 join()
Thread中的join方法
void join()
          等待该线程终止。
void join(long millis)
          等待该线程终止的时间最长为 millis 毫秒。
void join(long millis, int nanos)
          等待该线程终止的时间最长为 millis 毫秒 + nanos 纳秒。

join()让一个线程B“加入”到另外一个线程A的尾部。在A执行完毕之前,B不能工作,实现线程串行化执行。
join()方法使当前线程停下来等待,直至另一个调用join方法的线程终止。值得注意的是,线程的在被激活后不一定马上就运行,而是进入到可运行线程的 队列中。但是join()可以通过interrupt()方法打断线程的暂停状态,从而使线程立刻抛出InterruptedException。
如t.join(5000);当前线程运行完后,则让t线程等待5000毫秒,如果超过这个时间,则停止等待,变为可运行状态

3.2.5 interrupt()
Thread中的interrupt(),interrupted()方法
void interrupt()
          中断线程。
static boolean interrupted()
          测试当前线程是否已经中断。

interrupt()中断线程。需要注意的是,InterruptedException是线程自己从内部抛出的,并不是interrupt()方法抛 出的。对某一线程调用interrupt()时,如果该线程正在执行普通的代码,那么该线程根本就不会抛出InterruptedException。但 是,一旦该线程进入到wait()/sleep()/join()后,就会立刻抛出InterruptedException。

3.3 Java线程的优先级
线程总是存在优先级,优先级范围在1~10之间。JVM线程调度程序是基于优先级的抢先调度机制。在大多数情况下,高优先级的线程比低优先级的线程有更高的几率得到执行。但这仅仅是大多数情况。
当设计多线程应用程序的时候,一定不要依赖于线程的优先级。因为线程调度优先级操作是没有保障的,只能把线程优先级作用作为一种提高程序效率的方法,但是要保证程序不依赖这种操作。

3.3.1 线程优先级的值
线程默认优先级是5,Thread类中有三个常量,定义线程优先级范围:
static int MAX_PRIORITY  线程可以具有的最高优先级。
static int MIN_PRIORITY  线程可以具有的最低优先级。
static int NORM_PRIORITY  分配给线程的默认优先级。

3.3.2 设置线程优先级
Thread中获取线程优先级getPriority(),和设置线程优先级setPriority()。
int getPriority()
          返回线程的优先级。
void setPriority(int newPriority)
          更改线程的优先级。

可以通过setPriority方法更改优先级。优先级不能超出1-10的取值范围,否则抛出 IllegalArgumentException。另外如果该线程已经属于一个线程组(ThreadGroup),该线程的优先级不能超过该线程组的优先级。


4 线程的同步

5 线程的通信




/*********************

未完待续




  • 大小: 11.3 KB
分享到:
评论

相关推荐

    Java多线程设计模式上传文件

    Java多线程设计模式上传文件Java多线程设计模式上传文件Java多线程设计模式上传文件Java多线程设计模式上传文件Java多线程设计模式上传文件Java多线程设计模式上传文件Java多线程设计模式上传文件Java多线程设计模式...

    java多线程读取文件

    Java多线程读大文件 java多线程写文件:多线程往队列中写入数据

    java多线程ppt

    java多线程PPT 多线程基本概念 创建线程的方式 线程的挂起与唤醒 多线程问题

    java 多线程操作数据库

    一个java 多线程操作数据库应用程序!!!

    java多线程经典案例

    java多线程经典案例,线程同步、线程通信、线程阻塞等经典案例

    Java多线程编程技术

    《Java多线程编程核心技术》建议猿友们读两遍,因为其写得没有那么抽象,第一遍有些概念不是很理解,可以先跳过并记录起来,第一遍阅读的目的主要是了解整个架构。第二遍再慢慢品味,并贯穿全部是指点来思考,并将...

    Java多线程编程实战指南(核心篇)

    Java多线程编程实战指南(核心篇) 高清pdf带目录 随着现代处理器的生产工艺从提升处理器主频频率转向多核化,即在一块芯片上集成多个处理器内核(Core),多核处理器(Multicore Processor)离我们越来越近了――如今...

    Java多线程知识点总结

    该文档总结了Java多线程相关的知识点,分享给大家,简单易懂!

    java多线程的讲解和实战

    详细的讲解了java多线程的原理,并配有代码进行实战,适合java初学者和想对多线程有进一步了解的人。

    java多线程通信图解

    一张图方便理解和掌握java 多线程之间通信的实质 java 多线程 其实就是每个线程都拥有自己的内存空间,多线程之间的通信,比例A线程修改了主内存(main方法的线程)变量,需要把A线程修改的结果同步到主线程中,...

    java多线程处理数据库数据

    java多线程处理数据库数据,使用并发包,无框架,可批量处数据库数据,进行增删改。。等等操作。

    java多线程,对多线程,线程池进行封装,方便使用

    java多线程,对多线程,线程池进行封装,方便使用

    Java多线程编程经验

    现在的操作系统是多任务操作系统。多线程是实现多任务的一种方式。 线程是指进程中的一个执行流程,一个进程中可以运行多个线程。...本文档提供Java多线程编程经验,方便广大Java爱好者研究学习Java多线程

    java多线程处理大数据

    java多线程处理大数据,可根据配置的线程数,任务去调度处理

    java多线程并发

    java多线程并发的在新窗口

    Java多线程机制(讲述java里面与多线程有关的函数)

    Java多线程机制 9.1 Java中的线程 9.2 Thread的子类创建线程 9.3 使用Runable接口 9.4 线程的常用方法 9.5 GUI线程 9.6 线程同步 9.7 在同步方法中使用wait()、notify 和notifyAll()方法 9.8 挂起、恢复和终止线程 ...

    java多线程核心技术

    资深Java专家10年经验总结,全程案例式讲解,首本全面介绍Java多线程编程技术的专著 结合大量实例,全面讲解Java多线程编程中的并发访问、线程间通信、锁等最难突破的核心技术与应用实践 Java多线程无处不在,如...

    java多线程实现大批量数据导入源码

    java多线程实现大批量数据切分成指定份数的数据,然后多线程处理入库或者导出,线程的个数和每份数据的数量都可以控制

    java多线程查询数据库

    java多线程并发查询数据库,使用线程池控制分页,并发查询。

    java多线程模拟队列实现排队叫号

    java多线程模拟队列实现排队叫号,多线程模拟排队叫号取号 java多线程模拟队列实现排队叫号,多线程模拟排队叫号取号

Global site tag (gtag.js) - Google Analytics