静态代理和动态代理
代理模式可以在不修改被代理对象的基础上,通过扩展代理类,进行一些功能的附加与增强。
静态代理
代理类和被代理类应该共同实现一个接口,或者是共同继承某个类。
优点:
可以在不修改目标对象的前提下扩展目标对象的功能。
缺点:
冗余:由于代理对象要实现与目标对象一致的接口,会产生过多的代理类。
不易维护:一旦接口增加方法,目标对象与代理对象都要进行修改。
public static void main(String[] args) {
//UserService userService = new UserServiceImpl;
UserService userService = new UserServiceImplProxy;
userService.findName();
}
interface UserService{
public abstract void findName();
}
class UserServiceImpl implements UserService{
@Override
public void findName(){
System.out.print("正在查询~~~");
}
}
class UserServiceImplProxy implements UserService{
private UserServiceImpl userServiceImpl = new UserServiceImpl();
@Override
public void findName(){
System.out.print("我是增强方法~~~~~");
userServiceImpl.findName();
}
}
动态代理
动态代理利用了 JDK API,动态地在内存中构建代理对象,从而实现对目标对象的代理功能。
动态代理又被称为 JDK 代理或接口代理。
public static void main(String[] args) {
UserService userService = new UserServiceImpl();
UserServiceImplProxy_demo userServiceImplProxy_demo = new UserServiceImplProxy_demo();
//get(XXX)方法,XXX表示需要代理的对象
UserService userServiceProxy = (UserService)userServiceImplProxy_demo.get(userService);
userServiceProxy.findName();
}
interface UserService{
public abstract void findName();
}
class UserServiceImpl implements UserService{
@Override
public void findName(){
System.out.print("正在查询~~~");
}
}
class UserServiceImplProxy_demo implements InvocationHandler{
//写代码的时候不知道需要代理哪个对象
private Object object;
//获取原始对象,产生代理对象
public void get(Object o){
this.object = o;
//获取类加载器和接口
return Proxy.newProxyInstance(this.object.getClass().getClassLoader(),this.object.getClass().geiInterfaces(),this)
}
//静态代理产生对象:
//UserService userService = new UserServiceImplProxy;
System.out.print("我是增强方法2~~~~~");
@Override
public Object invoke(Object Proxy,Method method,Object[] args) throws Throwable{
/*
Proxy:对象
method:代理的对象的方法
args:参数
*/
return method.invoke(this.object,args);
}
}
静态代理与动态代理的区别主要在:
静态代理在编译时就已经实现,编译完成后代理类是一个实际的 class 文件
动态代理是在运行时动态生成的,即编译完成后没有实际的 class 文件,而是在运行时动态生成类字节码,并加载到 JVM 中
注意:
动态代理对象不需要实现接口,但是要求目标对象必须实现接口,否则不能使用动态代理。
JDK 中生成代理对象主要涉及两个类:
第一个类为 java.lang.reflect.Proxy,通过静态方法 newProxyInstance 生成代理对象,
第二个类为 java.lang.reflect.InvocationHandler 接口,通过 invoke 方法对业务进行增强
线程基本使用:
多线程执行:
例:
编写一个程序,创建两个线程,
一个线程每隔1秒输出”hello world”,输出10次退出;
一个线程每隔1秒输出”hi”,输出5次退出
public static void main(String[] args) {
T1 t1 = new T1();
T2 t2 = new T2();
Thread thread1 = new Thread(t1);
Thread thread2 = new Thread(t2);
thread1.start(); //启动第1个线程
thread2.start(); //启动第2个线程
}
class T1 implements Runnable{
int count = 0;
@Override
public void run(){
while(true){
System.out.print("hello world");
//try-catch
Thread.sleep(1000);
//try-catch
if (count == 10) {
break;
}
}
}
}
class T2 implements Runnable{
int count = 0;
@Override
public void run(){
while(true){
System.out.print("hi~~~~~");
//try-catch
Thread.sleep(1000);
//try-catch
if (count == 5) {
break;
}
}
}
}
继承Thread VS 实现Runnable的区别:
- 从java的设计来看,通过继承Thread或者实现Runnable接口来创建线程本质上没有区别,从jdk帮助文档我们可以看到Thread类本身就实现了Runnable接口
- 实现Runnable接口方式更加适合多个线程共享一个资源的情况,并且避免了单继承的限制
如:
T3 t3 = new T3("hello");
//两个线程共享t3
Thread thread01 = new Thread(t3);
Thread thread02 = new Thread(t3);
thread01.start();
thread02.start();
例:
编程模拟三个售票窗口售票100张;分别使用继承Thread 和 实现Runnable接口 方式
-------------------------------------------
public static void main(String[] args) {
SellTicket01 sellTicket01 = new SellTicket01();
SellTicket01 sellTicket02 = new SellTicket01();
SellTicket01 sellTicket03 = new SellTicket01();
//会出现超卖现象
sellTicket01.start(); //启动售票线程
sellTicket02.start(); //启动售票线程
sellTicket03.start(); //启动售票线程
}
//使用Thread方式
class SellTicket01 extends Thread{
private static int ticketNum = 100; //让多个线程共享ticketNum
@Override
public void run(){
while(true){
if (ticketNum <= 0) {
System.out.print("售票结束~~");
}
//休眠50毫秒
try{
Thread.sleep(50);
}catch(InterruptedException e){
e.printStackTrace();
}
System.out.print("窗口" + Thread.currentThread().getName()
+ "售出了一张票" + "剩余票数" + (--ticketNum));
}
}
}
-------------------------------------------
public static void main(String[] args) {
SellTicket02 sellTicket02 = new SellTicket02();
//也会出现超卖现象
new Thread(sellTicket02).start(); //第1个线程
new Thread(sellTicket02).start(); //第2个线程
new Thread(sellTicket02).start(); //第3个线程
}
//实现接口的方式
class SellTicket02 implements Runnable{
private int ticketNum = 100; //让多个线程共享ticketNum
@Override
public void run(){
while(true){
if (ticketNum <= 0) {
System.out.print("售票结束~~");
}
//休眠50毫秒
try{
Thread.sleep(50);
}catch(InterruptedException e){
e.printStackTrace();
}
System.out.print("窗口" + Thread.currentThread().getName()
+ "售出了一张票" + "剩余票数" + (--ticketNum));
}
}
}
-------------------------------------------
线程终止:
基本说明:
- 当线程完成任务后,会自动退出
- 还可以通过使用变量来控制run方法退出的方式停止线程,即通知方式
public static void main(String[] args) {
T t1 = new T();
t1.start();//如果希望main线程去控制t1,线程终止,必须可以修改loop //让t1退出run方法,从而终止 t1线程 -> 通知方式 //让主线程休眠10秒,再通知t1线程退出 Thread.sleep(10*1000) t1.setLoop(false);
}
class T extends Thread{
private int count = 0;
//设置一个控制变量
private boolean loop = true;
@Override
public void run(){
whlie(loop){
try{
Thread.sleep(50);
}catch (InterruptedException e) {
e.printStackTrace();
}
System.out.print(“T 运行中~~~” + (++count));
}
}
public void setLoop(boolean loop){
this.loop = loop;
}
}
线程常用方法:
第一组:
- setName //设置线程名称,使之与参数name相同
- getName //返回该线程的名称
- start //使该线程开始执行;java虚拟机底层调用该线程的start0方法
- run //调用线程对象run方法
- setPriority //更改线程的优先级
- getPriority //获取线程的优先级
- sleep //在指定的毫秒数内让当前正在执行的线程休眠(暂停执行)
- interrupt //中断线程(不是终止)
注意事项:
- start底层会创建新的线程,调用run,run就是一个简单的方法调用,不会启动新线程
- 线程优先级的范围
- interrupt,中断线程,但并没有真正的结束线程。所以一般用于中断正在休眠线程
- sleep:线程的静态方法,使当前线程休眠
例:
public static void main(String[] args) {
/*
主线程吃了100个包子,开始休眠,然后执行到t.interrupt(),中断休眠,再吃100个包子
*/
T t = new T();
t.setName("线程")
/*
优先级:
MAX_PRIORITY 10
MIN_PRIORITY 1
NORM_PRIORITY 5
*/
t.setPriority(Thread.MIN_PRIORITY)
t.start(); //启动子线程
//主线程打印5个hi,然后中断 子线程休眠
for (int i = 0; i < 5; i++) {
Thread.sleep(1000);
System.out.print("hi " + i);
}
t.interrupt(); //当执行到这里,就会中断,t线程的休眠
}
class ThreadDemo1 extends Thread{
@Override
public void run(){
whlie(true){
for (int i = 0; i <100; i++) {
//Thread.currentThread().getName() 获取当前线程名称
System.out.print(Thread.currentThread().getName() + "吃包子~~~~~~");
}
try{
System.out.print(Thread.currentThread.getName() + "休眠中~~~~~~~~");
Thread.sleep(20*1000);
}catch(InterruptedException e){
//当该线程执行到一个interrupt 方法时,就会catch一个异常,可以加入自己的业务代码
//InterruptedException 捕获一个中断异常
System.out.print(Thread.currentThread().getName() + "被 interrupt了");
}
}
}
}
第二组:
- yield:线程的礼让。让出cpu,让其他线程执行,但礼让的时间不确定,所以也不一定礼让成功
- join:线程的插队。插队的线程一旦插队成功,则肯定先执行完插入的线程所有的任务
例:
public static void main(String[] args) {
T2 t2 = new T2();
t2.start();
for (int i = 1; i <=20; i++) {
Thread.sleep(1000);
System.out.print("主线程(小弟) 吃了 " + i + "包子");
if (i == 5) {
System.out.print("主线程(小弟) 让 子线程(老大) 先吃");
t2.join(); //这里相当于让t2线程先执行完毕
System.out.print("子线程(老大) 吃完了,主线程(小弟)接着吃");
}
}
}
class T2 extends Thread{
@Override
public void run(){
for (int i = 1; i <= 20; i++) {
try{
Thread.sleep(1000);
}catch(InterruptedException){
e.printStackTrace();
}
System.out.print("子线程(老大) 吃了 " + i + "包子");
}
}
}
练习:
- 主线程每隔1s,输出hi,一共10次
- 当输出到 hi5时,启动一个子线程(要求实现Runnable),每隔1s输出hello,等该线程输出10次hello后,退出
-
主线程继续输出hi,直到主线程退出
public static void main(String[] args) { Thread t3 = new Thread(new T3()); //创建子线程 for (int i =1; i <= 10; i++) { System.out.print("hi " + i); if (i == 5) { //说明主线程输出了5次hi t3.start(); //启动子线程,输出 hello t3.join(); //立即将t3子线程插入main,让t3先执行 } } } class T3 implements Runnable{ int count = 0; @Override public void run(){ while (true) { System.out.print("hello" + (++count)); try{ Thread.sleep(1000); }catch(InterruptedException e){ e.printStackTrace(); } if (count == 10) { break; } } } }