Java并发编程的艺术(上)

来源:互联网 发布:html怎么导入php文件 编辑:程序博客网 时间:2024/06/10 05:15
ChapterOne 并发编程的挑战 
1. 并发编程的目的是为了让程序更快速的运行,但是并不是启用更多的线程就能让程序最大限度的并发执行。
2. 进行多线程并发编程时,会遇到许多挑战,列举三个:上下文切换、死锁、其他资源限制。
3. 是否并发一定就比串行快?  例子:
public class CurrencyTest {
private static final long count = 1000000001;
 
/**
* @author PeterS
* @date 2016年5月4日
* @param
* @description
*/
public static void main(String[] args) throws InterruptedException{
currency();
serial();
}
public static void currency() throws InterruptedException {
long start = System.currentTimeMillis();
Thread thread = new Thread(new Runnable() {
@Override
public void run() {
// TODO Auto-generated method stub
int a= 0;
for(long i =0;i<count;i++){
a+=5;
}
}
});
thread.start();
int b = 0;
for(long i=0;i<count;i++) b--;
long time = System.currentTimeMillis()-start;
thread.join(); //wait for the thread to die
System.out.println("currency:"+time+"ms,b="+b);
}
public static void serial() {
long start = System.currentTimeMillis();
int a =0,b=0;
for(long i = 0;i<count;i++) a+=5;
for(long i = 0;i<count;i++) b--;
long time = System.currentTimeMillis()-start;
System.out.println("serial:"+time+"ms,b="+b);
}
 
}
    这个是比较结果:
count的值currency(ms)serial(ms)100011010000013510000000138731000000001363695可见,在并发量小的时候,并行并不占优势,并发量大了,效果才明显。
4. 上下文切换:
减少上下文切换的方法:
①、无锁并发编程:避免使用锁,例如将数据ID按照hash算法取模分段,不同线程处理不同段的数据②、CAS算法:Atom类,不需要加锁③、使用最少线程④、协程:在单线程中进行多任务的调度和切换5、死锁:
public class DeadlockTest {
private static String a = "A";
private static String b = "B";
 
/**
* @author PeterS
* @date 2016年5月4日
* @param
* @description
*/
public static void main(String[] args) {
new DeadlockTest().deadlock();
}
public void deadlock() {
Thread thread1 = new Thread(new Runnable() {
@Override
public void run() {
synchronized(a){
try {
Thread.currentThread().sleep(2000);
} catch (Exception e) {
e.printStackTrace();
}
synchronized(b){
System.out.println("1");
}
}
}
});
Thread thread2 = new Thread(new Runnable() {
public void run() {
synchronized(b){
synchronized (a) {
System.out.println("2");
}
}
}
});
thread1.start();
thread2.start();
}
 
}
这段代码就可能出现死锁。

如何有效的避免死锁呢?首先要看看死锁的四个必要条件:互斥、请求与保持、不可剥夺、循环等待。
所以避免死锁的方法有:
避免一个线程获取多个锁尽量保证每个锁只占有一个资源尝试使用定时锁,lock.trylock(timeout)来代替使用内部锁机制对于数据库锁,加锁和解锁必须放到一个数据库连接里,否则会出现解锁失败6. 其他资源限制:
硬件资源限制,采取分布式解决;
软件资源限制,采取资源池复用解决。

ChapterTwo Java并发机制的底层实现原理

1. Java代码的生命流程:
Java代码——>Java字节码——>被类加载器加载到JVM中——>JVM执行字节码——>转化成汇编语言——>在CPU上执行
2. volatile关键字的应用
①volatile关键字保证所有线程看到这个变量是数据一致的,
②实现原理:volatile变量会触发两个操作
将当前处理器的缓存行数据写入系统内存这个写回内存的操作会使其他的CPu缓存了该地址的数据无效
3. synchronized的实现原理和应用
具体表现形式:
对于普通同步方法,锁是当前实例对象;对于静态同步方法,锁是当前类的Class 对象;对于同步代码块,锁是括号()里配置的内容;
锁一共四种状态,从低到高依次是:无锁、偏向锁、轻量级锁、重量级锁。
这四种状态的锁,只能升级,不能降级。
自旋锁:一直自旋,不调用睡眠的锁,会消耗CPU。
ChapterThree Java内存模型
ChapterFour Java并发编程基础
1. 一个不同的Java程序包含哪些线程?
如下代码:
public class MutiThread {
 
/**
* @author PeterS
* @date 2016年5月4日
* @param
* @description
*/
public static void main(String[] args) {
ThreadMXBean threadMXBean = ManagementFactory.getThreadMXBean();
ThreadInfo[] threadInfos = threadMXBean.dumpAllThreads(false, false);
for (ThreadInfo threadInfo : threadInfos) {
System.out.println("["+threadInfo.getThreadId()+"]"+threadInfo.getThreadName());
}
}
 
}

