JAVA学习笔记第九章
9.多线程和反射
9.1线程
9.1.1创建线程
【2】创建线程:线程类——>线程对象
方式一:继承Thread类
原理图:
运行结果:
设置线程的名字:
方式一:通过setName设置
方式二:通过构造器和super设置,实际上还是调用父类的有参构造器
例子:买车票
public class BuyTicketThread extends Thread {
public BuyTicketThread(String name){
super(name);
}
//一共10张票
static int ticketNum = 10; //多个对象共享10张票
//每个窗口都是一个线程对象:每个对象执行的代码放入run方法中
@Override
public void run() {
//每个窗口后有100个人抢票
for(int i = 1; i < 100; i++){
//对票数进行判断
if(ticketNum > 0) {
System.out.println(this.getName()+"买到第" + ticketNum-- + "张票");
}
}
}
}
public class Test {
public static void main(String[] args) {
//多个窗口抢票
BuyTicketThread t1 = new BuyTicketThread("窗口1");
t1.start();
BuyTicketThread t2 = new BuyTicketThread("窗口2");
t2.start();
BuyTicketThread t3 = new BuyTicketThread("窗口3");
t3.start();
}
}
结果:
【3】实现Runnable接口
同理,也需要实现run方法
例子:买车票
由于共享一个线程对象,TicketNum也是共享的
【4】实现Callable接口
FutureTask也实现了Runable接口
9.1.2线程的生命周期
9.1.3线程常用方法
(6)设置优先级setPriority
(7)join方法
运行结果:先执行主线程打印1-5,接着执行完子线程,再接着打印6-100
(8)sleep方法:人为的制造阻塞事件
案例:
(9)setDaemon方法:设置伴随线程
(10)stop:提前结束线程
9.1.4线程安全
【1】出现问题:
(1)出现2个或者3个第10张票
(2)出现-2, -1, 0张票
解决方法:加锁-》加同步-〉加同步监视器
加同步代码块:
多个线程必须用同一把锁才能锁住
另一种方式:但是buyTicket()锁住的是this
创建多个对象的时候this不是同一个,需要加上static关键字修饰,这样锁住的就是同步监视器 类.class
【5】Lock锁
Lock是一个接口,具有多个实现类,多态,扩展性好
使用:即使有异常也能关闭锁
线程同步的优缺点:
9.1.5生产者消费者模型
解决方式一:
加同步代码块,synchronized
解决方式二:
加同步方法,不能直接在生产者和消费者类中加同步方法,生成的两个对象使用的是不同的锁
可以在商品类中添加同步方法
线程通信的原理:
注意:wait()方法和notify()方法都需要在同步方法和同步代码块中(因为在同步的基础上线程通信才是有效的)
sleep和wait的区别:sleep进入阻塞,没有释放锁。wait进入阻塞同时释放锁。
在等待队列中可能存在多个生产者和消费者:
使用Condition中的await和signal方法
生产者:
消费者:
9.2反射
9.2.1引入
美团外卖的接口:Mtwm.java
//接口定制方:美团外卖
public interface Mtwm {
//在线支付功能
void payOnline();
}
微信、支付宝、银行卡类:
public class Wechat implements Mtwm{
@Override
public void payOnline() {
//实现微信支付的功能
System.out.println("我点外卖使用微信支付");
}
}
public class AliPay implements Mtwm{
@Override
public void payOnline() {
//实现支付宝支付
System.out.println("我点外卖用支付宝支付");
}
}
public class BankCard implements Mtwm{
@Override
public void payOnline() {
//银行卡支付
System.out.println("我点外卖用银行卡支付");
}
}
测试类
public class Test {
public static void main(String[] args) {
//定义一个字符串,模拟前台的支付方式:
String str = "支付宝";
//String str = "微信";
if ("微信".equals(str)) { //避免空指针异常str有可能为空
//微信支付
//new Wechat().payOnline();
pay(new Wechat());
}
if ("支付宝".equals(str)){
//支付宝支付
//new AliPay().payOnline();
pay(new AliPay());
}
if ("银行卡".equals(str)){
//银行卡支付
pay(new BankCard());
}
}
public static void pay(Wechat wc){
wc.payOnline();
}
public static void pay(AliPay ap){
ap.payOnline();
}
public static void pay(BankCard bc){
bc.payOnline();
}
}
为了提高代码的扩展性
但是多态的扩展性还不是最好的,更好的解决方法是反射
public class Demo {
public static void main(String[] args) throws Exception{
//定义一个字符串,模拟前台的支付方式:
String str = "test01.BankCard"; //实际上是微信类的全限定路径
Class cls = Class.forName(str);
Object o = cls.newInstance();
Method method = cls.getMethod("payOnline");
method.invoke(o);
}
}
9.2.2Class类
Class c3 = Class.forName("test01.Person");
ClassLoader loader = Test.class.getClassLoader();
Class c4 = loader.loadClass("test01.Person");
9.2.3Class类的实例的种类
9.2.4获取构造器和创建对象
指定构造器:
不填写参数,默认得到空的构造器
9.2.5获取属性
修饰符是一个具体的数字,如果需要转成字符串:
查看属性的修饰符也可以合成一句话:
给这个对象的score属性设置值为98
给属性赋值,必须要有对象
9.2.6获取方法
getMethods:获取运行时的方法和所有父类的方法(被public修饰)
9.2.7获取类的接口、包、注解
获取包名:
获取注解:
9.2.8反射面试题
程序的扩展性,需要在程序运行时才确定