Java多线程编程核心技术——第二章学习笔记

  • 某一段代码中的私有变量,不存在线程安全问题
  • 脏读:无效数据的读出
  • 如果多个线程共同访问一个对象那个中的实例变量,则有可能出现线程安全的问题。

synchronized关键字

  • 关键字synchronized使方法成为线程安全的,且取得的锁是对象锁,不是一段代码或者方法当做锁
  • 调用关键字synchronized声明的方法一定是排队运行的,只有共享的资源读写才需要同步化
  • 一个线程拿到了一个synchronized声明的方法的锁,那么这个类中的其它synchronized声明的方法都不能被其他线程访问(当然非synchronized声明的方法还可以被访问),直到该线程释放这个锁
  • synchronized具有锁重入功能,当一个线程得到一个对象锁后,再次请求此对象锁是可以再次得到该对象锁的。
  • 当存在父子类继承关系时,子类是完全可以通过可重入锁的机制调用父类中的同步方法。
  • 一个线程中执行的代码出现异常时,其所持有的锁会自动释放
  • 同步不具有继承性
  • synchronized关键字相当于sychronized(this){整个方法的代码块}

synchronized同步代码块:

  • synchronized块中就是异步执行,在synchrinized块中就是同步执行。

  • 当一个线程访问object的一个synchronized(this)同步代码块时,其他线程对同一个object中所有其他synchronized(this)同步代码块的访问将被阻

  • synchronized同步代码块可以使用任意对象作为对象监视器,实现同步。任意对象 大多数是实例变量以及方法的参数

  • 同步代码块放在非同步的synchronized方法中进行声明,并不能保证调用方法的线程的同步性,可能出现脏读;

细化的3个结论

  • 当多个线程同时执行synchronized(x){}同步代码块时呈现同步效果

  • 当其他线程执行x对象中synchronized同步方法时呈现同步效果

  • 其他线程执行x对象方法里面的synchronized(this)代码块时也呈现同一个效果
    这三个结论有点绕,本质上是一个结论:
      本质上是因为synchronized中是同一把锁,这把锁被一个线程持有时不能被另一个线程持有

package com.company;

public class Thread1 extends  Thread {
    private Service service;
    private MyObject object;
    public Thread1(Service service,MyObject object){
        this.service = service;
        this.object = object;
    }
    public void run(){
        try {
            service.testMethod1(object);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

package com.company;

public class Thread2 extends  Thread {
    private Service service;
    private MyObject object;
    public Thread2(MyObject object){
        this.object = object;
    }
    public void run(){
            object.speedPrintString();
    }
}

package com.company;

public class Service {
    public void testMethod1(MyObject object) throws InterruptedException {
        synchronized (object) {
            System.out.println("testMethod1____getLock time="+System.currentTimeMillis()+"run ThreadName="+Thread.currentThread().getName());
            Thread.sleep(5000);
            System.out.println("testMethod1____releaseLock time="+System.currentTimeMillis()+"run ThreadName="+Thread.currentThread().getName());
        }
        }
    }

package com.company;

public class MyObject
{
    public void speedPrintString(){
        synchronized (this) {
            System.out.println("speedPrintString_____getLock time=" + System.currentTimeMillis() + "run ThreadName=" + Thread.currentThread().getName());
            System.out.println("-------------------");
            System.out.println("speedPrintString_____releaseLock time=" + System.currentTimeMillis() + "run ThreadName=" + Thread.currentThread().getName());
        }
    }
}

package com.company;

public class Main {

    public static void main(String[] args) {
	// write your code here
        Service service = new Service();
        MyObject object = new MyObject();
        MyObject object1 =new MyObject();
        Thread1 a = new Thread1(service,object);
        a.setName("a");
        a.start();
        Thread2 b = new Thread2(object1);//传入不同的obejct对象作为锁就不会同步了
        b.setName("b");
        b.start();
    }
}

  • 静态同步synchronized方法,是对当前的*.java文件对应的Class类进行持锁。 但是Class锁并不影响非Class锁的类。
  • 锁对象尽量不要选择String,因为String常量池可能造成问题
  • 锁对象不变,对象的属性改变还是同一把锁

volatile关键字

JVM设置为Server服务器的环境中,为了线程的运行效率,线程一直在私有堆栈中取得变量的值,很有可能私有堆栈和公共堆栈的值不同步,从而造成问题。

volatile关键字主要作用就是强制当前线程从公共堆栈中取值。
volatile和synchronized比较

  • volatile是线程同步的轻量级实现,且只能修饰于变量;synchronized可以修饰方法和代码块
  • 多线程访问volatile不会发生阻塞,而synchronized会产生阻塞
  • volatile能保证数据的可见性,但不能保证原子性;synchronized能保证原子性,也可以间接保证可见性,它会把私有内存和公有内存的数据进行同步(原子性:要么完全执行,要么完全不执行)
  • volatile解决的是变量在多个线程之间的可见性;synchronized解决的是多个线程之间访问资源的同步性

使用原子类atomic来实现i++之类的操作,能保证操作的原子性,但是并不能保证方法调用的是同步的。
Thread.sleep()不会释放当前线程持有的锁

猜你喜欢

转载自blog.csdn.net/qq_41374768/article/details/89461795