本帖最后由 执迷不悟 于 2019-6-25 09:44 编辑
多线程1
1.1什么是线程 现代操作系统在运行一个程序时,会为其创建一个进程。例如,启动一个java程序,操作系统就会创建一个java的进程。现代操作系统调度的最小单位是线程,也叫轻量级进程,一个进程同时执行和创建多个任务,通常,每一个任务称为一个线程。 这些线程都拥有各自的计数器、堆栈和局部变量等属性,并且能够访问共享的内存变量(并发问题的根源)。处理器在这些线程上高速的切换(CPU分配时间片给每个线程),给人并发处理的感觉。 1.2为什么要使用线程(1)更多的处理器核心 随着处理器的核心越来越多,现在大多数计算机比以往更加擅长处理并发。线程是大多数操作系统的最小单元,而一个线程在同一时刻只能在一个处理器核心上运行,试想一下,一个单线程程序在运行时只能使用一个处理器核心,那么再多的处理器核心也无法加入,也无法提升程序的执行效率。如果改程序使用多线程技术,将计算逻辑分配到多个处理器核心上,就会显著的减少程序的处理时间,并随着更多处理器核心的加入而变得更有效率。 (2)更快的响应 有时候我们会编写一些较为复杂的代码(指复杂业务),例如:一笔订单的创建,它包括插入订单数据、订单快照的生成、发送邮件通知卖家和记录货物销售的数量等。用户从点击“订购”开始,就需要等待这些操作全部完成才能看到结果,这么多操作如何能让它更快速的完成。 可以使用多线程技术,将数据一致性不强的操作派发给其他线程处理(也可以使用消息队列),如生成订单快照,发送邮件等。这样做的好处是响应用户请求的线程可以尽快的处理完成,缩短响应时间,提升用户体验。 1.3线程优先级 每一个线程都有优先级,每当程序调度器有机会选择新的线程,它首先选择的是优先级高的线程,默认情况下一个线程继承它父线程的优先级,可以使用setPriority提高或降低一个线程的优先级,可以设置1到10之间的任何值。但是优先级的高度依赖于系统,线程优先级被映射到宿主主机,优先级根据主机增加或减少。 运行下面可以看到线程优先级没有生效,优先级1和优先级10的结果非常相似,这表示程序的正确性不能依赖线程的优先级高低。 代码: [Java] 纯文本查看 复制代码 public class Priority {
private static volatile boolean notStart=true;
private static volatile boolean notEnd=true;
public static void main(String[] args) throws InterruptedException {
List<Job> jobs = new ArrayList<Job>();
for (int i = 0; i < 10; i++) {
//如果i小于5那个i=1,如果大于51=10
int priority =i < 5 ? Thread.MIN_PRIORITY:Thread.MAX_PRIORITY;
Job job = new Job(priority);//初始化job
jobs.add(job);
Thread thread = new Thread(job, "Thread"+i);//创建线程
thread.setPriority(priority);//设置优先级
thread.start();//启动线程
}
notStart=false;
TimeUnit.SECONDS.sleep(10);
notEnd=false;
for (Job job : jobs) {
System.out.println("job priority:"+job.priority+"== count:"+job.jobCount);
}
}
static class Job implements Runnable{
private int priority;
private long jobCount;
public Job(int priority) {
this.priority=priority;
}
@Override
public void run() {
while (notStart) {
Thread.yield();
}
while (notEnd) {
Thread.yield();
jobCount++;
}
}
}
}
1.4线程的状态Java线程在运行的生命周期中可能处于6中不同的状态,在给定的一个时刻,线程只能处于一个状态。 | 初始状态,线程被构造,但是还没有调用start()方法 | | 运行状态,一旦调用start方法,线程处于runnable状态,一个可运行的线程可能正在运行也可能没有运行,这取决去操作其他给线程提供的运行时间。 | | 阻塞状态,当一个线程处于被阻塞和等待状态,它不运行任何代码且消耗的资源最少,当一个线程视图获取一个内部对象锁,而该锁被其他对象持有,则该线程进入阻塞状态,当所有线程释放该锁,并且线程调度器允许本线程持有它的时候,该线程变成非阻塞状态。 | | 等待状态,表示线程进入等待状态,该状态表示当前线程需要等待其他线程做出一些特定的动作(通知或中断) | | 计时等待,有几个方法有一个超时参数,调用它们导致线程进入计时等待状态,这一状态将保持到超时期满或者接收到适当的通知,带有超时参数方法有Tread.sleep和Object.wait、Tread.Jion、Lock.Trylock以及Condition.await的计时版。 | | 终止状态,线程因如下两个原因之一而被中止: 1.run方法正常退出而自然消亡。 2.因为一个没有捕获的异常终止了run方法而意外死亡。 |
线程的状态变迁: 1.5 Daemon线程 可以通过调用setDaemon将一个线程转换为守护线程,守护线程唯一的用处就是为其他线程提供服务,比如计时线程就是一个例子,它定时发送信号给其他线程或清空高速缓存项的线程,但只剩下一个守护线程,那么守护线程就不需要存在,虚拟机就会退出。 2 启动和终止线程
2.1 构造线程 在线程运行之前首先需要构造一个线程对象,线程对象在构造的时候需要提供线程所需的属性。如线程所属的线程组、线程优先级、是否是Daemon线程等信息。 2.2启动线程 线程对象在初始化完成之后,调用start()方法就可以启动线程,当前线程(即parent线程)同步告知java虚拟机,只要线程调度器空闲,应立即启动调用start()方法的线程。 注意:在启动一个线程前最好个线程起一个名字,方便于问题的排查 2.3理解中断 中断好比其他线程对该线程打了个招呼,其他线程通过调用该线程的interrupt()方法对其进行中断(线程的默认状态是false),线程标识被重置为true,表示该线程的状态为中断。 线程通过isInterrupted()来判断是否被中断,也可以调用静态方法Thread.interrupted()对当前线程进行复位。在调用isInterrupted依旧会返回false,当前线程调用interruped()方法返回线程中断状态,并且会将线程中断状态重置为false。详情请看API. 中断测试: [Java] 纯文本查看 复制代码 public class InterruptedTest implements Runnable{
private static Thread thread;
@Test
public void test(){
thread = new Thread(new InterruptedTest(),"InterruptedTest");
thread.setDaemon(true);
thread.start();
}
@Override
public void run() {
thread.interrupt();//中断线程
boolean interrupted2 = thread.interrupted();//获取线程状态,并将线程状态重置为false
System.out.println(interrupted2);//被中断true,
boolean interrupted = Thread.interrupted();//获取当前状态,false
System.out.println(interrupted);
boolean interrupted3 = Thread.interrupted();//获取当前状态,false
System.out.println(interrupted3);
}
}
2.4过期的suspend()、resume()和stop() 这些API都是过期的不建议使用,以suspend()方法为例,在调用后线程不会释放占有的资源(比如锁),而是带着资源进入休眠状态,如果线程死亡就会出现死锁问题。Stop()方法在终结线程时不会保证线程的资源正常释放。 2.5安全地终止线程 使用boolean变量手动控制线程,是否需要停止任务并终止该线程。 [Java] 纯文本查看 复制代码 public class Shutdown {[/align] public static void main(String[] args) throws Exception {
Runner one = new Runner();
Thread countThread = new Thread(one, "CountThread");
countThread.start();
// 睡眠1秒,main线程对CountThread进行中断,使CountThread能够感知中断而结束
TimeUnit.SECONDS.sleep(1);
countThread.interrupt();
Runner two = new Runner();
countThread = new Thread(two, "CountThread");
countThread.start();
// 睡眠1秒,main线程对Runner two进行取消,使CountThread能够感知on为false而结束
TimeUnit.SECONDS.sleep(1);
two.cancel();
}
private static class Runner implements Runnable {
private long i;
private volatile boolean on = true;
@Override
public void run() {
while (on && !Thread.currentThread().isInterrupted()) {
i++;
}
System.out.println("Count i = " + i);
}
public void cancel() {
on = false;
}
}
}
|