java.util.concurrent
(简称JUC)包,在此包中增加了在并发编程中很常用的工具类,// 使用 volatile 之前
public class TestVolatile{
public static void main(String[] args){
ThreadDemo td = new ThreadDemo();
new Thread(td).start();
while(true){
if(td.isFlag()){
System.out.println("########");
break;
}
}
}
}
class ThreadDemo implements Runnable{
private boolean flag = false;
public void run(){
try{
// 该线程 sleep(200), 导致了程序无法执行成功
Thread.sleep(200);
}catch(InterruptedException e){
e.printStackTrace();
}
flag = true;
Sytem.out.println("flag="+isFlag());
}
public boolean isFlag(){
return flag;
}
public void setFlag(boolean flag){
this.flag = flag;
}
}
// 解决问题方式一: 同步锁
// 但是,效率太低
public class TestVolatile{
public static void main(String[] args){
ThreadDemo td = new ThreadDemo();
new Thread(td).start();
while(true){
// 使用同步锁
synchronized(td){
if(td.isFlag()){
System.out.println("########");
break;
}
}
}
}
}
// 解决方式二: 使用 volatile 关键字
public class TestVolatile{
public static void main(String[] args){
ThreadDemo td = new ThreadDemo();
new Thread(td).start();
while(true){
if(td.isFlag()){
System.out.println("########");
break;
}
}
}
}
class ThreadDemo implements Runnable{
private volatile boolean flag = false;
同上(略)
}
i++
的原子性问题i++
的操作实际上分为三个步骤: "读-改-写";java.util.concurrent.atomic
包下,提供了常用的原子变量;
volatile
修饰,保证了内存可见性;int i = 10;
i = i++; // 此时, i=10
执行步骤:
int temp = i;
i = i + 1;
i = temp;
// 测试类
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;
public void run(){
try{
Thread.sleep(200);
}catch(InterruptedException e){
}
System.out.println(Thread.currentThread().getName() + ":" + getSerialNumber());
}
public int getSerialNumber(){
return serialNumber++;
}
}
/ 改进: 使用原子变量
class AtomicDemo implements Runnable{
private AtomicInteger serialNumber = new AtomicInteger();
public void run(){
try{
Thread.sleep(200);
}catch(InterruptedException e){
}
System.out.println(Thread.currentThread().getName()+":"+getSerialNumber());
}
public int getSerialNumber(){
// 自增运算
return serialNumber.getAndIncrement();
}
}
3.1 CAS 算法
// 模拟CAS 算法
class CompareAndSwap{
private int value;
// 获取内存值
public synchronized int get(){
return value;
}
// 无论更新成功与否,都返回修改之前的内存值
public synchronized int compareAndSwap(int expectedValue, int newValue){
// 获取旧值
int oldValue = value;
if(oldValue == expectedValue){
this.value = newValue;
}
// 返回修改之前的值
return oldValue;
}
// 判断是否设置成功
public synchronized boolean compareAndSet(int expectedValue, int newValue){
return expectedValue == compareAndSwap(expectedValue, newValue);
}
}
public class TestCompareAndSwap{
public static void main(String[] args){
final CopareAndSwap cas = new CompareAndSwap();
for(int i=0; i<10; i++){
// 创建10个线程,模拟多线程环境
new Thead(new Runnable(){
public void run(){
int expectedValue = cas.get();
boolean b = cas.compareAndSet(expectedValue, (int)(Math.random()*100));
System.out.println(b);
}
}).start();
}
}
}
java.util.concurrent
包中提供了多种并发容器类来改进同步容器的性能;4.1 ConcurrentHashMap
Collection
实现: ConcurrentHashMap
,ConcurrentSkipListMap
ConcurrentSkipListSet
, CopyOnWriteArrayList
和 CopyOnWriteArraySet
;
ConcurrentHashMap
通常优于同步的HashMap
;ConcurrentSkipListMap
通常优于同步的TreeMap
;CopyOnWriteArrayList
优于同步的ArrayList
;4.2 CountDownLatch(闭锁)
CountDownLatch
是一个同步辅助类,在完成一组正在其他线程中执行的操作之前,它允许一个或多个线程一直等待;// 测试类: 计算多线程的执行时间
public class TestCountDownLatch{
public static void main(String[] args){
final CountDownLatch latch = new CountDownLatch(10);
LatchDemo ld = new LatchDemo(latch);
long start = System.currentTimeMillis();
// 创建10个线程
for(int i=0; i<10; i++){
new Thread(ld).start();
}
try{
latch.await();
}catch(InterruptedException e){
}
long end = System.currentTimeMillis();
System.out.println("耗费时间为:"+(end - start));
}
}
class LatchDemo implements Runnable{
private CountDownLatch latch;
// 有参构造器
public LatchDemo(CountDownLatch latch){
this.latch = latch;
}
public void run(){
synchronized(this){
try{
// 打印50000以内的偶数
for(int i=0; i<50000; i++){
if(i % 2 == 0){
System.out.println(i);
}
}
}finally{
// 线程数量递减
latch.countDown();
}
}
}
}
// 测试类
public class TestCallable{
public static void main(String[] args){
ThreadDemo td = new ThreadDemo();
// 执行 Callable 方式,需要 FutureTask 实现类的支持
// FutureTask 实现类用于接收运算结果, FutureTask 是 Future 接口的实现类
FutureTask result = new FutureTask<>(td);
new Thread(result).start();
// 接收线程运算后的结果
try{
// 只有当 Thread 线程执行完成后,才会打印结果;
// 因此, FutureTask 也可用于闭锁
Integer sum = result.get();
System.out.println(sum);
}catch(InterruptedException | ExecutionException e){
e.printStackTrace();
}
}
}
class ThreadDemo implements Callable{
// 需要实现的方法
public Integer call() throws Exception{
// 计算 0~100 的和
int sum = 0;
for(int i=0; i<=100; i++){
sum += i;
}
return sum;
}
}
// 测试类: 以卖票为例
// 使用 lock 之前
public class TestLock{
public static void main(String[] args){
Ticket ticket = new Ticket();
new Thread(ticket,"1号窗口").start();
new Thread(ticket,"2号窗口").start();
new Thread(ticket,"3号窗口").start();
}
}
class Ticket implements Runnable{
private int tick = 100;
public void run(){
while(true){
if(tick > 0){
try{
Thread.sleep(200);
}catch(InterruptedException e){
}
System.out.println(Thread.currentThread().getName()+"完成售票,余票为: "+ --tick);
}
}
}
}
// 使用 Lock
class Ticket implements Runnable{
private int tick = 100;
private Lock lock = new ReentrantLock();
public void run(){
while(true){
// 上锁
lock.lock();
try{
if(tick > 0){
try{
Thread.sleep(200);
}catch(InterruptedException e){
}
System.out.println(Thread.currentThread().getName()+"完成售票,余票为: "+ --tick);
}
}finally{
// 释放锁
lock.unlock();
}
}
}
}
// 练习: 程序按序交替
// 编写一个程序,开启3个线程,这三个线程的 ID 分别为 A, B, C, 每个线程将自己的 ID 在屏幕上打印10遍,
// 要求输出的结果必须按顺序显示:
// 如: ABCABCABC... 依次递归
public class TestABCAlternate{
public static void main(String[] args){
AlternateDemo ad = new AlternateDemo();
new Thread(new Runnable(){
public void run(){
for(int i=1; i<20; i++){
ad.loopA(i);
}
}
},"A").start();
new Thread(new Runnable(){
public void run(){
for(int i=1; i<20; i++){
ad.loopB(i);
}
}
},"B").start();
new Thread(new Runnable(){
public void run(){
for(int i=1; i<20; i++){
ad.loopC(i);
System.out.println("--------------------");
}
}
},"C").start();
}
}
class AlternateDemo{
private int number = 1; // 当前正在执行线程的标记
private Lock lock = new ReentrantLock();
private Condition condition1 = lock.newCondition();
private Condition condition2 = lock.newCondition();
private Condition condition3 = lock.newCondition();
// totalLoop 表示循环第几轮
// 线程A
public void loopA(int totalLoop){
// 上锁
lock.lock();
try{
// 1. 判断
if(number != 1){
condition1.await();
}
// 2. 打印
for(int i=1; i <= 5; i++){
System.out.println(Thread.currentThread().getName()+"\t"+i+"\t"+totalLoop);
}
// 3. 唤醒线程B
number = 2;
condition2.signal();
}catch(Exception e){
e.printStackTrace();
}finally{
// 释放锁
lock.unlock();
}
}
// 线程B
public void loopB(int totalLoop){
// 上锁
lock.lock();
try{
// 1. 判断
if(number != 2){
condition2.await();
}
// 2. 打印
for(int i=1; i <= 15; i++){
System.out.println(Thread.currentThread().getName()+"\t"+i+"\t"+totalLoop);
}
// 3. 唤醒线程C
number = 3;
condition3.signal();
}catch(Exception e){
e.printStackTrace();
}finally{
// 释放锁
lock.unlock();
}
}
// 线程C
public void loopC(int totalLoop){
// 上锁
lock.lock();
try{
// 1. 判断
if(number != 3){
condition3.await();
}
// 2. 打印
for(int i=1; i <= 20; i++){
System.out.println(Thread.currentThread().getName()+"\t"+i+"\t"+totalLoop);
}
// 3. 唤醒线程A
number = 1;
condition1.signal();
}catch(Exception e){
e.printStackTrace();
}finally{
// 释放锁
lock.unlock();
}
}
}
// 测试类
public class TestReadWriteLock{
public static void main(String[] args){
ReadWriteLockDemo rw = new ReadWriteLockDemo();
// 一个线程进行写
new Thread(new Runnable(){
public void run(){
rw.set((int)(Math.random()*100));
}
},"Write:").start();
// 100个线程进行读操作
for(int i=0; i<100; i++){
new Thread(new Runnable(){
public void run(){
rw.get();
}
},"Read:").start();
}
}
}
class ReadWriteLockDemo{
private int number = 0;
private ReadWriteLock lock = new ReentrantReadWriteLock();
// 读
public void get(){
lock.readLock().lock(); // 上锁
try{
System.out.println(Thread.currentThread().getName()+":"+number);
}finally{
lock.readLock().unlock(); // 释放锁
}
}
// 写
public void set(int number){
lock.writeLock().lock();
try{
System.out.println(Thread.currentThread().getName());
this.number = number;
}finally{
lock.writeLock().unlock();
}
}
}
public class Thread8Monitor {
public static void main(String[] args) {
final Number number = new Number();
final Number number2 = new Number();
new Thread(new Runnable() {
public void run() {
number.getOne();
}
}).start();
new Thread(new Runnable() {
public void run() {
// number.getTwo();
number2.getTwo();
}
}).start();
/*new Thread(new Runnable() {
@Override
public void run() {
number.getThree();
}
}).start();*/
}
}
class Number{
public static synchronized void getOne(){//Number.class
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
}
System.out.println("one");
}
public synchronized void getTwo(){//this
System.out.println("two");
}
public void getThree(){
System.out.println("three");
}
}
题目:判断打印的"one" or "two"??
①两个普通的方法,两个线程,打印? // one two
②新增Thread.sleep()给getOne(),打印? // one two
③新增普通方法getThree(),打印? //three one two
④两个普通同步方法,两个Number对象,打印? // two one
⑤修改getOne()为静态方法,一个Number对象,打印? // two one
⑥修改两个方法均均为静态方法,一个Number对象? // one two
⑦一个静态同步方法,一个非静态同步方法,两个Number对象? // two one
⑧两个静态同步方法,两个Number对象? //one two
总结:
①一个对象里面如果有多个synchronized方法,某一个时刻内,只要一个线程去调用其中的一个synchronized方法了,其他的线程都只能等待,换句话说,某一时刻内,只能有唯一一个线程去访问这些synchronized方法。
②锁的是当前对象this,被锁定后,其他线程都不能进入到当前对象的其他的synchronized方法。
③加个普通方法后发现和同步锁无关。
④换成静态同步方法后,情况又变化
⑤所有的非静态同步方法用的都是同一把锁 -- 实例对象本身,也就是说如果一个实例对象的非静态同步方法获取锁后,该实例对象的其他非静态同步方法必须等待获取锁的方法释放锁后才能获取锁,可是别的实例对象的非静态同步方法因为跟该实例对象的非静态同步方法用的是不同的锁,所以毋须等待该实例对象已经取锁的非静态同步方法释放锁就可以获取他们自己的锁。
⑥所有的静态同步方法用的也是同一把锁 -- 类对象本身,这两把锁是两个不同的对象,所以静态同步方法与非静态同步方法之间不会有竞争条件。但是一旦一个静态同步方法获取锁后,其他的静态同步方法都必须等待该方法释放锁后才能获取锁,而不管是同一个实例对象的静态同步方法之间,还是不同的实例对象的静态同步方法之间,只要它们是同一个实例对象
java.util.concurrent.Executor
: 负责线程的使用和调度的根接口;ExecutorService
: 子接口,线程池的主要接口;ThreadPoolExecutor
: 线程池的实现类;ScheduledExecutorService
: 子接口,负责线程的调度;ScheduledThreadPoolExecutor
: 继承了线程池的实现类,实现了负责线程调度的子接口;Executors
ExecutorService newFixedThreadPool()
: 创建固定大小的线程池;ExecutorService newCachedThreadPool()
: 缓存线程池,线程池中线程的数量不固定,可以根据需求自动更改数量;ExecutorService newSingleThreadExecutor()
: 创建单个线程池, 线程池中只有一个线程;ScheduledExecutorService newScheduledThreadPool()
: 创建固定大小的线程,可以延时或定时的执行任务;public class TestThreadPool{
public static void main(String[] args){
// 1. 创建线程池
ExecutorService pool = Executors.newFixedThreadPool(5);
ThreadPoolDemo tpd = new ThreadPoolDemo();
// 2. 为线程池中线程分配任务
// submit(Callable task)
// submit(Runnable task)
for(int i=0; i<10; i++){
pool.submit(tpd);
}
// 3. 关闭线程池
pool.shutdown();
}
}
class ThreadPoolDemo implements Runnable{
private int i=0;
public void run(){
while(i <= 100){
System.out.println(Thread.currentThread().getName()+" : "+ i++)
}
}
}
9.1 线程调度
public class TestScheduledThreadPool{
public static void main(String[] args) throws Exception{
// 1. 创建线程池
ScheduledExecutorService pool = Executors.newScheduledThreadPool(5);
// 2. 分配任务
// pool.shedule(Callalbe callable, long delay, TimeUnit unit(时间单位))
for(int i=0; i < 10; i++){
Future result = pool.schedule(new Callable(){
public Integer call() throws Exception{
// 产生100以内的随机数
int num = new Random().nextInt(100);
System.out.println(Thread.currentThread().getName()+ ":" + num);
return num;
}
}, 3, TimeUnit.SECONDS);
System.out.println(result.get());
}
//3. 关闭线程池
pool.shutdown();
}
}
public class TestForkJoinPool{
public static void main(String[] args){
ForkJoinPool pool = new ForkJoinPool();
ForkJoinTask task = new ForkJoinSumCalculate(0L, 100000000L);
Long sum = pool.invoke(task);
System.out.println(sum);
}
}
class ForkJoinSumCalculate extends RecursiveTask{
private static final long serialVersionUID = 24340990L;
private long start;
private long end;
private static final long THURSHOLD = 10000L; // 拆分临界值
// 有参构造器
public ForkJoinSumCalculate(long start, long end){
this.start = start;
this.end = end;
}
public Long compute(){
long length = end - start;
if(length <= THURSHOLD){
long sum = 0L;
for(long i = start; i<=end; i++){
sum += i;
}
return sum;
}else{
long middle = (start + end ) / 2;
ForkJoinSumCalculate left = new ForkJoinSumCalculate(start, middle);
left.fork(); // 进行拆分,同时压入线程队列
ForkJoinSumCalculate right = new ForkJoinSumCalculate(middle + 1, end);
right.fork(); // 进行拆分,同时压入线程队列
return left.join() + right.join();
}
}
}