线程技术讨论

一:线程的概念

多线程是个有意思的概念,一个应用程序,比如酷我音乐或者某个游戏,是一个进程,然后cpu给该进程

分配内存空间,如果电脑内存空间有限,而且运行的程序比较多时就会比较卡,一个进程可能有多个线程

执行,比如游戏中,多个任务,建筑物等都是线程在操作,在单核处理器的情况下,是并发执行的,cpu给

每个线程随机分配时间片,得到时间片的那个任务执行,由于切换的频率比较快,所以用户看起来是一起

执行的。在多核处理器的情况下,是并行的,确实可以一起执行,所以多线程的好处就是不必考虑电脑是否多核,

确实可以提高执行效率。

下面看一个简单的例子如下:

//实现倒计时功能
class CountDown implements Runnable{
	
	private int countDown = 10; //用来计数
	
	public static int taskCount = 0; //用来标记实例,从零开始
	
	public final int id = taskCount++; //定义为final类型,不可修改
	
	public CountDown(){}
	
	public CountDown(int countDown){
		this.countDown = countDown;
	}
	
	public String getStatus(){
		return "#"+id+ "("+(countDown>0?countDown:"liftOff!")+")";
	}
	
	@Override
	public void run() {
		while(countDown-->0){
			System.out.println(getStatus());
		}
		
	}
}

实现一个简单的倒计时功能,countDown用来计数,id用来标记访问实例,在main方法中调用run:

public class TestThread2 {
	public static void main(String[] args) {
		CountDown countDown = new CountDown();
		countDown.run();
	}
}

执行结果:

#0(9)
#0(8)
#0(7)
#0(6)
#0(5)
#0(4)
#0(3)
#0(2)
#0(1)
#0(liftOff!)

顺序执行,实际上上面的执行并没有涉及到线程,计数的执行都是在main线程中执行的,如果想实现多线程执行,就必须

把上面的任务依附到某个线程上。

二:创建线程的两种方式

1:继承Thread

// 创建线程方式一
class MyThread extends Thread {
	public void run() {
		for (int index = 0; index < 100; index++) {
			System.out.println(Thread.currentThread().getName() + ":::" + index);
		}
	}
}
public class TestThread1 {
	
	public static void main(String[] args) {
		Thread thread = new MyThread();
		thread.start();
		for (int index = 0; index < 10; index++) {
			System.out.println(Thread.currentThread().getName() + ":::" + index);
		}
	}
	
}

 

Thread-0:::0
Thread-0:::1
Thread-0:::2
main:::0
Thread-0:::3
Thread-0:::4
Thread-0:::5
Thread-0:::6
Thread-0:::7
Thread-0:::8
Thread-0:::9
main:::1
main:::2
main:::3
main:::4
main:::5
main:::6
main:::7
main:::8
main:::9

从运行结果可以看出,main线程和Thread0线程随机获取cpu时间片执行任务!

 

 

//创建线程方式二
class MyRun implements Runnable{
	
	@Override
	public void run() {
		for (int index = 0; index < 100; index++) {
			System.out.println(Thread.currentThread().getName() + ":::" + index);
		}
	}
}
public class TestThread1 {
	
	public static void main(String[] args) {
		Runnable run = new MyRun();
		Thread thread = new Thread(run);
		thread.start();
		for (int index = 0; index < 10; index++) {
			System.out.println(Thread.currentThread().getName() + ":::" + index);
		}
	}
	
}
main:::0
Thread-0:::0
Thread-0:::1
main:::1
Thread-0:::2
Thread-0:::3
main:::2
Thread-0:::4
Thread-0:::5
main:::3
Thread-0:::6
main:::4
main:::5
main:::6
main:::7
Thread-0:::7
Thread-0:::8
Thread-0:::9
main:::8
main:::9

运行结果类似上面,所以也是多线程执行

三:线程池的使用

如果需要执行主线程之外的线程时,可以用上面的方式创建线程,但是如果并发的任务比较多时,频繁的创建和销毁线程会消耗过多的资源

所以最好的选择是使用线程池,在使用的时候由线程池分配,使用完成由线程池回收掉

1:没有返回值的线程池

newCachedThreadPool 、newFixedThreadPool、newSingleThreadPool这是几种常见的线程池,第一种是线程池是无界的,但是内存

