适合初学者学的java多线程

前言

多线程对于java初学来说一直是头疼的知识点,这里我已初学者的视角来给大家总结掌握多线程必备的基本知识点。

1,了解进程,线程及基本知识点

进程:是指一个内存中运行的应用程序,每个进程都有一个独立的内存空间
线程:是进程中的一个执行路径,共享一个内存空间,线程之间可以自由切换,并发执行
一个进程最少有一个线程线程实际上是在进程基础之上的进一步划分,一个进程启动之后,里面的若干执行路径又可以划分成若干个线程

1.1,线程调度

分时调度所有线程轮流使用 CPU 的使用权,平均分配每个线程占用 CPU 的时间。
优先调度优先让优先级高的线程使用 CPU,如果线程的优先级相同,那么会随机选择一个(线程随机性),Java使用的为
抢占式调度。CPU使用抢占式调度模式在多个线程间进行着高速的切换。对于CPU的一个核新而言,某个时刻,只能执行一个线程,而 CPU的在多个线程间切换速度相对我们的感觉要快,看上去就是 在同一时刻运行。 其实,多线程程序并不能提高程序的运行速度,但能够提高程序运行效率,让CPU的 使用率更高。

1.2,同步与异步

同步:排队执行 , 效率低但是安全.
异步:同时执行 , 效率高但是数据不安全.

1.3,并发与并行

并发:指两个或多个事件在同一个时间段内发生。
并行:指两个或多个事件在同一时刻发生(同时发生)。

1.4,临界区

临界区用来表示一种公共资源或者说是共享数据,可以被多个线程使用。但是每一次,只能有一个线程使用它,一旦临界区资源被占用,其他线程要想使用这个资源,就必须等待。在并行程序中,临界区资源是保护的对象。

1.5,阻塞和非阻塞

非阻塞指在不能立刻得到结果之前,该函数不会阻塞当前线程,而会立刻返回,而阻塞与之相反。

2,多线程常见两种方式

2.1,继承Thread类

代码如下(示例):

public class MyThread extends Thread {
	@Override
	public void run() {
		super.run();
		System.out.println("MyThread");
	}
}

启动线程

public static void main(String[] args) {
		MyThread mythread = new MyThread();
		mythread.start();
		System.out.println("运行结束");
	}
}

2.2,实现Runnable接口

代码如下(示例):

public class MyRunnable implements Runnable {
	@Override
	public void run() {
		System.out.println("MyRunnable");
	}
}

启动线程

public static void main(String[] args) {
		Runnable runnable=new MyRunnable();
		Thread thread=new Thread(runnable);
		thread.start();
		System.out.println("运行结束!");
	}

2.3,Runnable 与 Callable

接口定义

//Callable接口
public interface Callable<V> {
V call() throws Exception;
}

Callable使用步骤
1.编写类实现Callable接口 , 实现call方法

class XXX implements Callable<T> {
@Override
public <T> call() throws Exception {
return T;
}
}
  1. 创建FutureTask对象 , 并传入第一步编写的Callable类对象
FutureTask<Integer> future = new FutureTask<>(callable);
  1. 通过Thread,启动线程
new Thread(future).start();

2.3.1,Runnable 与 Callable的相同点

都是接口
都可以编写多线程程序
都采用Thread.start()启动线程

2.3.2,Runnable 与 Callable的不同点

Runnable没有返回值;Callable可以返回执行结果
Callable接口的call()允许抛出异常;Runnable的run()不能抛出

3,常用方法

3.1 currentThread()

返回对当前正在执行的线程对象的引用。

3.2 getId()

返回此线程的标识符

3.3 getName()

返回此线程的名称

3.4 getPriority()

返回此线程的优先级

3.5 isAlive()

测试这个线程是否还处于活动状态。

什么是活动状态呢?

活动状态就是线程已经启动且尚未终止。线程处于正在运行或准备运行的状态。

3.6 sleep(long millis)

使当前正在执行的线程以指定的毫秒数“休眠”(暂时停止执行),具体取决于系统定时器和调度程序的精度和准确性。

3.7 interrupt()

中断这个线程。

3.8 interrupted() 和isInterrupted()

interrupted():测试当前线程是否已经是中断状态,执行后具有将状态标志清除为false的功能

