多线程
基础概念
多线程:
进程:一个程序中至少一个进程,一个进程至少一个线程。
用户线程,正常情况下创建
守护线程:需要手动创建
线程是通过调用start()方法准备启动
线程调度!
线程的创建方式:两种,
1.Thread类派生,覆盖run()
2.实现Runnable接口
线程调度
什么是线程调度?线程是不可控的,那么如何干预线程的执行?
-等待:
让某一个线程进入等待状态,等到条件满足后,被其他线程唤醒
等待需要在同步环境,未持锁则报错
等待后被唤醒,需重新经过持锁状态,然后回到就绪状态,等待资源分配
-休眠:
是让当前线程进入休眠状态,这是Thread的静态方法,休眠只能自己响
1.至少消耗指定时间,(等待资源分配)
2.休眠不可以被唤醒,休眠必须有参数!
-让步:
1.不建议使用优先级、礼让!因为最终结果不可控!
2.使用,先设优先级,再启动线程
3.让步是不可控的
4.没有经过阻塞,直接回到就绪状态
-合并线程:
1.是让并行线程,变为串行状态
2.先执行,再合并
3.什么情况下要合并线程?
某个线程要等到另外一个线程结果时,需要合并,已等待前面的结果。
-守护线程:(用户线程的区别--所有用户线程执行完毕后,其他所有守护线程killed)
1.当用户线程全部执行完毕后,守护线程立即停止,无论他执行到哪儿
2.守护线程,先设置,再执行。
应用情况:定时任务;清理日志;定时同步数据
-唤醒:一个对象可以持有多个锁吗?所以:线程之间争夺锁资源唯一
线程同步
1.同步(底层看不到)
同步方法:保护整个方法当中所有数据的安全
同步代码块:保护方法当中某个区域的数据安全,意义在于让那些不需要考虑到同步的数据先执行,以提高运行效率。
一般来说:属于线程本身数据区域的数据和 读的数据
2.非静态同步,同步的锁是对象锁,只有相同实例才存在同步
3.静态同步,锁的是类,只要是这个类,就会触发同步。
4.不建议大家混杂同时使用静态和非静态锁。
容器基本都是多线程!
内部封装好的安全类:
ByteArrayInputStream---FilterInputStream
StringBuffer
Collections
Note:
--即使是线程安全的类,也应该特别注意,因为操作的线程之间仍然不一定安全。
线程安全类的安全仅仅局限于:它本身,但是其他操作不一定!
基本概念:
死锁:--解决,按照预定义的顺序获取锁。
1.两个线程互相持有对方的锁,且互相等待,这种情况下,有小概率发生死锁。
2.避免死锁的策略,就是按照相同的顺序获取锁,并释放锁。原子化:
一个整体、数据完整
--要么都成功,要么都失败。
wait()\notify()\notifyAll()三个方法:
看下面的代码,这个代码执行的话会报错,java.lang.IllegalMonitorStateException
上网查了一下,明白了。
1>当前线程不含有当前对象的锁资源的时候,调用obj.wait()方法;
2>当前线程不含有当前对象的锁资源的时候,调用obj.notify()方法。
3>当前线程不含有当前对象的锁资源的时候,调用obj.notifyAll()方法。
生产消费者模型
生产消费者模型:
1.消息队列,服务发布和服务消费。。。。
消费者:
1.先来后到的顺序
2.消费者需要的东西,多种多样
3.消费者需要的东西,可以不需要一次性全部获取
4.如果生产者的能力达到上限,那么需要等待。
生产者:
1.生产者需要尽可能按照消费的先后顺序完成生产
2.生产者还必须保证所有的生产都要完成
3.如果没有消费者的消费需求,则需要等待。//定义队列对象(+泛型)
import java.util.LinkedList;
public class MyQueue<T> {
private LinkedList<T> queue;
private final int DEFAULT_CAPACITY = 20;
private int maxSize;
public MyQueue() {
this.maxSize = DEFAULT_CAPACITY;
this.queue = new LinkedList<T>();
}
public MyQueue(int size) {
this();
this.maxSize = size;
this.queue = new LinkedList<T>();
}
public synchronized void put(T t) {
if (queue.size()>=this.maxSize) {
System.out.println("队列已满...等待");
try {
this.wait();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
queue.add(t);
//唤醒的是当前对象wait()的线程
this.notifyAll();
}
public synchronized T get() {
if (queue.size()<=0) {
System.out.println("队列为空...等待");
try {
this.wait();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
this.notifyAll();
return queue.removeLast();
}
public synchronized int getSize() {
return this.queue.size();
}
}//生产者、消费者持有同一个线程对象
//生产线程
public class ProducerThread implements Runnable{
private final MyQueue q;
private final String message;
public ProducerThread(final MyQueue q,final String message){
this.q = q;
this.message = message;
}
@Override
public void run() {
//每隔2秒生产一个
while(true){
try {
Thread.sleep(500);
q.put(Thread.currentThread()+":"+message);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
//消费者线程
public class ConsumerThread implements Runnable{
private final MyQueue q;
public ConsumerThread(final MyQueue q){
this.q = q;
}
@Override
public void run() {
// TODO Auto-generated method stub
while(true){
try {
Thread.sleep(1000);
Object obj = q.get();//消费
System.out.println(obj);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}//测试
public class Test {public static void main(String[] args) {
// TODO Auto-generated method stub
MyQueue q = new MyQueue();
//多生产者
new Thread(new ProducerThread(q, "生产者1")).start();
new Thread(new ProducerThread(q, "生产者2")).start();
new Thread(new ProducerThread(q, "生产者3")).start();
//多消费
new Thread(new ConsumerThread(q)).start();
new Thread(new ConsumerThread(q)).start();
new Thread(new ConsumerThread(q)).start();
}}
Volatile:关键字,不建议使用!
??
有限的情形下使用volatile变量替代锁。
1.对变量的写操作不依赖于当前值。
final型的变量是禁止修改的,也就不存在线程安全的问题。
//继承自父类的属性,this.属性指向super.属性,都是指向父类的同一个属性(效果等效)
反射的应用
--Java.lang.reflect.*
应用:
1.反射调用方法———将方法作为对象,而不再是类名
2.field.setAccessible(true);--暴力更改访问机制,访问private
3.constructor.newInstance()
面向对象编程
开发期间:类是构成Java程序的单位,Java程序通过类来组织其结构
运行期间:对象是Java程序在运行时的单位,运行期间的Java程序是通过多个对象之间的相互作用关系来完成需求,动态。
null:空指针,内存地址的一个值,指空,不存在。
垃圾回收机制GC:是一个独立的巡查线程,GC是一个定时任务,隔一段时间执行一次。基本数据类型在栈内存中开辟空间,同时直接将具体数值(而不是地址)保存在栈中
--int numb = null;报错
引用类型在栈中开辟空间,同时在堆中开辟空间,创建对象,将对象地址保存在栈中。
内存模型,原理?