JavaSE_知识点(下2)(多线程,反射)

Java——多线程,反射

3.多线程

(1).概述:

进程】:一个应用程序,一个进程可启动多个线程;
线程】:一个进程中的执行场景/单元。
多线程的作用和特点:
不同线程的栈内存独立,堆和方法区共享,多线程并发可提高程序处理效率。

(2).线程的生命周期:

在这里插入图片描述

(3).多线程的实现方法:

<1>.Thread类详解:

所属包名:java.lang.Thread
常用方法:


setName()方法:

/*
*修改线程的名字
*@param name:线程的新名字
*/
public final synchronized void setName(String name);

getName()方法:

/*
*获取线程的名字
*@return:线程的名字
*/
public final String getName();

interrupt()方法:

/*
*利用异常处理机制终止睡眠
*/
public void interrupt();

currentThread()方法:

/*
*获取当前线程
*return:当前线程
*/
public static native Thread currentThread();

sleep()方法:

/*
*使当前线程进入休眠,出现在哪个线程,使哪个线程进入休眠状态
*@param millis:休眠的毫秒数
*/
public static native void sleep(long millis);
<2>.继承实现多线程:

①编写一个类,使其继承java.lang.Thread类;
②重写run()方法;
③.启动线程:调用对象的【start()方法】(本质是在JVM中开辟新的栈空间)。

<3>.接口实现多线程:

①编写一个类,使其实现java.lang.Runnable接口;
②实现run()方法;
③创建线程对象,传入接口实现类;
④调用线程对象的【start()方法】。

补充对run()方法的理解:run()方法在分支线程中等同于主线程的main()

(4).账户存取款实战:

业务需求:写一个程序,要求使用多线程操控一个账户,同时进行取钱操作。寻找安全隐患问题。

源码展示:
Account账户类:

package Thread222;

public class Account {
    private String name;
    private double balance;

    public Account(){}
    public Account(String name,double balance){
        this.name = name;
        this.balance = balance;
    }

    //存钱方法
    public void deposit(double money){
        setBalance(balance+money);
        System.out.println("存钱成功,当前余额:$"+balance);
    }
    //取钱方法
    public void withdraw(double money){
        if (balance < money){
            System.out.println("取钱失败,余额仅剩"+balance);
            return;
        }
        setBalance(balance-money);
        System.out.println("取钱成功,当前余额:$"+balance);
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public double getBalance() {
        return balance;
    }

    public void setBalance(double balance) {
        this.balance = balance;
    }

    @Override
    public String toString() {
        return "Account{" +
                "name='" + name + '\'' +
                ", balance=" + balance +
                '}';
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Account account = (Account) o;
        return Double.compare(account.balance, balance) == 0 &&
                name.equals(account.name);
    }
}

主类以及线程类:

package Thread222;

public class Main {
    public static void main(String[] args) {
        Account act = new Account("QAQ",50000);
        Thread t1 = new Thread(new Thread111(act));
        Thread t2 = new Thread(new Thread222(act));
        Thread t3 = new Thread(new Thread333(act));
		//启动线程
        t1.start();
        t2.start();
        t3.start();
    }
}
class Thread111 implements Runnable{
    Account act;
    public Thread111(Account act){
        this.act = act;
    }
    @Override
    public void run() {
        //同时取钱:
        act.withdraw(5000);
    }
}

class Thread222 implements Runnable{
    Account act;
    public Thread222(Account act){
        this.act = act;
    }
    @Override
    public void run() {
        //同时取钱:
        act.withdraw(5000);
    }
}

class Thread333 implements Runnable{
    Account act;
    public Thread333(Account act){
        this.act = act;
    }
    @Override
    public void run() {
        //同时取钱:
        act.withdraw(5000);
    }
}

(5).线程安全:

<1>产生线程安全问题的情况:

通过上述程序可知,每个线程内部的执行顺序不确定,由此会产生线程安全问题。
总而言之,以下情况会产生线程安全问题:
Ⅰ.多线程并发程序;
Ⅱ.存在共享数据,比如上述的余额;
Ⅲ.共享数据有修改的行为,比如上述案例的取钱操作。


<2>.解决方案(重点):线程同步机制

synchronized关键字:
用法①:
synchronized(同步共享对象){同步代码块}
同步共享对象处可填:this,Object类,字符串
案例:

public void withdraw(double withdrawMoney){
    synchronized (this){
      	if (withdrawMoney>=0){
            if (getBalance()<withdrawMoney){
                System.out.println("取钱失败,余额仅剩"+getBalance()+"。");
            }else{
                setBalance(getBalance()-withdrawMoney);
                System.out.println("取钱成功,余额"+selectBalance());
            }
            }else{
                System.out.println("请输入有效的数字!");
            }
        }
    }

用法②:
使用在实例方法上,但这种方法使其共享对象仅能是【this】,且可能无故扩大作用域,效率较低,这种方式叫作添加【对象锁】。例如:

public synchronized void withdraw(double withdrawMoney){
    if (withdrawMoney>=0){
         if (getBalance()<withdrawMoney){
             System.out.println("取钱失败,余额仅剩"+getBalance()+"。");
         }else{
              setBalance(getBalance()-withdrawMoney);
              System.out.println("取钱成功,余额"+selectBalance());
          }
         }else{
             System.out.println("请输入有效的数字!");
         }
    }
}

