进程:
线程:
多线程的优点:
通过java.lang.Thread类来提现。
Thread类的特性:
构造器:
方式一:继承Thread类
注意点:
区别:
继承Thread:线程代码存放Thread子类run方法中。
实现Runnable:线程代码存在接口的子类的run方法。
实现方式的好处:
避免了单继承的局限性
多个线程可以共享同一个接口实现类的对象,非常适合多个相同线程来处理同一份资源。
Java的调度方法:
同优先级线程组成先进先出队列(先到先服务),使用时间片策略
对高优先级,使用优先调度的抢占式策略
优先级:
MAX_PRIORITY:10
MIN _PRIORITY:1
NORM_PRIORITY:5
涉及的方法:
getPriority() :返回线程优先值
setPriority(intnewPriority) :改变线程的优先级
说明:
线程创建时继承父线程的优先级
低优先级只是获得调度的概率低,并非一定是在高优先级线程之后才被调用
在它的一个完整的生命周期中通常要经历如下的五种状态:
模拟火车站售票程序,开启三个窗口售票。
package multiThread; public class ThreeTickets { public static void main(String[] args) { Ticket t = new Ticket(); Thread t1 = new Thread(t); Thread t2 = new Thread(t); Thread t3 = new Thread(t); t1.start(); t2.start(); t3.start(); } } class Ticket implements Runnable { private int tick = 100; public void run() { while(true) { if(tick > 0) { System.out.println(Thread.currentThread().getName()+"售出车票,tick号为:"+tick--); } } } }
这个时候多线程出现了安全问题,当多条语句在操作同一个线程共享数据时,一个线程对多条语句只执行了一部分,还没有执行完,另一个线程参与进来执行,导致共享数据的错误。上图t1进入线程但是输出前被阻塞,t2线程执行时,tick的值还是1,继续执行,仍然在输出前被阻塞,t3执行是,tick的值还是没有变,还是为1,继续执行,仍然被阻塞,t1阻塞完继续执行输出语句,打印车票为1号,tick-1 = 0,t2阻塞完成后输出打印车票时tick=0,tick-1= -1,t3阻塞完成继续输出打印,此时tick=-1。t2和t3线程出现了错误。解决办法为对多条操作共享数据的语句,只能让一个线程都执行完,在执行过程中,其他线程不可以参与执行,提出了专业的解决方式:同步机制,使用关键字synchronized来达到这一目的。
Synchronized的使用方法
1、synchronized(对象){
//需要被同步的代码;
}
2、synchronized还可以放在方法声明中,表示整个方法为同步方法。
例如:
public synchronized void show (String name){
....
}
必须确保使用同一个资源的多个线程共用一把锁,这个非常重要,否则就无法保证共享资源的安全
如何确定代码是否存在线程安全?(非常重要)
(1)明确哪些代码是多线程运行的代码
(2)明确多个线程是否有共享数据
(3)明确多线程运行代码中是否有多条语句操作共享数据
释放锁的操作
1)当前线程的同步方法、同步代码块执行结束。
2)当前线程在同步代码块、同步方法中遇到break、return终止了该代码块、该方法的继续执行。
3)当前线程在同步代码块、同步方法中出现了未处理的Error或Exception,导致异常结束。
4)当前线程在同步代码块、同步方法中执行了线程对象的wait()方法,当前线程暂停,并释放锁。
线程的死锁问题
死锁: 不同的线程分别占用对方需要的同步资源不放弃,都在等待对方放弃自己需要的同步资源,就形成了线程的死锁;出现死锁后,不会出现异常,不会出现提示,只是所有的线程都处于阻塞状态,无法继续
解决方法:专门的算法、原则;尽量减少同步资源的定义;尽量避免嵌套同步
Lock(锁)
从JDK 5.0开始,Java提供了更强大的线程同步机制——通过显式定义同步锁对象来实现同步。同步锁使用Lock对象充当。
ReentrantLock 类实现了Lock ,它拥有与synchronized 相同的并发性和内存语义,在实现线程安全的控制中,比较常用的是ReentrantLock,可以显式加锁、释放锁
Lock是显式锁(手动开启和关闭锁,别忘记关闭锁)
线程的通信
wait():令当前线程挂起并放弃CPU、同步资源并等待,使别的线程可访问并修改共享资源,而当前线程排队等候其他线程调用notify()或notifyAll()方法唤醒,唤醒后等待重新获得对监视器的所有权后才能继续执行。调用方法的必要条件:当前线程必须具有对该对象的监控权(加锁)调用此方法后,当前线程将释放对象监控权,然后进入等待
notify():唤醒正在排队等待同步资源的线程中优先级最高者结束等待,当前线程必须具有对该对象的监控权(加锁)
notifyAll():唤醒正在排队等待资源的所有线程结束等待.
这三个方法只有在synchronized方法或synchronized代码块中才能使用。
4、新增线程创建方式
方式一:实现Callable接口
方式二:使用线程池
经常创建和销毁、使用量特别大的资源,比如并发情况下的线程,对性能影响很大。提前创建好多个线程,放入线程池中,使用时直接获取,使用完放回池中,可以避免频繁创建销毁、实现重复利用。类似生活中的公共交通工具。
好处:提高响应速度(减少了创建新线程的时间);降低资源消耗(重复利用线程池中线程,不需要每次都创建);便于线程管理
注:尚硅谷多线程学习笔记
原文:https://www.cnblogs.com/lgh544/p/12878130.html