Java并发实战:JIRA并发调用的处理


java并发编程实践基础(一)
----------
1. 继承Thread创建线程

继承java.lang.Thread类创建线程是最简单的一种方法,也最直接。下面创建一个MyThread1类,继承Thread,重写其run()方法。并在main()方法中创建多个并发线程。

package simplethread;
public class MyThread1 extends Thread{
	public MyThread1(String name){
		super(name); //传递线程的名字
	}
	public static void main(String[] args){
		for(int i=0;i<5;i++){
			new MyThread1("thread"+i).start();
		}
	}
	@Override
	public void run(){
		for(int i=0;i<20;i++){	//输出线程名字和i
			System.out.println(this.getName()+":"+i);
		}
	}
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.


这种创建方式,把线程执行的逻辑代码直接写在了Thread的子类中,这样根据线程的概念模型,虚拟CPU和代码混合在一起了。并且java是单继承机制,线程体继承Thread类后,就不能继承其他类了,线程的扩展受影响。



2.

实现Runnable接口创建线程


为了构建结构清晰线程程序,

可以把代码独立出来形成线程目标对象,然后传给Thread对象。通常,实现Runnable接口的类创建的对象,称作线程的目标对象。下面创建一个类MyThread2实现Runnable接口,然后创建线程目标对象,传递给虚拟的CPU。

package simplethread;
public class MyThreadTarget implements Runnable{
	public static void main(String[] args){
		for(int i=0;i<5;i++){
			//创建线程目标对象
			Runnable r = new MyThreadTarget();
			//把目标对象传递进Thread,即虚拟的CPU
			new Thread(r,"thread"+i).start();
		}
	}
	@Override
	public void run(){
		for(int i=0;i<20;i++){
			System.out.println(Thread.currentThread().getName()+":"+i);
		}
	}
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.

从程序中可以看出线程目标对象和Thread分开了,并传递给了Thread。如果有比较复杂的数据要处理,可以在线程目标对象中引入数据。使用这种方式获得线程的名字就稍微复杂一些,需要使用到Thread中的静态方法,获得当前线程对象,然后再调用getName()方法。这种方式在较复杂的程序中用得比较普遍。



线程池


创建线程会使用相当一部分内存,其中包括有堆栈,以及每线程数据结构。如果创建过多线程,其中每个线程都将占用一些CPU时间,结果将使用许多内存来支持大量线程,每个线程都运行得很慢。这样就无法很好地使用计算资源。


java自从5.0以来,提供了线程池。线程的目标执行对象可以共享线程池中有限数目的线程对象。一般的服务器都需要线程池,比如web,FTP等服务器,不过它们一般都自己实现了线程池,比如Tomcat,Resion和Jetty等,现在JDK本身提供了,我们就没有必要重复造车轮了,直接使用就可以,何况使用也很方便,性能也非常高。


下面是使用线程池创建的多线程程序,100个线程目标对象共享2个纯程。


package pool;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class TestThreadPool{
	public static void main(String args[])throws InterruptedException{
		//在线程池中创建2个线程
		ExecutorService exec = Executors.newFixedThreadPool(2);
		//创建100个线程目标对象
		for(int index=0;index<100;index++){
			Runnable run = new Runner(index);
			//执行线程目标对象
			exec.execute(run);
		}
		//shutdown
		exec.shutdown();
	}
}
//线程目标对象
class Runner implements Runnable{
	int index = 0;
	public Runner(int index){
		this.index = index;
	}
	@Override
	public void run(){
		long time = (long)(Math.random()*1000);
		//输出线程的名字和使用目标对象及休眠的时间
		System.out.println("线程:"+Thread.currentThread().getName()+"(目标对象"
			+index+")"+":Sleeping"+time+"ms");
		try{
			Thread.sleep(time);
		}catch(InterruptedException e){
		
		}
	}
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.
  • 29.
  • 30.
  • 31.
  • 32.
  • 33.
  • 34.
  • 35.
  • 36.

执行结果的片断如下:


线程:pool-thread-1(目标对象23):Sleeping 938ms


线程:pool-thread-2(目标对象24):Sleeping 918ms


线程:pool-thread-2(目标对象25):Sleeping 238ms


线程:pool-thread-1(目标对象26):Sleeping 538ms


线程:pool-thread-1(目标对象27):Sleeping 933ms


线程:pool-thread-2(目标对象28):Sleeping 435ms


线程:pool-thread-1(目标对象29):Sleeping 198ms


线程:pool-thread-2(目标对象30):Sleeping 324ms


从执行结果可以看出,线程池中只生成了两个线程对象,100个线程目标对象共享他们。从程序中可以看出,使用JDK提供的线程池一般分为3步:


1.创建线程目标对象,可以是不同的,例如程序中的Runner;


2.使用Executors创建线程池,返回一个ExecutorService类型的对象;


3.使用线程池执行线程目标对象,exec.execute(run),阳后,结束线程池中的线程,exec.shutdown();



关于API:java.util.concurrent.Executors


该类主要定义了一些工厂方法和工具方法,其中最重要的就是创建各种线程池。


1.

public static ExecutorService newFixedThreadPool(int nThreads)
  • 1.

创建一个可重用的,线程数量固定的线程池(包括固定数量的Thread),

以共享的无界队列方式来运行这些线程,在需要时使用提供的ThreadFactory创建新线程。如果在所有线程处于活动状态时提交附加任务,则在有可用线程之前,附加任务将在队列中等待。如果在关闭前的执行期间由于失败而导致任何线程终止,那么一个新线程将代替它执行后续的任务(如果需要).在某个线程被显式地关闭之前,线程将在池中一直存在。


2.

public static ThreadFactory defaultThreadFactory()
  • 1.

返回用于创建新线程的默认线程工厂。新线程具有可通过pool-N-thread-M的Thread.getName()来访问的名称,其中N是此工厂的序列号,M是此工厂所创建线程的序列号。


3.

public static ExecutorService newCachedThreadPool()
  • 1.

创建一个可根据需要创建新线程的线程池,但是在以前构造的线程可用时将重用它们。对于执行很多短期异步任务的程序而言,这些线程池通常可提高程序性能。调用execute将重用以前构造的线程(如果线程可用)。如果现有线程没有可用的,则创建一个新线程并添加到池中。另外空闲时,也会终止并从缓存中移除那些已60秒钟未被使用的线程。因此,长时间保持空闲的线程池不会使用任何资源。

注意,可以使用ThreadPoolExecutor构造方法创建具有类似属性但细节不同(例如超时参数)的线程池。


4.

public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize)
  • 1.

创建一个线程池,它可安排在给定延迟后运行命令或者定期地执行。(

可排程线程的线程池)


5.

void execute(Runnable command)


在未来某个时间执行给定的命令。该命令可能在新的线程、已入池的线程或者正调用的线程中执行,这由Executor实现决定。


6.public static ExecutorService newSingleThreadExecutor()

创建一个使用单个 worker 线程的Executor,以无界队列方式来运行该线程。(只有一个Thread的线程池,循序的执行指定给它的每个任务)

7.public static ScheduledExecutorService newSingleThreadScheduledExecutor()

创建一个单线程执行程序,它可安排在给定延迟后运行命令或者定期地执行(单一可排程Thread的线程池)



QR Code
微信扫一扫,欢迎咨询~

联系我们
武汉格发信息技术有限公司
湖北省武汉市经开区科技园西路6号103孵化器
电话:155-2731-8020 座机:027-59821821
邮件:tanzw@gofarlic.com
Copyright © 2023 Gofarsoft Co.,Ltd. 保留所有权利
遇到许可问题?该如何解决!?
评估许可证实际采购量? 
不清楚软件许可证使用数据? 
收到软件厂商律师函!?  
想要少购买点许可证,节省费用? 
收到软件厂商侵权通告!?  
有正版license,但许可证不够用,需要新购? 
联系方式 155-2731-8020
预留信息,一起解决您的问题
* 姓名:
* 手机:

* 公司名称:

姓名不为空

手机不正确

公司不为空