的资源是有限的,一般会有限制,第二种是固定长度线程池,在创建线程池的时候就指定线程的长度,第三种只会创建一个线程,如果第二种

的参数为1就是第三种

public static void main(String[] args) {
		ExecutorService service = Executors.newCachedThreadPool();
		for(int i=0;i<5;i++){
			service.execute(new CountDown());
		}
		service.shutdown();
	}
#0(9)
#0(8)
#0(7)
#0(6)
#0(5)
#0(4)
#0(3)
#0(2)
#0(1)
#0(liftOff!)
#2(9)
#2(8)
#1(9)
#1(8)
#1(7)
#1(6)
#1(5)
#1(4)
#1(3)
#1(2)
#1(1)
#1(liftOff!)
#4(9)
#4(8)
#4(7)
#4(6)
#4(5)
#4(4)
#4(3)
#4(2)
#4(1)
#4(liftOff!)
#2(7)
#2(6)
#2(5)
#2(4)
#2(3)
#2(2)
#2(1)
#2(liftOff!)
#3(9)
#3(8)
#3(7)
#3(6)
#3(5)
#3(4)
#3(3)
#3(2)
#3(1)
#3(liftOff!)

从运行结果可以看出,随机放入5个执行任务到线程池中,然后这5个任务随机抢夺cpu的执行权

newFixedThreadPool和newCacheThreadPool的用法比较类似,cache是第一选择,如果有问题可以选择fixed,对于第三种

newSingleThreadPool,只有一个线程,如果对给它太多的任务,它会阻塞,然后顺序执行每一个任务:

public static void main(String[] args) {
		ExecutorService service = Executors.newSingleThreadExecutor();
		for(int i=0;i<5;i++){
			service.execute(new CountDown());
		}
		service.shutdown();
	}

 

#0(9)
#0(8)
#0(7)
#0(6)
#0(5)
#0(4)
#0(3)
#0(2)
#0(1)
#0(liftOff!)
#1(9)
#1(8)
#1(7)
#1(6)
#1(5)
#1(4)
#1(3)
#1(2)
#1(1)
#1(liftOff!)
#2(9)
#2(8)
#2(7)
#2(6)
#2(5)
#2(4)
#2(3)
#2(2)
#2(1)
#2(liftOff!)
#3(9)
#3(8)
#3(7)
#3(6)
#3(5)
#3(4)
#3(3)
#3(2)
#3(1)
#3(liftOff!)
#4(9)
#4(8)
#4(7)
#4(6)
#4(5)
#4(4)
#4(3)
#4(2)
#4(1)
#4(liftOff!)

这是没有返回值的线程池的用法,这种方式在需要异步调用的时候使用,同步调用的时候需要用到有返回值的线程池

2:有返回值的线程池

如果想让多线程执行完毕后,将结果返回,则需要实现Callable而不是Runnable,重写call()方法,而不是run()方法

//创建有返回值的线程池
class MyCall implements Callable<Integer>{
	
	private int id;
	
	public MyCall(int id){
		this.id = id;
	}
	
	@Override
	public Integer call() throws Exception {
		return id;
	}
	
}

 

public class TestThread3 {
	
	public static void main(String[] args) {
		ExecutorService exec = Executors.newFixedThreadPool(5);
		List<Future<Integer>> result = new ArrayList<Future<Integer>>();
		for(int i=0;i<5;i++){
			Future<Integer> future = exec.submit(new MyCall(i));
			result.add(future);
		}
		for(Future<Integer> future:result){
			Integer i;
			try {
				i = future.get();
				System.out.println(i);
			} catch (InterruptedException | ExecutionException e) {
				e.printStackTrace();
			} finally{
				exec.shutdown();
			}
			
		}
	}
	
}

 

0
1
2
3
4

多线程执行完任务后,最后将结果返回,其中get()会阻塞线程,起到计数器的作用,如果有一个线程没有执行完毕,那么就会一直阻塞

不会结束。

模拟如下:

修改call()方法代码如下:

public Integer call() throws Exception {
		if(3==id){
			Thread.sleep(3000);
		}
		return id;
	}

 那么在运行的时候,就会在id=3处阻塞,3后面的任务都不会执行,直到3执行结束。

今天多线程的知识就总结到这里了,后面继续......

posted @ 2017-06-08 22:02  程序员三藏  阅读(203)  评论(0编辑  收藏  举报