线程技术讨论

一:线程的概念

多线程是个有意思的概念,一个应用程序,比如酷我音乐或者某个游戏,是一个进程,然后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执行结束。

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

时间: 2024-10-23 02:52:21

线程技术讨论的相关文章

【java并发】传统线程技术中的定时器技术

传统线程技术中有个定时器,定时器的类是Timer,我们使用定时器的目的就是给它安排任务,让它在指定的时间完成任务.所以先来看一下Timer类中的方法(主要看常用的TimerTask()方法): 返回值 方法名 方法描述 void schedule(TimerTask task, long delay) 安排在指定延迟后执行指定的任务. void schedule(TimerTask task, long delay, long period) 安排指定的任务从指定的延迟后开始进行重复的固定延迟执

C# 文件去只读工具-线程-技术&amp;分享

using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Linq; using System.Text; using System.Windows.Forms; using System.IO; using Mysoft.Common.Multithread; namespace 除去只读 {

抛开时代背景,任何技术讨论都是瞎耽误功夫

前一阵和一群十几年从业经历的老工程师们小聚,当年技术都很出色但彼此差不多的一群人多年后基本分成三类. 1.有在IBM等大外企的一批,从当年的人前显贵,到现在无法压抑的悲观自嘲.比如一位写DB2核心的大哥说自己至今还坚持一线编程却明知DB2的消失只是时间问题,实在不知道自己这是在干什么. 2.有独自创业的一批,号称多年来从没停止过创业,然而多年过去却没有真的做起来,如今还能骄傲的就是当年的辉煌,见证了多少次身边的成功和至今仍是创业者的身份. 3.还有就是在京东,阿里这样的大互联网公司.显然这些人是

SQL Server 技术讨论专用 QQ 群

强烈推荐 SQL Server 技术讨论专用 QQ 群 QQ 群号: 213291913 注意事项: 本QQ群仅限于交流 SQL Server 技术,请勿灌水,否则直接踢人. 进群后请将群昵称改为实名,便于大家交流,群昵称格式为:地区-姓名. 听从指挥.文明交流.快乐学习.

【java并发】传统线程技术中创建线程的两种方式

传统的线程技术中有两种创建线程的方式:一是继承Thread类,并重写run()方法:二是实现Runnable接口,覆盖接口中的run()方法,并把Runnable接口的实现扔给Thread.这两种方式大部分人可能都知道,但是为什么这样玩就可以呢?下面我们来详细分析一下这两种方法的来龙去脉. 1. 揭秘Thread中run()方法 上面我们看到这两种方式都跟run()方法有关,所以我们来看一下Thread的源码中run()方法到底都干了什么: @Override public void run()

传统线程技术回顾

/** * * @描述: 传统线程技术回顾 . * <p> * *   多线程机制会提高程序的运行效率? * ============================================== 不会,会更慢,因为CPU资源有限 为什么会有多线程下载呢? 是为了抢夺服务器带宽 ============================================== 不一定,多线程又不能提高CPU的主频,也就是单位时间能够执行的指令数目,如果是一个单线程的任务, CPU也只能处理单线

线程技术 ? Future模式

线程技术可以让我们的程序同时做多件事情,线程的工作模式有很多,常见的一种模式就是处理网站的并发,今天我来说说线程另一种很常见的模式,这个模式和前端里的ajax类似:浏览器一个主线程执行javascript,页面渲染等操作,当我们使用ajax向服务端发起请求,由于这个过程很慢,ajax的异步模式可以让我们无需一直等待服务端的响应,而在这个等待结果时间里做其他的事情,这个模式在线程技术力称之为Future模式. Future模式和我前面文章里说到的html5技术里的worker技术差不多,当我们一个

Java 传统线程技术

Java 多线程 在Java中,线程类Thread创建方式有两种:一是继承Thread类,重写run方法:二是,实现Runnable接口.大多数情况下,推荐使用第二种方式,实现runnable接口,这样可以很好的将任务与执行单元分离,更加突出面向对象的思想. 在JDK1.5之前,线程间互斥主依靠内置锁(监视器),而线程间通信则采用Object实例的wait,notify等方法.在JDK1.5之后,增加了很多线程新技术,如线程池.锁.信号量.条件.栅栏.阻塞队列.同步容器等. 1.       传

JAVA多线程提高一:传统线程技术&amp;传统定时器Timer

前面我们已经对多线程的基础知识有了一定的了解,那么接下来我们将要对多线程进一步深入的学习:但在学习之前我们还是要对传统的技术进行一次回顾,本章我们回顾的则是:传统线程技术和传统的定时器实现. 一.传统线程技术 1.创建方式 1.继承thread类 Thread t = new Thread(){ @Override public void run() { } }; t.start(); 2.实现Runnable接口 Thread t1 = new Thread(new Runnable() {