A股上市公司传智教育(股票代码 003032)旗下技术交流社区北京昌平校区

 找回密码
 加入黑马

QQ登录

只需一步,快速开始

© 执迷不悟 中级黑马   /  2019-6-25 09:40  /  852 人查看  /  0 人回复  /   0 人收藏 转载请遵从CC协议 禁止商业使用本文

本帖最后由 执迷不悟 于 2019-6-25 09:44 编辑

            多线程1

1.1什么是线程
  现代操作系统在运行一个程序时,会为其创建一个进程。例如,启动一个java程序,操作系统就会创建一个java的进程。现代操作系统调度的最小单位是线程,也叫轻量级进程,一个进程同时执行和创建多个任务,通常,每一个任务称为一个线程。
这些线程都拥有各自的计数器、堆栈和局部变量等属性,并且能够访问共享的内存变量(并发问题的根源)。处理器在这些线程上高速的切换(CPU分配时间片给每个线程),给人并发处理的感觉。
1.2为什么要使用线程
(1)更多的处理器核心
     随着处理器的核心越来越多,现在大多数计算机比以往更加擅长处理并发。线程是大多数操作系统的最小单元,而一个线程在同一时刻只能在一个处理器核心上运行,试想一下,一个单线程程序在运行时只能使用一个处理器核心,那么再多的处理器核心也无法加入,也无法提升程序的执行效率。如果改程序使用多线程技术,将计算逻辑分配到多个处理器核心上,就会显著的减少程序的处理时间,并随着更多处理器核心的加入而变得更有效率。
(2)更快的响应
  有时候我们会编写一些较为复杂的代码(指复杂业务),例如:一笔订单的创建,它包括插入订单数据、订单快照的生成、发送邮件通知卖家和记录货物销售的数量等。用户从点击“订购”开始,就需要等待这些操作全部完成才能看到结果,这么多操作如何能让它更快速的完成。
可以使用多线程技术,将数据一致性不强的操作派发给其他线程处理(也可以使用消息队列),如生成订单快照,发送邮件等。这样做的好处是响应用户请求的线程可以尽快的处理完成,缩短响应时间,提升用户体验。
1.3线程优先级
  每一个线程都有优先级,每当程序调度器有机会选择新的线程,它首先选择的是优先级高的线程,默认情况下一个线程继承它父线程的优先级,可以使用setPriority提高或降低一个线程的优先级,可以设置110之间的任何值。但是优先级的高度依赖于系统,线程优先级被映射到宿主主机,优先级根据主机增加或减少。
运行下面可以看到线程优先级没有生效,优先级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中不同的状态,在给定的一个时刻,线程只能处于一个状态。
new
初始状态,线程被构造,但是还没有调用start()方法
runnable
运行状态,一旦调用start方法,线程处于runnable状态,一个可运行的线程可能正在运行也可能没有运行,这取决去操作其他给线程提供的运行时间。
blocked
阻塞状态,当一个线程处于被阻塞和等待状态,它不运行任何代码且消耗的资源最少,当一个线程视图获取一个内部对象锁,而该锁被其他对象持有,则该线程进入阻塞状态,当所有线程释放该锁,并且线程调度器允许本线程持有它的时候,该线程变成非阻塞状态。
Waiting
等待状态,表示线程进入等待状态,该状态表示当前线程需要等待其他线程做出一些特定的动作(通知或中断)
timed Waiting
计时等待,有几个方法有一个超时参数,调用它们导致线程进入计时等待状态,这一状态将保持到超时期满或者接收到适当的通知,带有超时参数方法有Tread.sleepObject.waitTread.JionLock.Trylock以及Condition.await的计时版。
Terminated
终止状态,线程因如下两个原因之一而被中止:
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;
        }
    }
}


0 个回复

您需要登录后才可以回帖 登录 | 加入黑马