2. 在不同的JVM和操作系统中,线程的规划存在差异,有些操作系统可能会忽略线程优先级的设定;
3. 线程的状态
六种:
状态名称说明NEW初始状态,线程被构建,但是还没有调用start()方法RUNNABLE就绪状态和运行中状态统称runnableBLOCKED阻塞状态,表示线程阻塞于锁WAITING等待状态TIME_WAITING超时等待状态,可在指定的时间自行返回TERMINATED终止状态,表示当前的线程已经执行完毕了4. 查看线程的状态:
用jps命令查看线程id;然后用jstack命令+线程id查看;


5. Java线程状态变迁图

6.Daemon线程(守护线程)
主要作用于后台调度以及支持性工作,当Java虚拟机中不存在一个Daemon线程时,虚拟机将退出。
可以用Thread.setDaemon(true)设置为Daemon线程。
7. 优雅的中断线程:
通过中断操作和标志位的方式。
public class Shutdown {
 
public static void main(String[] args) throws Exception{
Runner one = new Runner();
Thread countThread = new Thread(one,"CountThread");
countThread.start();
TimeUnit.SECONDS.sleep(1);
countThread.interrupt(); //用中断操作来终止进程
Runner two = new Runner();
countThread = new Thread(two,"CountThread");
countThread.start();
TimeUnit.SECONDS.sleep(1);
two.cancel(); //设置标志位来中断进程
}
private static class Runner implements Runnable{
private long i;
private volatile boolean on = true;
/**
* @author PeterS
* @date 2016年5月5日
* @param
* @description
*/
@Override
public void run() {
while(on&&!Thread.currentThread().isInterrupted()){ //判断终止的条件
i++;
}
System.out.println("count i="+i);
}
public void cancel() {
on=false;
}
}
 
}
8. 等待/通知机制(生产/消费者模型)
该范式分为两个部分:等待者(消费者)和通知者(生产者);
等待者(消费者):
synchronized(object){
while(condition不满足){
object.wait();
}
//to do
}
通知者(生产者):
synchronized(object){
改变condition;
object.notifyAll();
}
直接上代码:
public class WaitNotify {
static boolean flag = true;
static Object lock = new Object();
 
/**
* @author PeterS
* @date 2016年5月5日
* @param
* @description
*/
public static void main(String[] args) {
Thread thread1 = new Thread(new Wait(),"wait");
thread1.start();
SleepUtils.second(1);
Thread thread2 = new Thread(new Notify(),"notify");
thread2.start();
}
static class Wait implements Runnable {
public void run() {
synchronized (lock) {
if (flag) {
try {
System.out.println(Thread.currentThread()+"flag is true.wait@ "+new SimpleDateFormat("HH:mm:ss").format(new Date()));
lock.wait();
} catch (Exception e) {
// TODO: handle exception
}
}
System.out.println(Thread.currentThread()+"flag is false.running@ "+new SimpleDateFormat("HH:mm:ss").format(new Date()));
 
}
}
}
static class Notify implements Runnable{
 
/**
* @author PeterS
* @date 2016年5月5日
* @param
* @description
*/
@Override
public void run() {
synchronized (lock) {
System.out.println(Thread.currentThread()+"hold lock.notify@ "+new SimpleDateFormat("HH:mm:ss").format(new Date()));
lock.notifyAll();
flag = false;
SleepUtils.second(5);
}
synchronized (lock) {
System.out.println(Thread.currentThread()+"hold lock again.sleep@ "+new SimpleDateFormat("HH:mm:ss").format(new Date()));
SleepUtils.second(5);
}
}
}
public static class SleepUtils{
public static final void second(long sec) {
try {
TimeUnit.SECONDS.sleep(sec);
} catch (Exception e) {
// TODO: handle exception
}
}
}
 
}

