java(八)多线程

多线程

线程

  • 线程是程序执行的一条路径, 一个进程中可以包含多条线程。
  • 多线程并发执行可以提高程序的效率, 可以同时完成多项工作。

多线程
从软件或硬件上实现多个线程并发执行的技术。

多线程并行和并发的区别

并行就是两个任务同时运行,就是甲任务进行的同时,乙任务也在进行。(需要多核CPU)
并发是指两个任务都请求运行,而处理器只能按受一个任务,就把这两个任务安排轮流进行,由于时间间隔较短,使人感觉两个任务都在运行。

Java程序运行原理和JVM的启动是多线程的吗?

  • A:Java程序运行原理
    Java命令会启动java虚拟机,启动JVM,等于启动了一个应用程序(一个进程)。该进程会自动启动一个 “主线程” ,然后主线程去调用某个类的 main 方法。
  • B:JVM的启动是多线程的吗
    JVM启动至少启动了垃圾回收线程和主线程,所以是多线程的。

多线程有几种实现方法,都是什么?

主要有三种:继承Thread类;实现Runnable接口;使用ExecutorService、Callable、Future实现有返回结果的多线程。
1.继承Thread类
若需要访问当前线程,只需使用this即可,无需使用Thead.currentThead()方法。

public class MyThreadTest {
                public static void main(String[] args) {
                    MyThread mt = new MyThread();               //4,创建自定义类的对象
                    mt.start();                                 //5,开启线程, 内部会自动执行run方法  
                }
            }

            class MyThread extends Thread {                     //1,定义类继承Thread
                public void run() {                             //2,重写run方法
                //要做的事                                      //3,将要执行的代码,写在run方法中
                }
            }

2.通过Runnable接口实现
解决线程类不能被多继承的问题。

public class MyRunnableTest {
                public static void main(String[] args) {
                    //Runnable target = new MyRunnable();        //父类引用子类对象
                    MyRunnable mr = new MyRunnable();           //4,创建自定义类对象
                    Thread t = new Thread(mr);                  //5,将其当作参数传递给Thread的构造函数
                    t.start();                                  //6,开启线程
                }
            }

            class MyRunnable implements Runnable {              //1,自定义类实现Runnable接口
                @Override
                public void run() {                             //2,重写run方法
                    //要做的事                                  //3,将要执行的代码,写在run方法中
                }
            }

原理
1.看Thread类的构造函数,传递了Runnable接口的引用
2.通过init()方法找到传递的target给成员变量的target赋值
3.查看run方法,发现run方法中有判断,如果target不为null就会调用Runnable接口子类对象的run方法

Thread 和Runnable的区别

  • 继承Thread : 由于子类重写了Thread类的run(), 当调用start()时, 直接找子类的run()方法。
    • 好处是:可以直接使用Thread类中的方法,代码简单。
    • 弊端是:如果已经有了父类,就不能用这种方法。
  • 实现Runnable : 构造函数中传入了Runnable的引用, 成员变量记住了它, start()调用run()方法时内部判断成员变量Runnable的引用是否为空, 不为空编译时看的是Runnable的run(),运行时执行的是子类的run()方法。
    • 好处是:即使自己定义的线程类有了父类也没关系,因为有了父类也可以实现接口,而且接口是可以多实现的。
    • 弊端是:不能直接使用Thread中的方法需要先获取到线程对象后,才能得到Thread的方法,代码复杂。

3.使用ExecutorService、Callable、Future实现
都属于Executor框架的功能类。执行Callable任务后,可以获取一个Future的对象,在该对象上调用get()即可获取到Callable任务返回的Object,再结合线程池接口ExecutorService即可实现有返回结果的多线程。

匿名内部类实现线程的这两种方法:
继承Thread类:

new Thread() {                                  //1,new 类(){}继承这个类
            public void run() {                 //2,重写run方法
                //要做的事                      //3,将要执行的代码,写在run方法中
            }
        }.start();                              //4.启动线程

实现Runnable接口:

    new Thread(new Runnable(){                  //1,new 接口(){}实现这个接口
            public void run() {                 //2,重写run方法
                //要做的事                      //3,将要执行的代码,写在run方法中
            }
        }).start();                             //4.启动线程

this.getName():获取线程对象名字。
线程对象.setName(“XXXX”);设置线程对象名字。
休眠线程:Thread.slepp(毫秒,纳秒):控制当前线程休眠若干毫秒。
守护线程:线程对象.setDaemon(boolean):设置一个线程为守护线程,该线程不会单独执行,当其他非守护线程都执行结束后,自动退出。
加入线程:join():当前线程暂停,等待指定的线程执行结束后,当前线程再继续。
join(int):可以等待指定的毫秒之后继续。
礼让线程:yield():让出CPU。
setPriority():设置线程的优先级。
notify():随机唤醒单个等待的线程。
notifyAll():唤醒所有等待的线程。

**互斥锁Lock:**JDK1.5版本的新特性
1.同步:使用ReentrantLock类的lock()和unlock()方法进行同步
2.通信
* 使用ReentrantLock类的newCondition()方法可以获取Condition对象。
* 需要等待的时候使用Condition的await()方法, 唤醒的时候用signal()方法。
* 不同的线程使用不同的Condition, 这样就能区分唤醒的时候找哪个线程了。
lock():获取锁。
unlock():释放锁。

