【Java并发编程的艺术学习】第二章摘要补全
1.并发编程
多线程情况下,一个线程需要读取到其他线程写后内容再开始操作。
2.如何保证写后读
2.1加锁
? ? ? ? 当前线程对资源进行加锁,在此时其他线程相对该资源操作的话是无法上锁的,所以无法对该资源进行任何操作。
在该线程写完之前不可以释放锁
如果一个方法加锁,那么不管什么线程想拷贝该方法都要事先对该方法加锁。
如果想进行加锁的话需要对读和写操作同时进行加锁,分开加锁的话可能没有执行完毕时间片就到了,这样是没有任何意义的。
public class Test {
public static int a;
public static void main(String[] args) throws InterruptedException {
Thread t1=new Thread() {
public void run() {
getA();
}
};
t1.start();
t1.join();
}
public synchronized static int getA() {
return a; //读
}
public synchronized static void setA(int x) {
a = x; //写
}
public synchronized static void m1(int a) {
a++; //int k=a+1; a=k; 读+写
}
}
2.2不加锁的方式:先比较并且交换(CAS)
2.1volatile的应用
1.volatile保证可见性,什么是可见性
可见性是当一个线程修改一个共享变量,另一个线程能读到这个修改的值。
2.volatile能保证一定准确吗?
不一定。volatile只能保证任意时刻读取
比如有两个线程,线程1在t1时刻读取到值为20,恰好在读取后的下一瞬间t2线程2对该资源写回变为1000。在这个过程中,volatile只能保证在t1的一瞬间是正确的,在t2时刻我们在t1时刻读取到的值和t2并不同,所以说一定准确这个说法是不对的。
3.那我们如果不加volatile和加volatile还有什么区别,不加的话我们难道读取就是错的吗?
对于我们的代码来说,比如123456,在我们认知中通常执行顺序是123456,但是实际上并不是,在计算机内部会采用流水线执行,采用流水线执行的话会导致指令重排序,那么一些已经写完的读不到,就会导致计算错误,所以我们需要保证多线程下的可见性。
流水线:每个环节只做一件事
比如制造望远镜,每个人做一个那么需要完成所有,可能一个人完成一天完成一个,那么一百个人一天内最多完成一百个,效率很低下,但是如果每个人只需要完成一个部件,那么效率就会大大提升,所以计算机内部也采用流水线方式执行
4.除了volatile还有什么能保证线程的可见性?
final:防止指令重排序的作用,保证多线程的可见性。
2.2synchronized的实现原理和应用(重点)
1.Synchonized原理(锁的实现原理)?(我讲不清楚,就直接贴图了)
2.对于synchronized的使用
public class Test02 {
public synchronized void m1() throws InterruptedException{
System.out.println("开始执行m1");
Thread.sleep(3000);
System.out.println("结束执行m1");
}
public synchronized void m2() throws InterruptedException{
System.out.println("开始执行m2");
Thread.sleep(3000);
System.out.println("结束执行m2");
}
public synchronized static void m3() throws InterruptedException{
System.out.println("开始执行m3");
Thread.sleep(3000);
System.out.println("结束执行m3");
}
public synchronized static void m4() throws InterruptedException{
System.out.println("开始执行m4");
Thread.sleep(3000);
System.out.println("结束执行m4");
}
public void m5() throws InterruptedException{
System.out.println("开始执行m5");
Thread.sleep(3000);
System.out.println("结束执行m5");
}
public static void m6() throws InterruptedException{
System.out.println("开始执行m6");
Thread.sleep(3000);
System.out.println("结束执行m6");
}
}
public class Test0201 {
public static void main(String[] args) throws InterruptedException {
final Test02 x=new Test02();
final Test02 y=new Test02();
Thread t1=new Thread() {
public void run() {
try {
x.m1();//更改部分
}catch(InterruptedException e){
e.printStackTrace();
}
}
};
Thread t2=new Thread() {
public void run() {
try {
y.m2();//更改部分
}catch(InterruptedException e){
e.printStackTrace();
}
}
};
t1.start();
t2.start();
}
}
1.类锁:锁住类
动态方法需要通过类来调用,通过运行
可以看到,上锁部分为整个类
2.方法锁
在这里对比可以看到,对于静态方法,锁住的为方法区,(图四4比3先执行是因为对于操作系统来说,先进入就绪队列代表大概率先被选中执行,不是一定先执行)
3.对于类锁来说,锁住该类并不影响无锁的方法执行
4.对于方法区来说同样
建议自己运行一下这部分代码,言语逻辑表述不是很清晰。
3.概念补充:
HotSpot:JAVA运行区域
栈帧:方法
4.偏向锁
5.轻量级锁
6.重量级锁
2.3原子操作的实现原理
1.CAS方法不唯一,在Java中很多地方都封装了CAS
2.什么是线程安全类?
3.CAS原子操作三大问题
4.ABA问题示意图
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。 如若内容造成侵权/违法违规/事实不符,请联系我的编程经验分享网邮箱:veading@qq.com进行投诉反馈,一经查实,立即删除!