线程安全

来源:互联网 发布:斯坦福大学 知乎 编辑:程序博客网 时间:2024/05/21 19:23

1.线程是轻量级进程,由操作系统负责调度,线程没有独立的存储空间。
2.JVM的动态内存管理:
(1)涉及到cpu,寄存器,高速缓存和内存。cpu读取数据的顺序:寄存器–>高速缓存–>内存。线程的工作内存指的是cpu的寄存器和高速缓存的抽象描述。工作内存这个概念后期会经常提到,大家注意搞清楚它的本质。
(2)Java中多个线程共享一个主内存,每个线程都有自己的工作内存,用来存储主存中某些对象的副本。
线程访问修改主内存的过程:
①从主内存中复制一个副本到当前工作内存(read and load)
②执行代码,改变共享变量值(use and assign)
③使用工作内存中的更新数据刷新主内存的值(store and write)
综上,线程主要通过操作主内存来操作数据,但不能直接引用主内存中的变量,引用的是拷贝后的副本。
3.JVM的静态内存管理:对内存的物理划分
(1)分为程序计数器、线程栈、本地方法栈、堆、方法区和常量池。
(2)线程栈是线程私有的,但堆是线程共享的,包含垃圾回收等功能的实现。
4.解决多线程冲突的办法:
(1)synchronized:主要解决java多线程的执行有序性和内存可见性。
①采用加锁的方式
②public synchronized void add(int num) 锁是add方法所在对象
③public static synchronized void add(int num)锁是add方法所在的类。
(2)volatile:解决多线程的内存可见性,对于执行有序性没有用。
①轻量级同步
②不拷贝副本到工作内存,直接在主存中修改
③ 适合场景:一个变量被多个线程共享,线程直接给这个变量赋值,相比于synchronized来说,开销较小。
5.线程同步协作问题–>
经典生产者消费者问题:仓库类,主函数,生产者线程类,消费者线程类。
(1)生产者线程类中,在run()中实现往仓库中添加
(2)消费者线程类中,在run()中实现从仓库中取出
(3)主函数中包括创建生产者和消费者的对象,给对象用setName()赋值,以及启动各个线程。
(4)仓库类中包括add()和remove()方法,满足条件则执行,不满足条件则等待。
///////示例代码如下:
store.java
//仓库类
package Reflex;
public class Store {
private final int MAX_SIZE;//定义常量最大值
private int count; //定义当前货物数量
public Store(int n) {//初始化货物最大数和初值
MAX_SIZE = n;
count = 0;
}
//添加货物的方法
public synchronized void add() {
while(count>=MAX_SIZE) {
System.out.println(“已经满了”);
try {
this.wait();//进入等待池
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
count++;//若货物不满则加入货物 System.out.println(Thread.currentThread().toString()+”put”+count);//打印当前货物总量
this.notifyAll();//用于通知处在等待状态的线程的方法。
}
public synchronized void remove() {
while(count<=0) {
System.out.println(“仓库为空”);
try {
this.wait();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
count–;
System.out.println(Thread.currentThread().toString()+”remove”+count);
this.notify();
}
public static void main(String[] args) {
Store s = new Store(5);
Thread pro = new producer(s);
Thread con = new Consumer(s);
Thread pro2 = new producer(s);
Thread con2 = new Consumer(s);
pro.setName(“producer”);
con.setName(“Consmer”);
pro2.setName(“producer1”);
con2.setName(“Consumer2”);
pro.start();
con.start();
pro2.start();
con2.start();
}
}
Consumer.java
package Reflex;
class Consumer extends Thread {
private Store s;
public Consumer(Store s) {
this.s = s;
}
public void run() {
while(true) {
try {
s.remove();
Thread.sleep(1500);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
producer.java
//生产者在添加货物之前,检查是否已满,若满则通知消费者消费,否则添加
package Reflex;
class producer extends Thread {
private Store s;
public producer(Store s) {
this.s = s;
}
public void run() {
while(true) {
s.add();
try {
Thread.sleep(1500);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
注意:notify()和notifyAll()的区别:
都用来唤醒处于等待状态的线程,前者唤醒某个线程,后者唤醒所有线程。
6.死锁:
注意:Java不提供对死锁的检测机制,语言层面上不能解决死锁问题。
分析死锁常用工具:Java thread dump
怎样判断死锁是否发生?
–>JVM处于挂起状态,虚拟机占用率为0,收集waiting for monitor entry的thread个数,当大量的thread等待给同一个地址上锁的时候,说明死锁发生了。
关于死锁产生的原因,死锁产生的必要条件,处理死锁的基本方法,预防死锁的方法,死锁的检测与解除后期会补充完整。

0 0
原创粉丝点击