同步:当多线程并发,有多段代码同时执行,我们希望某一段代码执行过程中CPU不要切换到其他线程工作。
若两段代码时同步的,那么同一时间只能执行一段,在一段代码没执行结束之前,不会执行另外一段代码。
多个同步代码块如果使用相同的锁对象,那么它们就是同步的。
注意:

  • 锁对象可以是任何对象,但是被锁的代码需要保证是同一把锁,不能用匿名对象。
  • 非静态同步函数的锁是:this。
  • 静态同步函数的锁是:字节码对象。
    死锁:多线程同步的时候,若同步代码嵌套,使用相同锁,可能会出现死锁。

线程组: ThreadGroup
它可以对一批线程进行分类管理,Java允许程序直接对线程组进行控制。
默认情况下,所有的线程都属于主线程组。

线程池

程序启动一个新线程成本是比较高的,因为它涉及到要与操作系统进行交互。而使用线程池可以很好的提高性能,尤其是当程序中要创建大量生存期很短的线程时,更应该考虑使用线程池。线程池里的每一个线程代码结束后,并不会死亡,而是再次回到线程池中成为空闲状态,等待下一个对象来使用。在JDK5之前,我们必须手动实现自己的线程池,从JDK5开始,Java内置支持线程池。

ExecutorService pool = Executors.newFixedThreadPool(2);//创建线程池
pool.submit(new MyRunnable());//将线程放进池子里并执行
pool.submit(new MyRunnable());

pool.shutdown();//结束线程池

启动一个线程是用run()还是start()?
答:启动一个线程是调用start()方法,使线程就绪状态,以后可以被调度为运行状态,这并不意味着线程就会立即运行。当cpu分配给它时间时,才开始执行run()方法(如果有的话)。一个线程必须关联一些具体的执行代码,run()方法是该线程所关联的执行代码。

线程有几种运行状态?(线程的生命周期)
1.新建状态
用new语句创建的线程对象,在堆内存中被分配内存。
2.就绪状态
当一个线程创建之后,其他线程调用了它的start()方法,该线程即进入就绪状态。
处于这个状态的线程位于可运行池中,等待获得CPU的使用权。
3.运行状态
处于运行状态的线程占用CPU,执行程序的代码。
4.阻塞状态
Java虚拟机不会给线程分配CPU,直到线程重新进入就绪状态,它才有机会转到运行状态。
5.死亡状态
线程执行结束。
这里写图片描述

阻塞状态的三种情况
1.位于对象等待池中的阻塞状态:当线程运行时,若执行了某个对象的wait()方法,Java虚拟机就会把线程放到这个对象的等待池中。
2.位于对象锁中的阻塞状态:当线程运行时,试图获得某个对象的同步锁时,若该对象的同步锁已经被其他线程占用,JVM就会把这个线程放到这个对象的锁池中。
3.其他的阻塞状态:当前线程执行了sleep()方法,或者调用了其他线程的join()方法,或者发出I/O请求。

终止线程有几种方法?
1.使用退出表值,使线程正常退出(即当run()方法完成后线程终止)。
2.使用Thread的interrupt()方法中断线程。
3.使用Thread的stop()方法强制终止线程(不推荐,因为stop(),suspend(),resume()可能会发生不可预料的结果)。

sleep() 和 wait() 的区别
都是停止当前线程。

  • sleep来自Thread类。必须传入参数,时间到了自动醒来。
    导致此线程暂停执行指定时间,将执行机会让给其他线程,但监控状态依然保持,到暂停时间后会自动恢复。不会释放对象锁。
  • wait来自Object类。可传入参数,在时间结束后等待,不传入参数就是直接等待。
    会导致本线程放弃对象锁,进入等待此对象的等待锁定池,只有针对此对象发出notify()或notifyAll()后本线程才进入对线锁定池准备获得对象锁进入运行状态。

sleep() 和 yield() 的区别
sleep():让当前正在执行的线程休眠。sleep(0)可以替代yield()。
yield():暂停当前正在执行的线程对象,并执行其他线程,即交出CPU使用时间。
1.sleep()不考虑其他线程的优先级,会给较低线程一个运行机会;
yield()只会给相同或更高优先级的线程一个运行机会。
2.执行sleep(long millis)后,将转到阻塞状态。
执行yield()后,将转到就绪状态。
3.sleep()声明抛出InterruptedException异常,而yield()没有声明抛出任何异常。
4.sleep比yield()更有移植性。

Synchronized和Lock异同
都可以解决线程安全问题,且Lock能完成Synchronized实现的所有功能。
Synchronized:会自动释放锁。
Lock:需要手动释放,且必须在finally从句中释放。
在子线程更新UI
在Android开发中,子线程不能更新UI,而主线程又不能进行耗时操作。
在子线程中进行耗时操作,完成之后发送消息,通知主线程更新UI。或者使用异步任务,异步任务的实质也是对消息机制的封装。
https://www.cnblogs.com/joy99/p/6121280.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值