Java线程和线程同步 - 线程(2)

1. 概述

Java中的线程表现为Threadclass,创建线程的唯一方式就是创建此类的一个实例对象。

使用Java代码有两种的方式(但最终都是创建一个Thread类实例)来创建一个线程:

  1. 继承Thread类:不必说,他就是Thread类的实例
  2. 实现Runable接口:使用时需要将实现类作为参数传递给一个Thread类的实例

2. 同步

Java提供了很多种线程之间通信的机制,最基本的一种就是 synchronization - 同步,这是通过 monitor - 监视器来实现的

首先,所有对象都关联着一个监视器,线程可以锁定或者解锁此监视器;
其次,且同一时间只能有一个线程可以锁定监视器,如果有其它线程想要尝试锁定的话它就会 block - 阻塞,直到它获得锁定权;

2.1 synchronized statement - 语句

synchronized 语句被执行时,会尝试去锁定其指定的对象上的监视器,直到成功为止;然后执行语句,最后自动解锁

语法:在synchronized (Expression) {}Expression显示指定一个对象(最好的方式是实例语句中为this,类语句中为 类.class

2.2 synchronized method - 方法

synchronized方法不同与语句的地方只在于其充当监视器的对象不能指定,而是只能按照默认规则来:实例语句中为this,类语句中为 类.class

2.3 deadlock - 死锁

Java语言不会阻止和检测死锁,程序如果需要避免死锁,应该使用自己保证

2.4 其它同步机制

  1. volatile变量
    任何被volatile修饰的变量,都不拷贝副本到工作内存,而是任何修改直接操作主内存。因此,对于volatile变量的修改,所有线程可以马上看到。但是,volatile不能保证线程们对变量的修改是有序的。

    很简单,比如说多个线程操作volatile变量a时,

    public volatile int a;
    
    public void add(int b){
     a = a + b;
    }
    //比如,a = 3,每个线程的b都是传1。
    //1. 第一个线程来了,先进行3 + 1 记录,然后准备将4赋值给a时丢失CPU;
    //2. 第二个线程来了,也是3 + 1,然后赋值成功a等于4了。
    //3. 第一个线程恢复CPU权,将4赋值给a。
    //如上,其实正确结果无论线程一或二谁先执行,都应该是a = 5。但由于上述执行过程,a最终还是4,此时就是线程不安全。

    由于a = a + b;并非是一个原子操作,所以有可能第一个线程执行到一半时停止而第二个线程开始执行。

    所以,volatile适用的场景是有限的,总结如下:
    可行必要条件

    1. 对变量的写操作不依赖当前值。
    2. 变量没有包含在具有其它变量的不变式中。

    其实由于volatile只保证了可见性,所以其最符合直接赋值的场景。如上代码如果add方法内部只是a = b;即是。

  2. java.util.concurrent包中的类

对象、监视器、锁、线程的关系如下:
Java线程和线程同步 - 线程(2)_第1张图片

《Java线程模型、线程状态 - 线程(1)》
《Java线程安全 - 线程(3)》

你可能感兴趣的:(java,线程,同步)