- 1、昨日复习
- 2、线程的生命周期
- 3、线程的同步
- 4、线程的通信
- 5、jdk新增线程创建方式
1、昨日复习
谈谈你对程序、进程、线程的理解
代码完成继承Thread的方式创建分线程,并遍历100以内的自然数
代码完成实现Runnable接口的方法创建分线程,并遍历100以内的自然数
对比两种创建方式
说说你对IDEA中Project和Module的理解workspace projectproject modulepackage package
2、线程的生命周期

3、线程的同步
问题的提出:多个线程执行的不确定性引起执行结果的不稳定 多个线程对账本的共享,会造成操作的不完整性,会破坏数据。1、问题:买票过程中出现了,重票、错票———->出现了线程的安全问题。2、原因:当某个线程操作中,尚未完成时,其他线程参与进来,也操作车票。3、如何解决:当一个线程在操作票时,其他线程不能参与进来,直到线程a操作完,其他线程才可以开始操作票,即使线程a出现了阻塞,也不能改变。4、java中,通过同步机制,来解决线程的安全问题。
方式一:同步代码块synchronized(同步监视器){需要被同步的代码}说明:1、操作共享数据的代码,即为需要被同步的代码。—>不能包含代码多了,也不能少了。 2、共享数据:多个线程共同操作的变量。比如:票的数量。 3、同步监视器:锁。任何类的对象都可以充当锁。该对象不能是NULL值。 要求:多个线程必须共用同一把锁 补充:在实现Runnable接口创建多线程的方式中,可以考虑使用this充当同步监视器。在继承Thread类创建多线程的方式中,慎用this充当同步监视器,考虑使用当前类充当同步监视器
方式二:同步方法如果操作共享数据的代码完整的声明在一个方法中,我们不妨将此方法声明为同步的。1、同步方法任然涉及到同步监视器,只是不需要我们显示的声明。2、非静态的同步方法,同步监视器默认是:this静态的同步方法,同步监视器默认是:当前类本身。
5、同步的方式,解决了线程的安全问题。——>好处。 操作同步代码时,只能有一个线程参与,其他线程等待。相当于是一个单线程,效率低。——>局限性
1、死锁的理解:不同的线程分别占用对方需要的同步资源不放弃,都在等待对方放弃自己需要的同步资源,就形成了线程的死锁 2、说明:(1)出现死锁后,不会出现异常,不会出现报错,只是所有的线程都处于阻塞状态,无法继续(2)我们使用同步时,要避免出现死锁。解决方法 :专门的算法、原则 尽量减少同步资源的定义 尽量避免嵌套同步
方式三:Lock锁——-jdk5.0新增

