多线程(待补充)

一、多线程

1、程序:为了完成特定的任务用魔种语言编写的一种指令的集合,即指一段静态的静态对象。

2、进程:程序执行的一次过程,或者正在运行的一个程序是一个动态的过程:产生 -> 存在 -> 消亡 进程是资源分配的单位,徐通在运行是为每个进程分配不同的内存区域

3、进程:

(1)进程进一步细化为线程,是程序内部执行的一条路径

(2)若一个进程同时执行多个线程,就是执行多线程

(3)线程作为一个执行和调度的单位,每个线程拥有独立的运行栈和计数器,线程的切换开销比较小,方法区和堆内存被该进程的所有线程共享。


4、并行:多个CPU同时执行多个任务

5、并发:一个CPU(时间片)同时执行多个任务,多个任务之间切换。实际应用场景:秒杀

6、使用多线程的优点:提高应用程序的响应效率,对于图形化界面的意义重大,变相的提高了用户的体验,提高CPU的利用率,优化程序的结构,可以把复杂冗长的进程分为多个线程,提高CPU的利用率,优化程序的结构,可以把复杂冗长的进程分为多个线程,每个线程独立运行。降低耦合度,便于修改

7、使用多线程的时机:需要同时完成多个任务,文件的读写操作,网络的操作,搜索,需要后台运行的时候。

8、一个java程序运行至少需要3个线程,一个是main()主线程,gc()线程,异常处理线程,线程的创建和使用:4种方法(jdk1.5之前只有前两种)


二、实现多线程的方法

1、方法一:

(1)继承Thread类

(2)重写run方法

(3)创建子类对象

(4)调用对象的start()方法

2、方法二:

(1)实现Runnable接口

(2)实现run方法

(3)创建实现类对象放入Thread对象种

(4)调用Thread对象的start()方法,静态代理中,实现Runable:

3、继承Thread类:线程代码是存放在Thread子类run方法;实现Runable:线程代码存在实现类的run方法中。

4、推荐使用实现的方式:

(1)可以避免单继承的局限性。

(2)多个线程共享同一接口实现类的对象,适合多个相同线程处理共享资源。

5、Thread类中的常用方法

(1)void start() 启动线程,并执行对象的run方法

(2)void run() 线程在被调度的时执行的方法。

(3)String getName() 返回线程的名字

(4)setName() 设置线程的名字

(5)static Thread currentThread() 返回当前线程对象

(6)static void yield() 线程让步(把执行权交给优先级更高的线程),会暂停当前正在执行的线程,把执行权交给相优先级、或者高优先级的线程。

交出CPU的执行权后,CPU依旧可能会分配给它执行权。

(7)join() 当前线程调用了join方法,那么当前线程会被阻塞,转而执行join所加进来的线程,直到加入进来的线程执行完毕才会继续指向当前线程

(8)static void sleep() 当前线程休眠指定的毫秒数。

(9)setPriority() 设置线程优先级,最小1、最大10、默认5。

(10)getPriority() 获取优先级,低优先级的线程获得调度的概率不为0,只是有点小

通过继承的方式常见线程,优先级等于父类

(11)在java中线程分为:用户线程和守护线程(通过jvm何时离开进行划分),守护线程是为了用户线程服务的。可以在strat之前调用线程的deamon(true)设置用户线程为守护线程

 

三、线程的生命周期

1、时间片;抢占式:高优先级的线程抢占CPU

2、生命周期:

(1)新建:当Tread类或者其子类被声明创建的时候,新生的线程对象处于新建状态

(2)就绪:新建线程调用了start方法,进入到线程队列,等待CPU的时间片,此时已经具备了运行条件,只是没有获得CPU执行权。

(3)运行:就绪的线程获得CPU的资源,并被调度,进入运行状态

(4)阻塞:将线程被动挂起,将CPU临时终止了线程执行,进入阻塞状态

(5)消亡:被人为的停止、执行完成或出现异常没有处理之后,线程消亡。


四、线程的同步

1、解决线程安全的问题

(1)使用synchronized同步代块

synchronized(Object 同步监听器){

操作共享的代码

}

(2)同步监听器:数可以使用任意对象充当,可以是Object类型

