java多线程与并发之java并发编程实践(一)

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);
		}
	}
}
这种创建方式,把线程执行的逻辑代码直接写在了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);
		}
	}
}
从程序中可以看出线程目标对象和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){
		
		}
	}
}
执行结果的片断如下:
线程: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)
创建一个可重用的,线程数量固定的线程池(包括固定数量的Thread)以共享的无界队列方式来运行这些线程,在需要时使用提供的ThreadFactory创建新线程。如果在所有线程处于活动状态时提交附加任务,则在有可用线程之前,附加任务将在队列中等待。如果在关闭前的执行期间由于失败而导致任何线程终止,那么一个新线程将代替它执行后续的任务(如果需要).在某个线程被显式地关闭之前,线程将在池中一直存在。
2. public static ThreadFactory defaultThreadFactory()
返回用于创建新线程的默认线程工厂。新线程具有可通过pool-N-thread-M的Thread.getName()来访问的名称,其中N是此工厂的序列号,M是此工厂所创建线程的序列号。
3. public static ExecutorService newCachedThreadPool()
创建一个可根据需要创建新线程的线程池,但是在以前构造的线程可用时将重用它们。对于执行很多短期异步任务的程序而言,这些线程池通常可提高程序性能。调用execute将重用以前构造的线程(如果线程可用)。如果现有线程没有可用的,则创建一个新线程并添加到池中。另外空闲时,也会终止并从缓存中移除那些已60秒钟未被使用的线程。因此,长时间保持空闲的线程池不会使用任何资源。 注意,可以使用ThreadPoolExecutor构造方法创建具有类似属性但细节不同(例如超时参数)的线程池
4. public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize)
创建一个线程池,它可安排在给定延迟后运行命令或者定期地执行。( 可排程线程的线程池)
5. void execute(Runnable command)
在未来某个时间执行给定的命令。该命令可能在新的线程、已入池的线程或者正调用的线程中执行,这由Executor实现决定。

6.public static ExecutorService newSingleThreadExecutor()

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

7.public static ScheduledExecutorService newSingleThreadScheduledExecutor()

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


你可能感兴趣的:(java多线程与并发)