线程的编程4种实现方法

本文详细介绍了Java中通过继承Thread、实现Runnable接口、Callable/Future及线程池创建线程的四种方法,包括Thread类的使用、Runnable接口的lambda表达式和匿名内部类、Callable/Future接口的应用以及线程池优化性能的方式。
摘要由CSDN通过智能技术生成

线程的编程4种实现方法

1、继承Thread

Thread类本质上是实现了Runnable接口的一个实例,代表一个线程的实例。

public class Thread implements Runnable 
@FunctionalInterface //函数式接口,其中包含一个抽象方法run 
public interface Runnable { 
	public abstract void run(); 
}

启动线程的唯一方法就是通过Thread类的start()实例方法,不能直接调用run()方法。start()方法是一个native方 法,它将启动一个新线程,并执行run()方法。这种方式实现多线程很简单,通过自己的类直接extend Thread,并 复写run()方法,就可以启动新线程并执行自己定义的run()方法

class LeftThread extends Thread{
	public void run() { 
		for(int i=0;i<50;i++) { 
			System.out.println("左手一个慢动作..."); 
			try {//使当前正在执行的线程休眠等待500ms 
				Thread.sleep(500); 
			} catch (InterruptedException e) { 
				e.printStackTrace(); 
			} 
		} 
	} 
}
//在主线程中启动这个线程 
Thread t1=new LeftThread(); 
t1.start();//不能直接调用run方法,否则就是在主线程中单线程执行,不是多线程

源代码阅读:
public class Thead implements Runnable{}线程类定义
重要属性

/* what will be run. */ 
private Runnable target;

接口方法的实现,覆盖定义时不能修改方法签名

public void run(){ 
	if(target!=null) 
		target.run(); 
}

粗略的阅读实现

public synchronized void start() { 
	if (threadStatus != 0) //当前线程状态检查 
		throw new IllegalThreadStateException(); 
	group.add(this); //将当前线程对象添加到一个线程组中进行管理 
	boolean started = false; //标志值用于判断当前线程是否已经启动 
	try {
		start0(); 
		started = true; 
	} finally { 
		try {
			if (!started) { 
				group.threadStartFailed(this); 
			} 
		} catch (Throwable ignore) { 
		} 
	}	 
}
private native void start0();//通过对等类实现启动线程的细节,内部就是自动调用run()方法

定义线程类

class LeftThread extends Thread{ 
	//定义左手线程类,只需要覆盖(重写)定义其中的run()方法,这里实际就是线程的执行线索,执行的程序 
	public void run(){ 
		for(int i=0;i<50;i++) 
			System.out.println("左手一个慢动作....") 
	} 
}

调用方法

Thread t1=new LeftThread(); 
t1.start();//启动线程必须使用start方法,而不能是调用run方法 
//t1.run();不允许直接调用run方法,因为这种调用方式不是多线程,而是单线程
public class T4 { 
	public static void main(String[] args) { 
		new Thread() { 
			public void run() { 
				System.out.println("匿名类内部输出"+Thread.currentThread().getName()); 
			} 
		}.run();//从语法上来说,可以直接调用run方法,但是运行run方法则不是多线程 
		//.start();输出结果证明不是main线程 
		System.out.println("main方法输出"+Thread.currentThread().getName()); 
	}
}

如果判断线程运行情况:

最简单的方式是查看线程个数 
// Thread.currentThread()用于获取当前正在运行这个代码的线程对象 
System.out.println(Thread.currentThread()); 
Thread t1 = new LeftThread(); 
// t1.run();在当前主线程中执行t1对象中的run方法 
// t1.start();可以在LeftThread中获取到另外一个线程对象 

public class LeftThread extends Thread { 
	@Override 
	public void run() {// 这里包含的就是执行线索 
		System.out.println(Thread.currentThread()); 
	} 
}

最大的限制实际上就是Java的单根继承体系,这种方式基本不用

2、实现Runnable接口

接口定义

@FunctionalInterface //函数式接口,简化定义方式为lambda表达式 
public interface Runnable{ 
	public abstract void run(); 
}

可以使用lambda表示式进行定义,或者使用匿名内部类进行定义

class RightRunnable implements Runnable{ 
	public void run(){ 
		for(int i=0;i<50;i++) 
			System.out.println(Thread.currentThread().getName()+"说:右手慢动作重播!"); 
	} 
}

启动方法:

//启动通过实现Runnable接口定义的线程对象,不能直接使用Runnable r=new RightRunnable(); 
Thread t2=new Thread(new RightRunnable()); 
t2.start();

使用lambda表达式的写法

//匿名内部类的写法 
Thread t1 = new Thread(new Runnable() { 
	public void run() { 
		for(int i = 0; i < 20; i++) { 
			System.out.println(Thread.currentThread() + "--" + i); 
		} 
	} 
}); t1.start(); 

//lambda表达式的写法---要求接口必须是函数式接口 
Thread t1 = new Thread(()-> { 
	for (int i = 0; i < 20; i++) { 
		System.out.println(Thread.currentThread() + "--" + i); 
	} 
}); t1.start();
static int sum=0; 
public static void main(String[] args){ 
	Thread t1=new Thread(){ 
		public void run(){ 
			for(int i=0;i<=100;i++) 
				sum+=i; 
		} 
	};
	Thread t2=new Thread(new Runnable(){ 
		public void run(){ 
			for(int i=1;i<=100;i++) 
				sum+=i; 
		} 
	}); 
	t2.start(); 
	t1.start(); 
	try{
		Thread.currentThread().sleep(1000); 
	}catch(Exception e){ 
		e.printStackTrace(); 
	} 
}

因为进程中的参数sum是所有进程中的线程所共享的(main\t1\t2),所以可以通过线程共享的内容进行数据的传递,但 是由于并发操作有可能会出现计算结果不正确的情况

