线程控制方法
- Java提供一个线程调度器来监控程序中启动后进入就绪状态的所有线程。线程调度器 按照线程的优先级决定应调度哪个线程来执行。
- 线程的优先级用数字表示,范围从1到10
Thread.MIN_PRIORITY = 1
Thread.MAX_PRIORITY = 10
Thread.NORM_PRIORITY = 5
- 使用下述方法获得或设置线程对象的优先级。
int getPriority();
void setPriority(int newPriority);
注意:优先级低只是意味着获得调度的概率低。并不是绝对先调用优先级高后调用 优先级低的线程。
-
join ()
:阻塞指定线程等到另一个线程完成以后再继续执行 -
sleep ()
:使线程停止运行一段时间,将处于阻塞状态,如果调用了sleep方法之后,没有其他等待执行的线程,这个时候当前线程不会马上恢复执行! -
yield ()
:让当前正在执行线程暂停,不是阻塞线程,而是将线程转入就绪状态.如果调用了yield方法之后,没有其他等待执行的线程,这个时候当前线程就会马上恢复执行! -
setDaemon()
- 可以将指定的线程设置成后台线程
- 创建后台线程的线程结束时,后台线程也随之消亡
- 只能在线程启动之前把它设为后台线程
-
interrupt()
:并没有直接中断线程,而是需要被中断线程自己处理 -
stop()
:结束线程,不推荐使用
线程同步实战
-
应用场景:多个用户同时操作一个银行账户。每次取款100元,取款前先检查余额是否足够。如果不够, 放弃取款
-
分析
- 使用多线程解决
- 开发一个取款线程类,每个用户对应一个线程对象
- 因为多个线程共享同一个银行账户,使用Runnable方式解决
-
思路
- 创建银行账户类Account
- 创建取款线程AccountRunnable
- 创建测试类TestAccount,让两个用户同时取款
-
当多个线程访问同一个数据时,容易出现线程安全问题。需要让线程同步,保证数据安全
-
当两个或两个以上线程访问同一资源时,需要某种方式来确保资源在某一时刻只被一个线程使用
线程同步的实现方案
- 同步代码块
synchronized (obj){ }
- 同步方法
private synchronized void makeWithdrawal(int amt) {}
同步监视器
- synchronized (obj){ }中的obj称为同步监视器
- 同步代码块中同步监视器可以是任何对象,但是推荐使用共享资源作为同步监视器
- 同步方法中无需指定同步监视器,因为同步方法的同步监视器是this,也就是该对象本事
同步监视器的执行过程
- 第一个线程访问,锁定同步监视器,执行其中代码
- 第二个线程访问,发现同步监视器被锁定,无法访问
- 第一个线程访问完毕,解锁同步监视器
- 第二个线程访问,发现同步监视器未锁,锁定并访问
Lock锁
- JDK1.5后新增功能,与采用synchronized相比,lock可提供多种锁方案,更灵活
-
java.util.concurrent.lock
中的 Lock 框架是锁定的一个抽象,它允许把锁定的实现作为 Java 类,而不是作为语 言的特性来实现。这就为 Lock 的多种实现留下了空间,各种实现可能有不同的调度算法、性能特性或者锁 定语义。 - ReentrantLock 类实现了 Lock ,它拥有与 synchronized 相同的并发性和内存语义, 但是添加了类似锁投票、 定时锁等候和可中断锁等候的一些特性。此外,它还提供了在激烈争用情况下更佳的性能。
- 注意:如果同步代码有异常,要将unlock()写入finally语句块
Lock和synchronized的区别
- Lock是显式锁(手动开启和关闭锁,别忘记关闭锁),synchronized是隐式锁
- Lock只有代码块锁,synchronized有代码块锁和方法锁
- 使用Lock锁,JVM将花费较少的时间来调度线程,性能更好。并且具有更好的扩展性(提供更多的子类)
优先使用顺序: - Lock----同步代码块(已经进入了方法体,分配了相应资源)----同步方法(在方法体之外)
线程同步的好处
解决了线程安全问题
线程同步的缺点
- 性能下降
- 会带来死锁
死锁
- 当两个线程相互等待对方释放“锁”时就会发生死锁
- 出现死锁后,不会出现异常,不会出现提示,只是所有的线程都处于阻塞状态,无法继续
- 多线程编程时应该注意避免死锁的发生