layout: posts
title: JAVA并发
date: 2019-03-10 20:04:52
tags: java
categories: 代码
线程
下面是一个单独的线程中执行一个任务的简单过程
1)将任务代码移动到实现了Runable接口的类的run方法中。
public interface Runnable{void run();}
由于Runable是一个函数式接口,可以用lambda表达式创建一个实例:
Runnable r = () ->{task code}
2)由Runnable创建一个Thread对象:
Thread t = new Thread(r);
3)启动线程:
t.start();
**4)也可通过构建一个Thread类的子类定义一个线程(从方法不推荐):
class MyThread extends Thread{public void run(){task code;}}
线程创建调用
中断线程

线程状态
线程可以有以下6种状态:
- New(新创建)
- Runnable(可运行)
- Blocked(被阻塞)
- Waiting (等待)
- Timed waiting(计时等待)
- Terminated(被终止)
线程状态:
线程属性
线程优先级
在java中,每一个线程有一个优先级。默认情况下,一个线程继承它的父线程的优先级。可以使用setPriority()提高或降低任何一个线程的优先级。可以将优先级设置为在MIN_PRIORITY(1)与MAX_PRIORITY(10)之间的任何值。
守护线程
为其他线程提供服务。
未捕获异常处理器(*)
线程的run方法不能抛出任何受查异常,但是,非受查异常会导致线程终止。在这种情况下,线程就死亡了。
异常处理器必须属于一个实现Thread.UncaughtExceptionHandler接口的类。
同步
竞争条件的一个例子
/*** Bank类* A bank with a number of bank accounts**/public class Bank {private final double[] accounts;public Bank(int n, double initialBalance) {accounts = new double[n];Arrays.fill(accounts, initialBalance);}public void transfer(int from, int to, double amount) {if (accounts[from] <amount) return;System.out.print(Thread.currentThread());accounts[from]-=amount;System.out.printf("%10.2f from %d to %d", amount, from, to);accounts[to]+=amount;System.out.printf("Total Balance: %10.2f%n", getTotalBalance());}public double getTotalBalance() {double sum = 0;for (double a : accounts) {sum+=a;}return sum;}public int size() {return accounts.length;}}
/****This program shows data corruption when multiple threads access a data structure**/public class UnsynchBankTest {public static final int NACCOUNTS = 100 ;public static final double INITIAL_BALANCE = 1000 ;public static final double MAX_AMOUNT = 1000 ;public static final int DELAY = 10;public static void main(String[] args){Bank bank = new Bank(NACCOUNTS, INITIAL_BALANCE);for (int i = 0; i < NACCOUNTS; i++) {int fromAccount =i;Runnable r = () -> {try {while (true) {int toAccount = (int) (bank.size() * Math.random());double amount = MAX_AMOUNT * Math.random();bank.transfer(fromAccount, toAccount, amount);Thread.sleep((int) (DELAY * Math.random()));}} catch (InterruptedException e) {}};Thread t = new Thread(r);t.start();}}}
当两个线程试图同时更新同一个账号的时候,问题就出现了,架设两个线程同时执行指令:
accounts[to] += amount
问题在于这不是原子操作。该指令可能被处理如下:
1 ) 将 accounts [ to ] 加载到寄存器 。
2 ) 增 加 amount 。
3 ) 将结果写回 accounts [ to ] 。
现在假定第1个线程执行步骤1和步骤2,然后,他被剥夺了运行权。假定第2个线程被唤醒并修改了accounts数组中的同一项。然后,第2个线程被唤醒并完成其第3步。这一动作擦去了第2个线程所做的更新。
锁对象
有两种机制防止代码块受并发访问的干扰。java语言提供一个synchronized关键字达到这一目的》synchronized关键字自动提供一个锁以及相关的条件。
用ReentrantLock保护代码块的基本结构如下:
myLock.lock();try{critical section}finally{myLock.unlock(); //make sure the lock is unlocked even if an exception is thrown}
这一结构确保任何时刻只有一个线程进入临界区,一旦一个线程封锁了锁对象,其他任何线程都无法通过lock语句。当其他线程调用lock时,他们被阻塞,直到第一个线程释放锁对象(将锁放在finally子句之内是至关重要的)。
给代码上锁:

条件对象
一个锁对象可以有一个或多个相关的条件对象。你可以使用newCondition方法获得一个条件对象,习惯上给每一个条件对象命名为可以反映它所表达的条件的名字,例如,在此设置一个条件对象来表达“余额充足”条件。
Class Bank{private Condition sufficientFunds;public Bank(){...sufficientFunds =bankLock.newCondition();}}
如果transfer方法发现余额不足,它调用
sufficientFunds.await();
当前线程现在被阻塞了,并放弃了锁。我们希望这样可以使得另一个线程可以进行增加账户余额的操作。
当另一个线程转账时,它应该调用:
sufficientFunds.singalAll()
等待获得锁的线程和调用await方法的线程存在本质上的不同。一旦一个线程调用await方法,它进入该条件的等待集。当锁可用时,该线程不能马上解除阻塞。相反,他处于阻塞状态,直到另一个线程调用同一条件上的singalAll方法时为止。singalAll方法仅仅是通知正在等待的线程:此时有可能已经满足条件,值得再次去检验条件。<br />对await的调用应该在如下的形式的循环体中
while(!(ok to proceed))condition.await();
应该何时调用 signalAll 呢?经验上讲,在对象的状态有利于等待线程的方向改变时调用signalAll。
public void transfer ( int from , int to , int amount ){bankLock.lock();try{while ( accounts [ from ] < amount )sufficientFunds.await();/ / transfer fundssufficientFunds.signalAll();}finally{bankLock.unlock();}}
synchronized关键字
如果一个方法用synchronized关键字声明,那么对象的锁将保护整个方法。也就是说,要调用该方法,线程必须获得内部的对象锁。<br /> 换句话说:
public synchronized void method(){method body}
等价于
public void method(){this.intrinsicLock.lock();try{method body}finally{this.intrinsiclock.unlock;}}
同步阻塞
略 待补
监视器概念
略 待补
final变量
略 待补
Volatile 域
略 待补
死锁
遗憾的是
Java 编程语言中没有任何东西可以避免或打破这种死锁现象。必须仔细设计
程序,以确保不会出现死锁。
线程局部变量
略 待补
锁测试与超市
线程在调用 lock 方法来获得另一个线程所持有的锁的时候,很可能发生阻塞。应该更加谨慎地申请锁。tryLock 方法试图申请一个锁,在成功获得锁后返回 true, 否则,立即返回false,而且线程可以立即离开去做其他事情。
读写锁

