1、并发编程的艺术

时间:2023-01-17 20:51:47

待??:p13,16

多线程与单线程比较:

public class ConcurrencyTest {
private static final long count = 100000l;
public static void main(String[] args) throws InterruptedException {
concurrency();
serial();
}

// 并行
private static void concurrency() 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执行完,才会最后的输出
thread.join();
System.out.println("concurrency :" + time+"ms,b="+b);

}

// 串行
private static void serial() {
long start = System.currentTimeMillis();
int a = 0;
for (long i = 0; i < count; i++) {
a += 5;
}
int b = 0;
for (long i = 0; i < count; i++) {
b--;
}
long time = System.currentTimeMillis() - start;
System.out.println("serial:" + time+"ms,b="+b+",a="+a);
}

}

注:写了两个方法,一个是串行执行,另一个是在concurrency()方法体内定义了一个子线程,在main函数中调用时,实际上是主线程Main与子线程thread并发执行。

运行结果:
当count为1000000,并行是8ms,串行是13ms;
当count为10000,并行是1ms,串行是0ms;
结论:当次数较少时,串行较快,因为并行线程间切换需要浪费很多时间;当次数很大时,并行快。

为什么要写子线程?
子线程解决部分工作,使整体能够多线程并发提高求解速度。
创造线程的数量要适当,否则大量线程会处于等待状态。

多线程切换的上下文切换非常耗时。
上下文切换:在任务(线程)之间来回切换。

减少上下文切换的方法有无锁并发编程、CAS算法、使用最少线程和使用协程。

·无锁并发编程。多线程竞争锁时,会引起上下文切换,所以多线程处理数据时,可以用一些办法来避免使用锁,如将数据的ID按照Hash算法取模分段,不同的线程处理不同段的数据。
·CAS算法。Java的Atomic包使用CAS算法来更新数据,而不需要加锁。
·使用最少线程。避免创建不需要的线程,比如任务很少,但是创建了很多线程来处理,这样会造成大量线程都处于等待状态。
·协程:在单线程里实现多任务的调度,并在单线程里维持多个任务间的切换。

死锁案例:

// 演示死锁的例子

public class DeadLockDemo {
// 创建两个锁
private static String A = "A";
private static String B = "B";
public static void main(String[] args) {
new DeadLockDemo().deadLock();
}

// 会产生死锁的方法
private void deadLock() {
Thread t1 = new Thread(new Runnable() {
@Override
public void run() {
synchronized (A) {
try { Thread.currentThread().sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (B) {
System.out.println("1");
}
}
}
});
Thread t2 = new Thread(new Runnable() {
@Override
public void run() {
synchronized (B) {
synchronized (A) {
System.out.println("2");
}
}
}
});
t1.start();
t2.start();

}
}

注:在方法deadLock()内部有两个线程类t1和t2,在之后分别启动它们(启动只是说让它进入可运行状态,但是不是立刻就执行),在调用deadLock()时,如果执行顺序是t1 t2就会发生死锁,因为线程t1和线程t2互相等待对方释放锁。