java线程间的交互

java 中线程交互的方式多种多样,你可能需要都看一下。

Thread.join()

最简单的就是通过线程的join方法进行交互。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public static void main(String[] args) throws Exception {
Thread thread1 = new Thread(() -> {
try {
Thread.sleep(5000);
System.out.println("thread1 finish");
} catch (InterruptedException e) {
e.printStackTrace();
}
});
Thread thread12 = new Thread(() -> System.out.println("thread2 finish"));

thread11.start();
thread2.start();
thread1.join();
thread2.join();
System.out.println("all thread finish");
}

执行结果

1
2
3
thread1 finish
thread2 finish
all thread finish

join方法用于让当前执行线程等待join线程执行结束。其实现原理是不停检查,join线程是否存活,如果join线程存活则让当前线程永远等待。

CountDownLatch

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public static void main(String[] args) throws Exception {
//参数是2表示对象执行2次countDown方法才能释放锁
CountDownLatch c = new CountDownLatch(2);
new Thread(() -> {
System.out.println(1);
c.countDown();
System.out.println(2);
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
c.countDown();
}).start();

c.await(); //线程等待
System.out.println("3");
}

执行结果

1
2
3
1
2
3

CyclicBarrier

同步屏障,CyclicBarrier的字面意思是可循环使用(Cyclic)的屏障(Barrier)。CyclicBarrier的字面意思是可循环使用(Cyclic)的屏障(Barrier)。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
 public static void main(String[] args) throws Exception {
CyclicBarrier c = new CyclicBarrier(2);
new Thread(() -> {
try {
c.await();
} catch (Exception e) {
}
System.out.println(1);
}).start();

Thread.sleep(2000);
System.out.println(2);
try {
c.await();
} catch (Exception e) {
}
}

执行结果

1
2
2
1

CyclicBarrier还提供一个更高级的构造函数CyclicBarrier(int parties,Runnable barrier-Action) ,用于在线程到达屏障时,优先执行barrierAction,方便处理更复杂的业务场景。

CyclicBarrier和CountDownLatch的区别,CountDownLatch的计数器只能使用一次,而CyclicBarrier的计数器可以使用reset()方法重置,同时CyclicBarrier还提供其他有用的方法,比如getNumberWaiting方法可以获得CyclicBarrier阻塞的线程数量。isBroken()方法用来了解阻塞的线程是否被中断。

Semaphore

信号量:是用来控制同时访问特定资源的线程数量,它通过协调各个线程,以保证合理的使用公共资源。Semaphore可以控制系统的流量,拿到信号量的线程可以进入,否则就等待。通过acquire()和release()获取和释放访问许可。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
public static void main(String[] args) throws Exception {
int threadCount = 30;
ExecutorService threadPool = Executors.newFixedThreadPool(threadCount);
Semaphore s = new Semaphore(5);
for (int i = 0; i < threadCount; i++) {
threadPool.execute(new Runnable() {
@Override
public void run() {
try {
s.acquire(); //获取许可证
System.out.println(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()) + "\t\t\tsave data");
//每隔5s执行5个线程,直到全部执行完
Thread.sleep(5000);
s.release(); //释放许可证
} catch (InterruptedException e) {
}
}
});
}

//shutdown后不允许提交线程
threadPool.shutdown();
}

Condition

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
    public class TestCondition {

private Lock lock = new ReentrantLock();
private Condition condition = lock.newCondition();

public void method1(){
try {
lock.lock();
System.out.println("当前线程:" + Thread.currentThread().getName() + "进入等待状态..");
Thread.sleep(3000);
System.out.println("当前线程:" + Thread.currentThread().getName() + "释放锁..");
condition.await(); // Object wait
System.out.println("当前线程:" + Thread.currentThread().getName() +"继续执行...");
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}

public void method2(){
try {
lock.lock();
System.out.println("当前线程:" + Thread.currentThread().getName() + "进入..");
Thread.sleep(3000);
System.out.println("当前线程:" + Thread.currentThread().getName() + "发出唤醒..");
condition.signal(); //Object notify
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}

public static void main(String[] args) {

final TestCondition uc = new TestCondition();
Thread t1 = new Thread(() -> uc.method1(), "t1");
Thread t2 = new Thread(() -> uc.method2(), "t2");

t1.start(); //t1先执行,然后阻塞,然后t2线程执行await释放锁,
t2.start();
}

}

执行结果

1
2
3
4
5
当前线程:t1进入等待状态..
当前线程:t1释放锁..
当前线程:t2进入..
当前线程:t2发出唤醒..
当前线程:t1继续执行...

Exchanger

Exchanger 交换者,是用于线程间进行数据交换的。它提供一个同步点,在这个同步点两个线程可以交换数据。如果第一个线程先执行exchange()方法,它会一直等待第二个线程也执行exchange方法,当两个线程都到达同步点时,这两个线程就可以交换数据,将本线程的数据传递给对方。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
public static void main(String[] args) {

Exchanger<String> exgr = new Exchanger<String>();
ExecutorService threadPool = Executors.newFixedThreadPool(2);

threadPool.execute(new Runnable() {
@Override
public void run() {
try {
String A = "银行流水A";// A录入银行流水数据
Thread.sleep(4000);

System.out.println("B输入的是 :" + exgr.exchange(A));
} catch (InterruptedException e) {
}
}
});

threadPool.execute(new Runnable() {
@Override
public void run() {
try {
String B = "银行流水B";// B录入银行流水数据
String A = exgr.exchange(B); //得到A值
System.out.println("A和B数据是否一致:" + A.equals(B) + ",A录入的是:" + A + ",B录入是:" + B);
} catch (InterruptedException e) {
}
}
});

threadPool.shutdown();
}