9. 管道传输
管道输入输出流主要用于线程之间的数据传输,传输媒介为内存。
分为4种表现形式:PipedOutputStream、PipedInputStream、PipedReader、PipedWriter。
10. join()
含义:如果线程a执行了thread.join(),表示当前线程A等待thread线程终止之后才从thread.join()返回。
join(long millis)和join(long millis,int nanos)提供了超时特性,如果线程没有在给定的时间内结束,将返回。
11. ThreadLocal使用
ThreadLocal是一个线程变量,是一个以ThreadLocal为key,任意对象为value的存储结构。建议深入学习一下。这里不做细致讲解。
12. 线程池的实例:
ConnectionPool.java
public class ConnectionPool {
private LinkedList<Connection> pool = new LinkedList<>();
/**
* @author PeterS
* @date 2016年5月5日
* @param
* @description
*/
public ConnectionPool(int initialSize ) {
if (initialSize>0) {
for(int i=0;i<initialSize;i++){
pool.addLast(ConnectionDriver.createConnection());
}
}
}
public void releaseConnection(Connection connection) {
if (connection!=null) {
synchronized (pool) {
//连接释放后需要通知,这样其他消费者可以感知到连接池中已经归还了一个连接
pool.addLast(connection);
pool.notifyAll();
}
}
}
public Connection fetchConnection(long millis) throws InterruptedException{
synchronized (pool) {
//完全超时
if (millis<=0) {
while (pool.isEmpty()) {
pool.wait();
}
return pool.removeFirst();
}
else{
//这是一个超时等待模型,当等待超过一定的时常millis,就直接返回
long future = System.currentTimeMillis()+millis;
long remaining = millis;
while (pool.isEmpty()&&remaining>0) {
pool.wait(remaining);
remaining = future-System.currentTimeMillis();
}
Connection result = null;
if (!pool.isEmpty()) {
result=pool.removeFirst();
}
return result;
}
}
}
 
}
ConnectionDriver.java
public class ConnectionDriver {
static class ConnectionHandler implements InvocationHandler{
 
/**
* @author PeterS
* @date 2016年5月5日
* @param
* @description
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if (method.getName().equals("commit")) {
TimeUnit.SECONDS.sleep(100);
}
return null;
}
}
public static final Connection createConnection() {
return (Connection)Proxy.newProxyInstance(ConnectionDriver.class.getClassLoader(),new Class<?>[]{Connection.class}, new ConnectionHandler());
}
 
}
ConnectionPoolTest.java
public class ConnectionPoolTest {
static ConnectionPool pool = new ConnectionPool(10);
static CountDownLatch start = new CountDownLatch(1);
static CountDownLatch end;
 
/**
* @author PeterS
* @date 2016年5月5日
* @param
* @description
*/
public static void main(String[] args) throws Exception{
int threadCount = 10;
end = new CountDownLatch(threadCount);
int count = 20;
AtomicInteger got = new AtomicInteger();
AtomicInteger noGot = new AtomicInteger();
for(int i = 0;i<threadCount;i++){
Thread thread = new Thread(new ConnectionRunner(count, got, noGot),"ConnectionRunnerThread");
thread.start();
}
start.countDown();
end.await();
System.out.println("total invoke:"+(threadCount*count));
System.out.println("got connection:"+got);
System.out.println("not got connection:"+noGot);
}
static class ConnectionRunner implements Runnable{
int count;
AtomicInteger got;
AtomicInteger noGot;
/**
* @author PeterS
* @date 2016年5月5日
* @param
* @description
*/
public ConnectionRunner(int count,AtomicInteger got,AtomicInteger noGot) {
this.count=count;
this.got=got;
this.noGot=noGot;
}
 
/**
* @author PeterS
* @date 2016年5月5日
* @param
* @description
*/
@Override
public void run() {
try {
start.await();
} catch (Exception e) {
}
while (count>0) {
try {
Connection connection = pool.fetchConnection(1000);
if (connection!=null) {
try {
connection.createStatement();
connection.commit();
} finally{
pool.releaseConnection(connection);
got.incrementAndGet();
}
}
else {
noGot.incrementAndGet();
}
} catch (Exception e) {
}finally {
count--;
}
}
end.countDown();
}
}
 
}
0 0
原创粉丝点击