synchronized
一般的多线程,执行顺序随机:
public class ThreadTest {
public static void main(String[] args)
{
for(int i=0;i<3;i++)
{
MyThread temp=new MyThread();
temp.start();
}
}
}
class MyThread extends Thread
{
@Override
public void run()
{
for(int i=0;i<5;i++)
{
System.out.println(Thread.currentThread().getName()+": ["+i+"]");
}
}
}
执行效果:

使用synchronized关键字修饰的多线程会顺序执行:
package 同步;
public class SynchronizedTest {
static int number=0;
//synchronized块(对象级)
public String setNumber1()
{
synchronized(this)
{
number++;
return "setNumber1.:"+number;
}
}
//synchronized块(类级别)
public String setNumber2()
{
synchronized(SynchronizedTest.class)
{
number++;
return "setNumber2.:"+number;
}
}
//synchronized 方法
public synchronized String setNumber3()
{
number++;
return "setnumber3.:"+number;
}
public String setNumber4() {
return"setnumber4.:"+number;
}
}
package 同步;
public class Test{
public static void main(String[] args)
{
SynchronizedTest t = new SynchronizedTest();
for(int i=0;i<3;i++)
{
MyThread2 temp=new MyThread2(t);
temp.start();
}
}
}
class MyThread2 extends Thread{
SynchronizedTest Object;
public MyThread2(SynchronizedTest Object) {
this.Object = Object;
}
@Override
public void run() {
// TODO Auto-generated method stub
try {
Thread.sleep(0);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"--"+Object.setNumber1());
System.out.println(Thread.currentThread().getName()+"--"+Object.setNumber3());
}
}
执行效果:

所以使用锁进行多线程的同步就能控制执行的顺序。
volatile
volatile的读写操作与锁的释放和获取有着同样的效果
public class VolatileTest {
volatile int number = 0;
public int getNumber() {
return number;
}
public void setNumber(int number) {
this.number = number;
}
public int getandIncrement() {
number++;
return number;
}
public static void main(String args[]) {
VolatileTest test = new VolatileTest();
test.setNumber(5);
System.out.println( test.getNumber());
test.getandIncrement();
System.out.println(test.getandIncrement());
}
}
运行结果:

package 同步;
public class VolatileExample {
int number = 0;
public synchronized void setNumber(int number) {
this.number = number;
}
public int getandIncrement() {
int t =getNumber();
t+= 1;
setNumber(t);
return t;
}
public synchronized int getNumber() {
return number;
}
public static void main(String args[]) {
VolatileExample test = new VolatileExample();
test.setNumber(5);
System.out.println( test.getNumber());
test.getandIncrement();
System.out.println(test.getandIncrement());
}
}
运行结果:

从上面的例子中可以看出对一个 volatile变量的单个读/写操作,与对一个普通变量的读/写操作使用同一个锁来同步,它们之间的执行效果相同。锁的happens-before规则保证释放锁和获取锁的两个线程之间的内存可见性,这意味着对一个volatile变量的读,总是能看到(任意线程)对这个 volatile变量最后的写入。锁的语义决定了临界区代码的执行具有原子性。这意味着即使是 64 位的 long 型和double 型变量,只要它是volatile变量,对该变量的读写就将具有原子性。如果是多个volatile操作或类似于 volatile++这种复合操作,这些操作整体上不具有原子性。简而言之,volatile 变量自身具有下列特性:
可见性。对一个 volatile 变量的读,总是能看到(任意线程)对这个 volatile 变量最后的写入。
原子性:对任意单个 volatile 变量的读/写具有原子性,但类似于 volatile++这 种复合操作不具有原子性
volatile 写-读建立的 happens before 关系
从内存语义的角度来说,volatile 的写-读与锁的释放-获取有相同的内存效果: volatile 写和锁的释放有相同的内存语义;volatile 读与锁的获取有相同的内存语 义。 请看下面使用 volatile 变量的示例代码:
class VolatileExample {
int a = 0;
volatile boolean flag = false;
public void writer() {
a = 1; //1
flag = true; //2
}
public void reader() {
if (flag) { //3
int i = a; //4
}
}
}
假设线程 A 执行 writer()方法之后,线程 B 执行 reader()方法。根据 happens before 规则,这个过程建立的 happens before 关系可以分为两类:
- 根据程序次序规则,1 happens before 2; 3 happens before 4。
- 根据 volatile 规则,2 happens before 3。
- 根据 happens before 的传递性规则,1 happens before 4。 上述 happens before 关系的图形化表现形式如下: 在上图中,每一个箭头链接的两个节点,代表了一个 happens before 关系。黑色 箭头表示程序顺序规则;橙色箭头表示 volatile 规则;蓝色箭头表示组合这些规则 后提供的 happens before 保证。 这里 A 线程写一个 volatile 变量后,B 线程读同一个 volatile 变量。A 线程在写 volatile 变量之前所有可见的共享变量,在 B 线程读同一个 volatile 变量后,将立 即变得对 B 线程可见。 volatile读写时就是几个线程通过主内存进行通信

volatile 写-读的内存语义 volatile 写的内存语义如下: .当写一个 volatile 变量时,JMM 会把该线程对应的本地内存中的共享变量值刷 新到主内存。 以上面示例程序 VolatileExample 为例,假设线程 A 首先执行 writer()方法,随后 线程 B 执行 reader()方法,初始时两个线程的本地内存中的 flag 和 a 都是初始状 态。

volatile 读的内存语义如下:
当读一个 volatile 变量时,JMM 会把该线程对应的本地内存置为无效。线程接下来将从主内存中读取共享变量。

线程 A 写一个 volatile 变量,实质上是线程A向接下来将要读这个volatile变量的某个线程发出了(其对共享变量所在修改的)消息。
线程 B 读一个 volatile 变量,实质上是线程B接收了之前某个线程发出的(在写这个volatile变量之前对共享变量所做修改的)消息。
线程 A 写一个 volatile 变量,随后线程 B 读这个 volatile 变量,这个过程实质上是线程 A 通过主内存向线程 B发送消息。