《java多线程编程核心技术》第二章笔记

本章通过synchronized和volatile关键字的学习,掌握线程同步的编程

脏读的例子:A对公共资源obj进行写,obj=1, 然后A被中断;接着B对公共资源obj写,obj = 2,然后B被中断。 此时A恢复,接着读obj的数据,读到的是错误的数据。

synchronized

下面都用sync来代替

1、sync方法

1.1、sync public void fun();

对方法进行加锁,等价于对其对应的类对象进行加锁,因此假设多个线程共享一个类对象,那么就生成一个锁。 锁的个数和类对象的个数一致。

1.2、sync里可以调用对象内的另一个sync,也可以调用父类的sync

1.3、sync继承重写之后,失效。不重写的话依然可以同步

import javax.security.auth.Subject;
import java.util.*;

class fa{
    synchronized public void methodA( ) throws InterruptedException {

        System.out.println("begin time: " + System.currentTimeMillis());
        System.out.println(Thread.currentThread().getName());
        Thread.sleep(5000);
        System.out.println("end time: " + System.currentTimeMillis());
    }
}

class Sub extends fa{
}

class MyThreadA extends Thread{
    private Sub sub;

    MyThreadA(Sub sub) {
        this.sub = sub;
    }

    @Override
    public void run() {
        try {
            sub.methodA();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

    }
}
class MyThreadB extends Thread{
    private Sub sub;

    MyThreadB(Sub sub) {
        this.sub = sub;
    }

    @Override
    public void run() {
        try {
            sub.methodA();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}
public class Main {
    public static void main(String[] args) {

        Sub sub = new Sub();

        MyThreadA myThreadA = new MyThreadA(sub);
        myThreadA.setName("A");
        MyThreadB myThreadB = new MyThreadB(sub);
        myThreadB.setName("B");
        myThreadA.start();
        myThreadB.start();

    }

}

1.4 sync方法的弊端

它是对整个对象的同步锁, 而且需要保证一口气执行完的代码是整个方法。 我们不需要让整个方法一口气执行完,更多的只是关键代码要同步。 因此下面学习sync()同步代码块。

2、sync同步代码块

sync(对象监视器), 对象监视器是判断属于同一个锁的关键。

2.1 sync(this)

sync(this){
代码块
}

锁对象也是 对象 ,跟sync方法一样。代码块的内容上锁

2.2 静态同步: synchronized静态方法 和 synchronized(XX.class){}

对类进行上锁

2.3 静态同步 和 该类中的其他同步 不属于一个锁。

当线程A占用了Class Class1 的静态同步锁, 线程B可以访问Cass1的普通同步代码块。

3、volatile关键字

让变量在线程间可见,具备可见性但不具备原子性

案例一:server模式下的内存读取

调成server模式的时候,虚拟机处于性能考虑,只读取私有栈,也可以说读取的是线程的缓存。而我们对变量进行修改是在公共栈进行修改的,因此线程中读取的变量不会被改变。

解决这个问题需要在变量上加volatile关键字。作用是强制从公共堆栈中得到变量,而不是在线程私有的堆栈中获得,由于没有同步性,因此用于通知类的变量

import javax.security.auth.Subject;
import java.util.*;

class RunThread extends Thread{

	// ** 在这里加
    private boolean isRunning = true;
    public void setRunning(boolean running) {
        this.isRunning = running;
    }
    public boolean isRunning() {
        return isRunning;
    }
    @Override
    public void run() {
        System.out.println("进入run");
        while (isRunning == true) {

        }
        System.out.println("离开run");
    }
}
public class Main {
    public static void main(String[] args) {
        try {
            RunThread runThread = new RunThread();
            runThread.start();
            Thread.sleep(1000);
            runThread.setRunning(false);
            System.out.println("已经赋值为false");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

4、原子类

AtomicInteger 原子类,不过使用原子类也并非完全安全,因为输出语句是不同步的,因此打印出来也是乱序。

5、synchronized也可以实现volatile的可见性

synchronized(anyString){
}

就让线程对anyString的读取,从私有栈改为公共内存。

你可能感兴趣的:(java多线程)