Java 线程学习笔记(二)—— 进阶篇

在Java 5.0 提供了java.util.concurrent(简称JUC )包,在此包中增加了在并发编程中很常用的实用工具类,用于定义类似于线程的自定义子系统,包括线程池、异步IO 和轻量级任务框架。提供可调的、灵活的线程池。还提供了设计用于多线程上下文中的Collection 实现等。

volatile 关键字 内存可见性

package cn.fg.test03;

public class Tester {

	public static void main(String[] args) {
		Hello hello = new Hello();
		new Thread(hello).start();
		while (true) {
			if (hello.isFlag()) {
				System.out.println("----------------------------");
				break;
			}
		}
	}
	 
}

class Hello implements Runnable {
	
	private boolean flag = false;

	@Override
	public void run() {
		
		try {
			Thread.sleep(1000);   //模拟逻辑处理睡1秒
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		flag = true; //业务逻辑处理完置标志
		System.out.println(flag);
	}

	public boolean isFlag() {
		return flag;
	}
}

通过执行上述代码,你会发现while循环永远不会break,main线程会一直等待。为什么?hello线程花了1秒来处理逻辑,然后设置的flag的值,main线程进入while时flag为false,由于while底层效率非常高,main线程永远读不到true,如果你在while外或里面也睡一会,就会读到flag=true了。

Java 线程学习笔记(二)—— 进阶篇_第1张图片

如何解决?加上synchronized代码块即可

	public static void main(String[] args) {
		Hello hello = new Hello();
		new Thread(hello).start();
		
		while (true) {
			synchronized (hello) {
				if (hello.isFlag()) {
					System.out.println("----------------------------");
					break;
				}
			}
			
		}
	}

虽然synchronized可以解决,但是不是最优选择。Java提供了一种稍弱的同步机制,即volatile 变量,用来确保将变量的更新操作通知到其他线程。可以将volatile 看做一个轻量级的锁,但是又与锁有些不同:对于多线程,不是一种互斥关系;不能保证变量状态的“原子性操作”。说简单点,volatile 只能解决内存可见,不能解决线程安全。

//volatile 解决内存可见问题,加上volatile后,会实时更新主存,没有了缓存,效率要稍差些
//加与不加,具体看业务场景
private volatile boolean flag = false;

Java 线程学习笔记(二)—— 进阶篇_第2张图片

Atomic 原子变量

还是先来看一段代码

package cn.fg.test03;

public class TestAtomicDemo {

	public static void main(String[] args) {
		AtomicDemo ad = new AtomicDemo();
		for (int i = 0; i < 10; i++) {
			new Thread(ad).start();
		}
	}
}

class AtomicDemo implements Runnable{
	
	private int serialNumber = 0;
	
	@Override
	public void run() {
		serialNumber++;  //自加1
		System.out.println(serialNumber);
	}
}

在做++操作的时候,会有线程安全问题,可以使用synchronized来解决;这里我们使用另外一种解决方式,使用AtomicXXX原子变量。为什么叫原子变量?我们以++语法为例,++操作实际是做了以下几步:

1. int temp = serialNumber ; 
2. serialNumber = serialNumber + 1; 
3. serialNumber = temp;

可以简单理解为事务,读、写、改必须一步完成,不能被其他操作修改其值;原子变量底层使用了CAS算法

Java 线程学习笔记(二)—— 进阶篇_第3张图片所以在java.util.concurrent.atomic包下为我们提供了原子类,这里我们使用AtomicInteger来修改代码

package cn.fg.test03;

import java.util.concurrent.atomic.AtomicInteger;

public class TestAtomicDemo {

	public static void main(String[] args) {
		AtomicDemo ad = new AtomicDemo();
		for (int i = 0; i < 10; i++) {
			new Thread(ad).start();
		}
	}
}

class AtomicDemo implements Runnable{
	
	private AtomicInteger serialNumber = new AtomicInteger(0);
	
	@Override
	public void run() {
		System.out.println(serialNumber.getAndIncrement());
	}
}

使用原子变量的效率比synchronized高,synchronized可以看成是悲观锁,Atomic可以看成是乐观锁。

待续。。。

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