package com.atguigu.java3;/*银行有一个账户。有两个储户分别向同一个账户存3000元,每次存1000,存3次。每次存完打印账户余额。*/public class AccountTest {public static void main(String[] args) {Account acct = new Account(0);Customer c1 = new Customer(acct);Customer c2 = new Customer(acct);c1.setName("甲");c2.setName("乙");c1.start();c2.start();}}class Account {private double balance;public Account(double balance) {this.balance = balance;}public synchronized void depoist(double amt){if (amt>0){try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}balance+=amt;System.out.println(Thread.currentThread().getName()+":存钱成功,余额为:"+this.balance);}}}class Customer extends Thread{private Account account;public Customer(Account account) {this.account = account;}@Overridepublic void run() {for (int i = 0; i < 3; i++) {account.depoist(1000);}}}
4、线程的通信
涉及到的三个方法:wait():一旦执行该方法,当前线程就进入阻塞状态,并释放同步监视器。notify():一旦执行该方法,就会唤醒wait的一个线程,如果有多个wait的线程,就唤醒优先级高的notifyAll():一旦执行该方法,就会唤醒所有的被wait的线程。说明:1、三个方法必须使用在同步代码块或同步方法中。2、这三个方法的调用者必须是同步代码块或同步方法中的同步监视器,否则会出异常。3、这三个方法是定义在Object类中的
面试题:sleep()和wait()的异同1、相同点:一旦执行方法,都可以使得当前线程进入阻塞状态2、不同点:(1)两个方法声明的位置不同:Thread类中声明sleep(),Object()类中声明wait()(2)调用的范围不同:sleep()可以在任何需要的场景下调用,wait()必须使用在同步代码块 或同步方法中。(3)关于是否释放同步监视器:如果两个方法都使用在同步代码块或同步方法中,sleep不会释放 锁,wait会释放锁。
package com.atguigu.java4;/*生产者(Productor)将产品交给店员(Clerk),而消费者(Customer)从店员处取走产品,店员一次只能持有固定数量的产品(比如:20),如果生产者试图生产更多的产品,店员会叫生产者停一下,如果店中有空位放产品了再通知生产者继续生产;如果店中没有产品了,店员会告诉消费者等一下,如果店中有产品了再通知消费者来取走产品。分析:1、是否是多线程?是2、是否有共享数据?是3、如何解决线程的安全?三种4、是否涉及到线程的通信?是*/public class ProductTest {public static void main(String[] args) {Clerk clerk= new Clerk();Productor p1 = new Productor(clerk);p1.setName("生产者1");Customer c1 = new Customer(clerk);c1.setName("消费者1");Customer c2 = new Customer(clerk);c2.setName("消费者2");p1.start();c1.start();c2.start();}}class Clerk {private int productCount=0;public synchronized void produceProduct() {//thisif (productCount<20){productCount++;System.out.println(Thread.currentThread().getName()+":开始生产第"+productCount+"个产品");notify();}else {try {wait();} catch (InterruptedException e) {e.printStackTrace();}}}public synchronized void consumeProduct() {//thisif (productCount>0){System.out.println(Thread.currentThread().getName()+":开始消费第"+productCount+"个产品");productCount--;notify();}else {try {wait();} catch (InterruptedException e) {e.printStackTrace();}}}}class Productor extends Thread {private Clerk clerk;public Productor(Clerk clerk) {this.clerk = clerk;}@Overridepublic void run() {System.out.println(getName()+":开始生产产品。。。");while (true){try {sleep(10);} catch (InterruptedException e) {e.printStackTrace();}clerk.produceProduct();}}}class Customer extends Thread {private Clerk clerk;public Customer(Clerk clerk) {this.clerk = clerk;}@Overridepublic void run() {System.out.println(getName()+":开始消费产品。。。");while (true){try {sleep(20);} catch (InterruptedException e) {e.printStackTrace();}clerk.consumeProduct();}}}
5、jdk新增线程创建方式
新增方式一:实现Callable接口与使用Runnable相比, Callable功能更强大些 相比run()方法,可以有返回值 方法可以抛出异常 支持泛型的返回值 需要借助FutureTask类,比如获取返回结果
Future接口 可以对具体Runnable、Callable任务的执行结果进行取消、查询是否完成、获取结果等。 FutrueTask是Futrue接口的唯一的实现类 。FutureTask 同时实现了Runnable, Future接口。它既可以作为Runnable被线程执行,又可以作为Future得到Callable的返回值
1、创建一个实现Callable的实现类2、实现call方法,将此线程需要执行的操作声明在call中3、创建一个Callable接口实现类的对象4、将此Callable接口实现类的对象作为参数传递到FutureTask构造器中,创建FutureTask的对象5、将FutureTask的对象作为参数传递到Thread类的构造器中,创建Thread对象,并调用start()方法。6、获取Callable中call方法的返回值,调用FutureTask的对象.get();方法。
如何理解实现Callable接口的方法创建多线程比实现Runnable接口创建多线程方式强大?1、call()可以有返回值2、call()可以抛出异常,被外面的操作捕获,获取异常的信息3、call()是支持泛型的
新增方式二:使用线程池背景:经常创建和销毁、使用量特别大的资源,比如并发情况下的线程,对性能影响很大。 思路:提前创建好多个线程,放入线程池中,使用时直接获取,使用完放回池中。可以避免频繁创建销毁、实现重复利用。类似生活中的公共交通工具。 好处: 提高响应速度(减少了创建新线程的时间) 降低资源消耗(重复利用线程池中线程,不需要每次都创建) 便于线程管理 corePoolSize:核心池的大小 maximumPoolSize:最大线程数 keepAliveTime:线程没有任务时最多保持多长时间后会终止
1、提供指定线程数量的线程池。2、执行指定的线程的操作,需要提供实现Runnable接口或Callable接口实现类的对象。3、关闭线程池。
版权声明
本站仅做备份收录,仅供研究与教学参考之用。
读者将信息用于其他用途的,全部法律及连带责任由读者自行承担,本站不承担任何责任。









评论