day30_学习总结
多线程实现的两种方式
第一种实现方式:通过继承Thread实现
步骤:
定义继承Thread的子类
重写run()方法
创建线程
启动线程
第二种实现方式:通过实现Runnable接口
步骤:
定义实现Runnable接口的子实现类
重写run()方法
创建资源类对象
创建线程
启动线程
方式二中的线程操作的是同一个资源类,资源类是共享的,更符合多线程的理念
静态代理
通过代理角色来完成真是角色的功能
代理角色
目标角色:真实角色
静态代理的特点:代理角色和目标角色都必须实现同一个接口
eg:多线程通过Runnable接口实现多线程的方式就是静态代理
Thread类为代理角色,实现Runnable接口的子实现类是目标角色
代理角色中要定义实现接口的对象或者真实角色对象
代理角色中需要有有参构造方法,参数为接口或者实现接口的类(真实角色对象)
同步机制—解决多线程安全问题
同步代码块
格式:
synchronized(任意类对象){
对共享数据的操作;
}
同步方法:将对共享数据的操作定义为一个方法,并且方法声明上通过synchronized修饰
权限修饰符 synchronized 返回值类型 方法名(){}
静态同步方法:
权限修饰符 static synchronized 返回值类型 方法名(){}
对于同步方法,其锁对象是this,当前类对象的地址值引用
对于静态同步方法,锁对象是类字节码文件对象,为类名.class
JDK5以后提供接口Lock,也能够实现锁定操作,比synchronized方法更广泛
通过继承Lock接口的子实现类ReentrantLock创建锁对象
void lock()
对象名.lock();//获取锁
void.unlock()
对象名.unlock();//释放锁
注意:
对于Lock这种锁操作,可以配合try...finally...使用,通过finally释放资源
try{
对象名.lock();//获取锁
对共享资源的操作
}finally{
if(对象名!=null){
对象名.unlock();
}
}
判断多线程安全的标准?
1>是否为多线程环境
2>是否存在共享数据
3>是否有多条语句对共享数据进行操作
死锁现象
多个线程操作不同的资源对象,使用同步代码块之后,造成多个线程之间相互等待的现象
生产者消费者模式–等待唤醒机制
生产者:不断生成数据
消费者:不断消耗数据
操作的是同一个资源对象
通过Object类中的wait()方法和notify()方法
例如:以下学生,不断定义对象,不断输出对象
public class Student {
private String name ;
private int age ;
public boolean flag ;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public boolean isFlag() {
return flag;
}
public void setFlag(boolean flag) {
this.flag = flag;
}
}
public class SetThread implements Runnable {
private Student s ;
public SetThread(Student s){
this.s = s ;
}
private int x = 0 ;
@Override
public void run() {
while(true){
synchronized (s){
if(s.flag){
try {
s.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
if(x % 2 == 0){
s.setName("路飞");
s.setAge(19);
}else{
s.setName("索隆");
s.setAge(18);
}
s.flag = true ;
s.notify();
}
x++;
}
}
}
public class GetThread implements Runnable{
private Student s ;
public GetThread(Student s){
this.s = s ;
}
@Override
public void run() {
synchronized (s) {
while (true) {
if(!s.flag){
try {
s.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println(s.getName() + "---" + s.getAge());
s.flag = false ;
s.notify();
}
}
}
}
public class StudentDemo {
public static void main(String[] args) {
Student s = new Student() ;
SetThread st = new SetThread(s) ;
GetThread gt = new GetThread(s) ;
Thread t1 = new Thread(st) ;
Thread t2 = new Thread(gt) ;
t1.start();
t2.start();
}
}
面试题
wait()和notify()为什么不定义在Thread类中,而定义在Object类中?
wait()和notify()方法都和监视锁有关系,通锁对象有关,而锁对象可以为任意Java类对象,因此,定义在Object类中,任意类对象都可以调用这两个方法
sleep()和wait()方法区别?
不同点:
来源不同:
sleep()定义在Thread类中
wait()定义在Object类中
是否会释放锁:
sleep()方法不会释放锁
wait()方法在调用时会释放锁,然后通过notify()方法唤醒其他线程
相同点:都存在异常,需要对异常进行处理
InterruptedException中断异常