3、使用Callable和Future接口创建线程

@FuntionalInterface //属于函数式接口,所以可以直接使用lambda表达式进行定义 
public interface Callable<V>{ //<>写法叫做泛型,用于定义返回的数据类型 
	V call()throws Exception; 
}

Future接口用于Callable接口中call方法的执行结果,并可以测试Callable的执行情况

public interface Future<V>{//用于获取Callable接口中的call方法的执行结果 
	boolean cancel(boolean mayIntersupteIfRunning); 
	boolean isCancelled(); 
	boolean isDone(); 
	V get() throws InterruptedException, ExecutionException; 
	V get(long timeout, TimeUnit unit)throws InterruptedException, ExecutionException; 
}

系统提供了一个Future接口的实现类FutureTask

public class FutureTask<V> implements RunnableFuture<V> //这个实现类实现了两个接口Runnable和 Future

有了FutureTask类则可以将Callable实现封装到FutureTask对象中,同时由于FutureTask实现了Runnable接口, 所以可以作为Thread类的构造器参数传入Thread对象中,供线程执行对应call方法中的逻辑

Callable<Integer> myCallable = new MyCallable(); // 创建MyCallable对象 
FutureTask<Integer> ft = new FutureTask<Integer>(myCallable); //使用FutureTask来包装 MyCallable对象 
Thread thread = new Thread(ft);//FutureTask对象作为Thread对象的target创建新的线程 
thread.start(); //线程进入到就绪状态 
int sum = ft.get(); //取得新创建的新线程中的call()方法返回的结果

使用方法:
1、 定义Callable接口的实现类

public class MyCallable implements Callable<Integer>{ 
	int begin=0; 
	int end=0; 
	public MyCallable(int begin, int end){ 
		this.begin=begin; 
		this.end=end;
	}
	public Integer call()throws Exception{ 
		int res=0; 
		for(int k=begin; k<=end; k++) 
			res+=k; 
		System.out.println(Thread.currentThread()+"---"+res); 
		return res; 
	} 
}

2、调用

FutureTask[] arr=new FutureTask[10]; 
for(int i=0;i<10;i++){ 
	arr[i]=new FutureTask<>(new MyCallable(i*10+1,(i+1)*10)); 
	Thread t=new Thread(arr[i]); 
	t.start(); 
}
int res=0; 
for(int i=0;i<arr.length;i++) 
	res+=((Integer)arr[i].get()); 
System.out.println("计算结果为:"+res);

注意:FutureTask实现了Future和Runnable接口,所以new Thread(futureTask),当执行thread.start()方法时会自 动调用Callable接口实现中的call方法。当调用futureTask.get()方法时可以获取对应的线程对象的执行结果,如果 线程并没有返回时,当前线程阻塞等待

4、使用线程池创建线程

享元模式

享元模式Flyweight Pattern主要用于减少创建对象的数量,以减少内存占用和提高性能。这种类型的设计模式属于 结构模式,它提供了减少对象数量从而改善应用所需的对象结构的方式

优点:大大减少对象的创建,降低系统内存的使用,以提高程序的执行效率。

缺点:提高了系统的复杂度,需要分离出外部状态和内部状态,而且外部状态具有固有化的性质,不应该随着内部 状态的变化而变化,否则会造成系统的混乱。

不考虑线程问题

public interface IShape { 
	void draw(); 
}

public class Circle implements IShape { 
	public void draw() { 
		System.out.println("我画一个圆"); 
	} 
}

定义工厂类提供获取实例的方法

public class ShapeFactory { 
	private static IShape[] shapes = new IShape[20]; 
	static{ 
		for(int i=0;i<shapes.length;i++) 
			shapes[i]=new Circle(); 
	}

	public static IShape getShape() { 
		IShape res = null; 
		for (int i = shapes.length - 1; i >= 0; i--) { 
			res = shapes[i]; 
			if (res != null) { 
				shapes[i]=null; 
				break; 
			} 
		}
		return res; 
	}

	public static void relaseShape(IShape circle) { 
		for (int i = 0; i < shapes.length; i++) { 
			IShape obj = shapes[i]; 
			if (obj == null) 
				shapes[i] = circle; 
		} 
	} 
}

使用ExecutorService、Callable、Future实现有返回结果的线程 ,连接池的具体实现实际上是依赖于ThreadPoolExecutor

//需要计算1+...+10000 
public class T1 { 
	public static void main(String[] args) throws Exception { 
		// 创建一个固定大小的连接池,经常实现使用少量线程以应对波峰请求 
		ExecutorService es = Executors.newFixedThreadPool(3); 
		// 为了使用返回结果所以使用Callable 
		Future[] fs = new Future[10]; 
		for (int i = 0; i < fs.length; i++) { 
			Callable<Integer> caller = new MyCallable(i * 1000 + 1, (i + 1) * 1000); 
			// 使用线程池执行任务并获取Future对象 
			fs[i]=es.submit(caller); 
		}
		int res=0; 
		for(int i=0;i<fs.length;i++){ 
			Object obj=fs[i].get(); 
			if(obj!=null && obj instanceof Integer) 
				res+=(Integer)obj; 
		}
		es.shutdown();//关闭线程池 
		System.out.println("Main:"+res); 
	} 
}

class MyCallable implements Callable<Integer> { 
	private int begin; 
	private int end; 
	public MyCallable(int begin, int end) { 
		this.begin = begin; 
		this.end = end; 
	}

	public Integer call() throws Exception { 
		System.out.println(Thread.currentThread()+"---"+begin+".."+end); 
		int res = 0; 
		for (int i = begin; i <= end; i++) 
			res += i; 
		return res; 
	} 
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值