isInterrupted(): 测试线程Thread对相关是否已经是中断状态,但部清楚状态标志

3.9 setName(String name)

将此线程的名称更改为等于参数 name 。

3.10 isDaemon()

测试这个线程是否是守护线程。

3.11 setDaemon(boolean on)

将此线程标记为 daemon线程或用户线程。

3.12 join()

在很多情况下,主线程生成并起动了子线程,如果子线程里要进行大量的耗时的运算,主线程往往将于子线程之前结束,但是如果主线程处理完其他的事务后,需要用到子线程的处理结果,也就是 主线程需要等待子线程执行完成之后再结束,这个时候就要用到join()方法了。

join()的作用是:“等待该线程终止”,这里需要理解的就是该线程是指的主线程等待子线程的终止。也就是在子线程调用了join()方法后面的代码,只有等到子线程结束了才能执行

3.13 yield()

yield()方法的作用是放弃当前的CPU资源,将它让给其他的任务去占用CPU时间。注意:放弃的时间不确定,可能一会就会重新获得CPU时间片。

3.14 setPriority(int newPriority)

更改此线程的优先级

4,线程池Executors

如果并发的线程数量很多,并且每个线程都是执行一个时间很短的任务就结束了,这样频繁创建线程就会大大降低 系统的效率,因为频繁创建线程和销毁线程需要时间.线程池就是一个容纳多个线程的容器,池中的线程可以反复使用,省去了频繁创建线程对象的操作,节省了大量的时间和资源。

4.1 线程池的好处

降低资源消耗。
提高响应速度。
提高线程的可管理性

4.2 Java中的四种线程池 . ExecutorService

4.2.1 缓存线程池

(长度无限制)
执行流程:
1.判断线程池是否存在空闲线程
2.存在则使用
3. 不存在,则创建线程 并放入线程池, 然后使用

ExecutorService service = Executors.newCachedThreadPool();
//向线程池中 加入 新的任务
service.execute(new Runnable() {
@Override
public void run() {
System.out.
println("线程的名称:"+Thread.currentThread().getName());
}
});
service.execute(new Runnable() {
@Override
public void run() {
System.out.println("线程的名称:"+Thread.currentThread().getName());}
});
service.execute(new Runnable() {
@Override
public void run() {
System.out.println("线程的名称:"+Thread.currentThread().getName());
}
});

4.2.2 定长线程池

(长度是指定的数值)
执行流程:
1.判断线程池是否存在空闲线程
2.存在则使用
3. 不存在空闲线程,且线程池未满的情况下,则创建线程 并放入线程池, 然后使用
4. 不存在空闲线程,且线程池已满的情况下,则等待线程池存在空闲线程

ExecutorService service = Executors.newFixedThreadPool(2);
service.execute(new Runnable() {
@Override
public void run() {
System.out.println("线程的名称:"+Thread.currentThread().getName());}
});
service.execute(new Runnable() {
@Override
public void run() {
System.out.println("线程的名称:"+Thread.currentThread().getName());}});

4.2.3 单线程线程池

1.判断线程池 的那个线程 是否空闲
2. 空闲则使用
3. 不空闲,则等待 池中的单个线程空闲后 使用

ExecutorService service = Executors.newSingleThreadExecutor();
service.execute(new Runnable() {
@Override
public void run() {
System.out.println("线程的名称:"+Thread.currentThread().getName());}});
service.execute(new Runnable() {
@Override
public void run() {
System.out.println("线程的名称:"+Thread.currentThread().getName());}});

4.2.4 周期性任务定长线程池

执行流程:
1.判断线程池是否存在空闲线程
2.存在则使用
3. 不存在空闲线程,且线程池未满的情况下,则创建线程 并放入线程池, 然后使用
4. 不存在空闲线程,且线程池已满的情况下,则等待线程池存在空闲线程
周期性任务执行时:
定时执行, 当某个时机触发时, 自动执行某任务

ScheduledExecutorService service = Executors.newScheduledThreadPool(2);
service.schedule(new Runnable() {
@Override
public void run() {
System.out.
println("俩人相视一笑~嘿嘿嘿");}
},5,2,TimeUnit.SECONDS);

参数1. runnable类型的任务
参数2.时长数字(延迟执行的时长)
参数3. 周期时长(每次执行的间隔时间)
参数4.时长数字的单位

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值