用法③:
修饰静态方法时叫作【类锁】。
类锁】:一百个同类型对象一把类锁。
对象锁】:一个对象一把对象锁。
死锁】:无法解开的锁叫作死锁,比如两个锁方法互相或者嵌套调用。

高效解决线程安全问题的方法:
Ⅰ.使用局部变量(存在栈中)替代成员变量;(推荐)
Ⅱ.创建多个对象,使线程和对象一一对应。
Ⅲ.使用【synchronized】关键字。

(6).守护线程(后台线程):

实例:垃圾回收机制;
特点:.
①.守护线程是一个死循环;
②.所有用户线程结束后,守护线程自动结束;
③.将用户线程转换为守护线程的方法(Thread下的方法):

public final void setDaemon(boolean on);

(7).生产者模式与消费者模式:

<1>.Object类下的两个方法介绍:

①wait():

/*
* 让正在对象上活动的线程无限期等待
* 【释放之前占用的对象锁】
*/
public final void wait();

②notify()与notifyAll:

/*
* 唤醒正在对象上等待的线程
* 该方法不会释放锁
*/
public final native void notify();
<2>.业务实战:

业务需求: 巧妙地利用wait()方法与notify()方法对线程进行生产者与消费者的平衡。
源码展示:

package 多线程;

import java.util.ArrayList;
import java.util.List;

/*
* 测试生产者模式与消费者模式:
*       巧妙地利用wait()方法与notify()方法对线程进行
*       生产者与消费者的平衡。
*       wait()方法释放对象锁,让线程进行等待;
*       notify()方法不会释放锁,仅仅告知唤醒被wait的对象。
* */
public class Producer_and_Consumer {
    public static void main(String[] args) {
        //创建仓库对象:
        List list = new ArrayList();
        //创建线程对象:
        Thread t1 = new Thread(new Producer(list));
        Thread t2 = new Thread(new Consumer(list));
        //设置线程名字:
        t1.setName("生产者线程");
        t2.setName("消费者线程");
        //启动线程:
        t1.start();
        t2.start();;
    }
}

