目录
等待和通知机制
锁对象:
-
同步方法
-
静态方法 : 类.class
-
实例方法: this
同步代码块
写在synchronized括号中的对象
-
可以通过锁对象调用Object类的方法:
-
wait() 让当前线程等待,直到被通知
-
wait(long) 让线程等待,直到被通知或时间结束
-
notify() 随机唤醒一个线程
-
notifyAll() 唤醒所有线程
注意:如果不是锁对象调用上面的方法会出现异常IllegalMonitorStateException
/**
* 等待和通知案例
*/
public class WaitDemo {
//测试等待
public synchronized void testWait(){
for (int i = 0; i < 10; i++) {
System.out.println(Thread.currentThread().getName()+"执行了"+i);
if(i == 5){
System.out.println(Thread.currentThread().getName()+"进行等待");
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
//测试通知
public synchronized void testNotify(){
this.notifyAll();
}
public static void main(String[] args) {
WaitDemo demo = new WaitDemo();
//启动线程测试等待
for(int i = 0;i < 5;i++) {
new Thread(() -> {
demo.testWait();
}).start();
}
//5秒后主线程通知
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
demo.testNotify();
}
}
相关问题:
线程的交替输出,线程1负责输出A,线程2负责输出B,两个线程同时执行打印出ABABAB....
public class PrintDemo {
public static synchronized void printA(){
while(true){
//通知对方
PrintDemo.class.notify();
System.out.println("A");
//自己等待
try {
PrintDemo.class.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public static synchronized void printB(){
while(true){
//通知对方
PrintDemo.class.notify();
System.out.println("B");
//自己等待
try {
PrintDemo.class.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public static void main(String[] args) {
new Thread(()->printA()).start();
new Thread(()->printB()).start();
}
}
生产者消费者模式
是一种设计模式,用于解决两个点(线程、进程、服务器)之间数据通信的协调问题。
生产数据的点叫生产者,使用数据的点叫消费者,生产者和消费者可能存在速度不一致的情况。生产者速度过快,消费者消费速度慢,会浪费大量资源;反过来,消费者速度快,生产者速度慢,消费者浪费时间取等待。
过程:
-
设置缓冲区,设定临界值
-
生产者生产数据,存入缓冲区,如果缓冲区达到临界值,生产者就等待;否则就通知消费者进行消费
-
消费者从缓冲区取出数据,如果缓冲区空了,消费者等待;否则就通知生产者继续生产
解决的问题:
1. 解耦,生产者和消费者之间加入缓冲区,不需要直接调用
2. 忙闲不均,协调生产者和消费者之间的速度
3. 节约资源,减少生产者资源的浪费,和消费者等待的时间
使用synchronized实现
/**
* 包子类
*/
public class Baozi {
private int id;
public Baozi(int id) {
this.id = id;
}
@Override
public String toString() {
return "包子 id:" + id;
}
}
/**
* 包子铺
*/
public class BaoziStore {
//临界值
public static final int MAX_SIZE = 50;
//缓冲区
private List<Baozi> baozis = new ArrayList<>();
//做包子
public synchronized void makeBaozi(){
//如果缓冲区满了,生产者等待
if(baozis.size() == MAX_SIZE){
System.out.println("包子铺满了,生产者"+Thread.currentThread().getName()+"等待");
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}else{
//通知所有消费者
this.notifyAll();
}
if(baozis.size() < MAX_SIZE) {
//创建包子对象,存入缓冲区
Baozi baozi = new Baozi(baozis.size());
baozis.add(baozi);
System.out.println("生产者" + Thread.currentThread().getName() + "做了" + baozi + ",总数:" + baozis.size());
}
}
//拿包子
public synchronized void takeBaozi(){
//如果缓冲区空了,消费者等待
if(baozis.size() == 0){
System.out.println("包子铺空了,消费者"+Thread.currentThread().getName()+"等待");
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}else{
//通知生产者生产
this.notifyAll();
}
if(baozis.size() > 0){
//从缓冲区拿出一个包子
Baozi baozi = baozis.remove(0);
System.out.println("消费者" + Thread.currentThread().getName() + "拿了" + baozi + ",总数:" + baozis.size());
}
}
public static void main(String[] args) {
BaoziStore store = new BaoziStore();
new Thread(()->{
for (int i = 0; i < 100; i++) {
store.makeBaozi();
}
}).start();
for (int i = 0; i < 10; i++) {
new Thread(()->{
for (int j = 0; j < 10; j++) {
store.takeBaozi();
}
}).start();
}
}
}
使用Lock实现
Lock的等待和通知
Condition 类可以提供锁对象类似的方法
创建:
Condition condition = lock.newCondition();
等待和通知:
await() 让线程等待
signal() 通知线程
signalAll() 通知所有线程
/**
* 包子铺
*/
public class BaoziStore2 {
//创建锁对象
private Lock lock = new ReentrantLock();
//获得Condition对象
private Condition condition = lock.newCondition();
//临界值
public static final int MAX_SIZE = 50;
//缓冲区
private List<Baozi> baozis = new ArrayList<>();
//做包子
public void makeBaozi(){
try {
//上锁
lock.lock();
//如果缓冲区满了,生产者等待
if (baozis.size() == MAX_SIZE) {
System.out.println("包子铺满了,生产者" + Thread.currentThread().getName() + "等待");
try {
condition.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
} else {
//通知所有消费者
condition.signalAll();
}
if (baozis.size() < MAX_SIZE) {
//创建包子对象,存入缓冲区
Baozi baozi = new Baozi(baozis.size());
baozis.add(baozi);
System.out.println("生产者" + Thread.currentThread().getName() + "做了" + baozi + ",总数:" + baozis.size());
}
}finally {
//释放锁
lock.unlock();
}
}
//拿包子
public void takeBaozi(){
try {
lock.lock();
//如果缓冲区空了,消费者等待
if (baozis.size() == 0) {
System.out.println("包子铺空了,消费者" + Thread.currentThread().getName() + "等待");
try {
condition.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
} else {
//通知生产者生产
condition.signalAll();
}
if (baozis.size() > 0) {
//从缓冲区拿出一个包子
Baozi baozi = baozis.remove(0);
System.out.println("消费者" + Thread.currentThread().getName() + "拿了" + baozi + ",总数:" + baozis.size());
}
}finally {
lock.unlock();
}
}
public static void main(String[] args) {
BaoziStore2 store = new BaoziStore2();
new Thread(()->{
for (int i = 0; i < 100; i++) {
store.makeBaozi();
}
}).start();
for (int i = 0; i < 10; i++) {
new Thread(()->{
for (int j = 0; j < 10; j++) {
store.takeBaozi();
}
}).start();
}
}
}
阻塞队列
是一种特殊的集合,会自动阻塞对队列添加或删除数据的线程,也可以自动通知
BlockingQueue接口
put(T) 添加数据到队尾,到临界值阻塞
T take 从队头取出并删除数据,到空时阻塞
常用实现类:
ArrayBlockingQueue 数组结构的阻塞队列
LinkedBlockingQueue 链表结构的阻塞队列
SynchronousQueue 同步机制的队列
//创建阻塞队列 临界值为5
BlockingQueue<Integer> queue = new ArrayBlockingQueue<>(5);
new Thread(()->{
for(int i = 0;i < 100;i++){
System.out.println(Thread.currentThread().getName()+"添加"+i+",size:" + queue.size());
try {
queue.put(i);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}).start();
new Thread(()->{
for(int i = 0;i < 100;i++){
try {
Integer data = queue.take();
System.out.println(Thread.currentThread().getName()+"取出"+data+",size:" + queue.size());
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}).start();
使用阻塞队列实现
/**
* 包子铺
*/
public class BaoziStore3 {
//临界值
public static final int MAX_SIZE = 50;
//缓冲区 使用阻塞队列实现
private BlockingQueue<Baozi> baozis = new ArrayBlockingQueue<>(MAX_SIZE);
//做包子
public void makeBaozi(){
//创建包子对象,存入缓冲区
try {
Baozi baozi = new Baozi(baozis.size());
baozis.put(baozi);
System.out.println("生产者" + Thread.currentThread().getName() + "做了" + baozi + ",总数:" + baozis.size());
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//拿包子
public void takeBaozi(){
//从缓冲区拿出一个包子
try {
Baozi baozi = baozis.take();
System.out.println("消费者" + Thread.currentThread().getName() + "拿了" + baozi + ",总数:" + baozis.size());
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
BaoziStore3 store = new BaoziStore3();
new Thread(()->{
for (int i = 0; i < 100; i++) {
store.makeBaozi();
}
}).start();
for (int i = 0; i < 10; i++) {
new Thread(()->{
for (int j = 0; j < 10; j++) {
store.takeBaozi();
}
}).start();
}
}
}
ThreadLocal
线程局部变量,也是解决线程同步问题的一种方式
锁机制 多个线程排队访问一个资源 以时间换空间
ThreadLocal 多个线程并发访问自己独立的资源 以空间换时间
使用ThreadLocal
public class ThreadLocalDemo {
//线程共享的变量
static volatile int value = 1;
public static void main(String[] args) {
//线程本地变量
ThreadLocal<Integer> local = new ThreadLocal<Integer>(){
//设置初始值
@Override
protected Integer initialValue() {
return 1;
}
};
for(int j = 0;j < 3;j++) {
new Thread(() -> {
for (int i = 0; i < 10; i++) {
value++;
local.set(local.get() + 1);
}
System.out.println(Thread.currentThread().getName()+" value=" + value);
System.out.println(Thread.currentThread().getName()+" local=" + local.get());
}).start();
}
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+" value="+value);
System.out.println(Thread.currentThread().getName()+" local="+local.get());
}
}
ThreadLocal的原理
每个线程内部有Map集合ThreadLocalMap,获得数据时候返回当前线程的ThreadLocalMap,Map中有大量键值对Entry,键是当前的ThreadLocal对象,值是保存的数据。
public T get() {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null) {
ThreadLocalMap.Entry e = map.getEntry(this);
if (e != null) {
@SuppressWarnings("unchecked")
T result = (T)e.value;
return result;
}
}
return setInitialValue();
}
public void set(T value) {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value);
else
createMap(t, value);
}
相关问题
强软弱虚引用
强引用: 一般情况下创建的对象
Person person = new Person()
如果对象有引用指向,就不会被垃圾收集,没有引用指向就可能被垃圾收集
person = null;
软引用: SoftReference
如果即将出现OOM,gc会优先回收软引用,比强引用更容易被回收
主要用于缓存
弱引用:WeakReference
只要出现gc,都会进行回收
可以缓存临时数据
虚引用: PhantomReference
形同虚设,不能作为对象的引用使用,主要用于跟踪垃圾收集器的活动
强引用 > 软引用 > 弱引用 > 虚引用