2、解决线程安全的方法:

(1)将共享

(3)可以通过反射获取到线程类的对象:线程类.class

3、同步代码块包裹的必须是操作供选个数据的代码,范围不能太大,否则体现不出多线程的功能;范围太小可能锁不住共享数据

4、解决线程安全问题的第二种方式:

使用同步方法:public synchronized void salaTicket(){};默认获取的同步监听器为this

5、不管使用哪种方法,都要保证同步监听器的唯一

6、一个线程类种的所有静态方法共用同一把锁(类名.class),非静态方法公共的同一把锁(this)。

7、释放锁操作:

(1)同步方法或者同步代码块中return 和 break释放锁操作;

(2)yeild()、sleep()不会释放锁;

(3)执行完同步方法或者同步代码也会释放锁

(4)当前进程同步代码块或者同步方法调用了wait()方法,当前线程阻塞,并且释放锁

(5)当前线程同步方法调用了supend()方法,将线程挂起,不会释放锁。(尽量不要使用supend()方法,与之相对应的还有一个resume()方法,用来放下挂起的状态)

8、单例模式

懒汉式的线程安全:懒汉式需要构造方法私有化

9、死锁:不同的线程分别占用了对方所需要的同步资源不放弃。都在等待对方先放弃同步资源,最终形成了死锁;出现死锁之后不会异常、不会出现提示,所有的线程都处于阻塞状态。

10、解决死锁:尽量避免同步嵌套;尽量减少同步资源的定义;

11、Lock显示锁:接口,和synchronized拥有箱体哦那个的并发性和内存语义。

Lock lock = ...//通常是ReentrantLock对象(是Lock的一个子类)

lock.lock();

try{

共享资源

}finally{

lock.unlock();

}

12、synchronized和Lock对比:

(1)Lock是显示锁:可以手动的开启和关闭锁;synchronized是隐式锁:出了作用域自动释放。

(2)Lock是代码块锁;synchronized有代码块锁和方法所。

(3)JVM中:使用Lock花费的调度线程时间较少,性能更好,扩展性较高。优先使用:Lock-->同步代码块-->同步方法

 

 

 

五、线程的通信

(1)基本思路:进程一种进入等待后,唤醒正在等待的进程二(通过wait()和notify()),同理,进程二进入等待后唤醒正在等待的进程一。

(2)wait():让当前进程挂起,并放弃CPU,同步资源并等待,使别的线程可以访问并修改共享资源,只有其他线程在获取锁之后,调用notify()或者notifyAll()该线程才能被唤醒,并重新排队。

(3)notify():唤醒正在等待同步资源的线程中优先级最高者。

(4)notifyAll():唤醒正在排队等待同步资源的所有线程。

(5)notify和wait只能用在同步方法中


六、线程池

1、非阻塞队列:当队满,入队,数据丢失;对空,出队,得到null

2、阻塞队列:当队满,入队,进行队列,直到出队操作执行之后再入队;出队,等待,当队中有元素之后再出队

LinkedBlockingDeque

3、线程池用的是阻塞队列

4、线程池:节省了线程创建和销毁的时间,线程池中的线程无需创建和销毁,可以直接拿来使用


5、ThreadPoolExecutor(核心线程数,最大线程数,时间,时间单位TimeUnit,阻塞队列)

6、线程池的分类

(1)可缓存:ExutorService es = Excutors.newCachedThreadPool();

(2)定长:ExutorService = Excutors.newFixedThreadPool(3) //核心线程数固定

(4)定时:ExutorService = Excutors.newScheduleThreadPool(3)

(5)单例:ExutorService = Excutors.newSingleThreadPool

 

七、Callable

1、步骤:

(1)实现Cakkable接口

(2)实现call方法

(3)借助FutureTask类

(4)启动线程(FutureTask是Runnable的实现类)

(5)FutureTask类对象的get()方法,方法回调取得call()的返回值

2、对比:相对于run方法 call方法是可以有返回值的,是可以抛异常的、支持泛型 但是需要借助FutureTask类,比如说获取方法返回值

 

posted @ 2019-07-26 08:55  JQbiu  阅读(185)  评论(0编辑  收藏  举报