//生产者线程:
class Producer implements Runnable{
    // 传入共享对象仓库List集合:
    List list;
    public Producer(List list){
        this.list = list;
    }
    @Override
    public void run() {
        while(true){
            synchronized(list) {
                if (list.size() > 0) {
                    try {
                        list.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                //程序走到这里代表集合中无元素,需要“生产”:
                Object o = new Object();
                list.add(o);
                System.out.println(Thread.currentThread().getName() + "--->" + o);
                list.notify();
            }
        }
    }
}
//消费者线程:
class Consumer implements Runnable{
    //传入共享对象仓库List集合:
    List list;
    //构造方法:
    public Consumer(List list){
        this.list = list;
    }
    @Override
    public void run() {
        while(true){
            synchronized(list){
                if (list.size() == 0){
                    try {
                        list.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                //如果程序能走到这里,说明集合需要消费:
                Object o = list.remove(0);
                System.out.println(Thread.currentThread().getName()+"--->"+o);
                list.notify();
            }
        }
    }
}

4.反射(java.lang.reflect.*)

(1).定义与作用:

定义:
反射就是把java类中的各种成分映射成一个个的Java对象。
例如:一个类有:成员变量、方法、构造方法、包等等信息,利用反射技术可以对一个类进行解剖,把个个组成部分映射成一个个对象。

作用:
通过反射可以操作字节码文件(class文件)。

(2).反射的常用功能:

<1>.获取某类的字节码:

方法①:
该方法会导致类加载,静态代码块会执行。

//该方法会导致类加载,静态代码块会执行。
Class c = Class.forName("(完整类名带包名)");

方法②:

Class c = (引用).getClass();

方法③:

Class c = (任何类型).class;

上述三种方式中:
①,③两种方式使用类名反射;
②方式使用对象反射;

<2>.通过反射机制实例化对象:

注:这种方式创建对象仅能调用其无参构造。

//注:这种方式创建对象仅能调用其无参构造。
Class c = Class.forName("Student");
Student s = (Student)c.newInstance();
<3>.获取文件的绝对路径:
String path = 
Thread.currentThread().getContextClassLoader().
getResource("全限定名称路径").getPath();
<4>.资源绑定器:

所在包:java.util.ResourceBundle

 public static final ResourceBundle getBundle(String baseName)

实例:
存在属性配置文件db.properties(src下):

id=001
name=guan
age=20
ResourceBundle bundle = ResourceBundle.getBundle("db");
String id = bundle.getString("id");
String name = bundle.getString("name");
String age = bundle.getString("age");

(3).反射应用(java.lang.reflect.*):

<1>.反射属性(~.Field):

常用方法:
Class类下:

getFields()方法:
只能拿到public修饰的属性

//拿到反射类对象的以public修饰的属性
public Field[] getFields();

getDeclaredFields():

//拿到反射类对象的所有属性
public Field[] getDeclaredFields();

Field类下:

getName():

//拿到属性的名字
public String getName();

getType():

//拿到属性的类型
public String getType();

set():

//给Object的对象赋值
public void set(Object o,Object value);

get():

//获取该属性的值
public Object get(Object obj);
<2>.反射方法(~.Method):

常用方法:
getReturnType():

//获取返回值类型
public Class<> getReturnType();

getModifiers():

//获取修饰符列表
Modifier.toString(method.getModifiers);
//获取返回值类型字符串
String returnType = method.getReturnType().getSimpleName();

getName():

//获取方法名
String name = method.getName();

getParameterTypes():

//获取方法的参数列表
public Class[] getParameterTypes();

//获取方法
getDeclaredMethod(…);
//调用方法:

public Object invoke(Object obj,参数列表);
<3>.反射类的父类和接口
//获取父类
public Class getSuperClass();
//获取父接口
public Class[] getInterfaces();

5.注解(annotation)

(1).概述:

①.注解是一种引用数据类型,编译后也生产class文件。
②.定义注解的方法:[修饰符列表]@interface 注解类型名{ ... }
③.注解可修饰属性,方法,变量,枚举,注解等…
④.注解使用时的语法格式:@注解类型名(@override)

(2).两种常见注解:

@override:
作用:编译器检查编译,自动检查方法是否重写父类方法,若没有重写,则报错。
用来注解的注解叫作【元注解】。

@Deprecated:
作用:所标注的元素已过时。

(3).注解的其他用法:

①.注解自定义可定义属性,且当有属性时,必须给属性赋值;
例如:@......(属性名=属性值)
属性声明时格式为:数据类型+属性名();
例如:String name()
②.如果注解中属性名为value时并且只有这一个属性时,value可省略。

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值