简介
java多线程中可以使用synchronized关键字来实现线程间同步互斥,但在jdk1.5中新增加了ReentrantLock类也能实现同样的效果,并且在扩展功能上也更加强大,比如具有嗅探锁定、多路通知分支等功能,并且使用上比synchronized更加灵活。
如何使用ReentrantLock
主要是lock.lock()和lock.unlock()两个方法
public class MyService implements Runnable { protected ReentrantLock lock; public MyService(ReentrantLock lock){
this.lock = lock;
} public void run() {
lock.lock();
for (int i = 0; i < 5; i++) {
System.out.println(Thread.currentThread().getName()+" "+i);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
lock.unlock();
} }
/**
* 测试类
* @author ko
*
*/
public class Test { public static void main(String[] args) {
ReentrantLock lock = new ReentrantLock();
for (int i = 0; i < 4; i++) {
new Thread(new MyService(lock)).start();
}
}
}
Thread-0 0
Thread-0 1
Thread-0 2
Thread-0 3
Thread-0 4
Thread-1 0
Thread-1 1
Thread-1 2
Thread-1 3
Thread-1 4
Thread-2 0
Thread-2 1
Thread-2 2
Thread-2 3
Thread-2 4
Thread-3 0
Thread-3 1
Thread-3 2
Thread-3 3
Thread-3 4
使用Condition类实现wait、notify的功能
Condition类也是jdk1.5里出来的,它能实现synchronized和wait、notify搭配的功能,另外比后者更灵活,Condition可以实现多路通知功能,也就是在一个Lock对象里可以创建多个Condition(即对象监视器)实例,线程对象可以注册在指定的Condition中,从而可以有选择的进行线程通知,在调度线程上更加灵活。
而synchronized就相当于整个Lock对象中只有一个单一的Condition对象,所有的线程都注册在这个对象上。线程开始notifyAll时,需要通知所有的WAITING线程,没有选择权,会有相当大的效率问题。
使用Condition为什么会报java.lang.IllegalMonitorStateException异常
还是刚才的代码,稍作改动
public class MyService implements Runnable { protected ReentrantLock lock;
protected Condition condition; public MyService(ReentrantLock lock,Condition condition){
this.lock = lock;
this.condition = condition;
} public void run() {
// lock.lock();
try {
condition.await();
} catch (InterruptedException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
for (int i = 0; i < 5; i++) {
System.out.println(Thread.currentThread().getName()+" "+i);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
// lock.unlock();
} }
/**
* 测试类
* @author ko
*
*/
public class Test { public static void main(String[] args) {
ReentrantLock lock = new ReentrantLock();
Condition condition = lock.newCondition();
for (int i = 0; i < 4; i++) {
new Thread(new MyService(lock,condition)).start();
}
}
}
Exception in thread "Thread-0" Exception in thread "Thread-1" Exception in thread "Thread-2" Exception in thread "Thread-3" java.lang.IllegalMonitorStateException
at java.util.concurrent.locks.ReentrantLock$Sync.tryRelease(Unknown Source)
at java.util.concurrent.locks.AbstractQueuedSynchronizer.release(Unknown Source)
at java.util.concurrent.locks.AbstractQueuedSynchronizer.fullyRelease(Unknown Source)
at java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.await(Unknown Source)
at ww.MyService.run(MyService.java:26)
at java.lang.Thread.run(Unknown Source)
报错的异常信息是监视器出错,原因是在调用condition.await()之前是要先调用lock.lock()来获得同步监视器。
正确使用Condition类
public class MyService implements Runnable { protected ReentrantLock lock;
protected Condition condition; public MyService(ReentrantLock lock,Condition condition){
this.lock = lock;
this.condition = condition;
} public void await(){
try {
lock.lock();
System.out.println("await time is "+System.currentTimeMillis());
condition.await();
System.out.println("after await info...");
} catch (InterruptedException e1) {
e1.printStackTrace();
} finally {
lock.unlock();
}
} public void signal(){
try {
lock.lock();
System.out.println("signal time is "+System.currentTimeMillis());
condition.signal();
} finally {
lock.unlock();
}
} public void run() {
await();
} }
/**
* 测试类
* @author ko
*
*/
public class Test { public static void main(String[] args) {
ReentrantLock lock = new ReentrantLock();
Condition condition = lock.newCondition();
MyService service = new MyService(lock,condition);
new Thread(service).start();
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
service.signal();
}
}
await time is 1501142954379
signal time is 1501142957381
after await info...
成功实现等待通知模式,整理一下,下表的方法功能是对应的
Object类 |
Condition类 |
Wait() |
Await() |
Wait(long timeout) |
Await(long time,TimeUnit unit) |
Notify() |
Signal() |
notifyAll() |
signalAll() |
使用多个Condition实现通知部分线程
public class MyService implements Runnable { protected ReentrantLock lock;
protected Condition conditionA;
protected Condition conditionB; public MyService(ReentrantLock lock,Condition conditionA,Condition conditionB){
this.lock = lock;
this.conditionA = conditionA;
this.conditionB = conditionB;
} public void await_A(){
try {
lock.lock();
System.out.println(Thread.currentThread().getName()+" await_A time is "+System.currentTimeMillis());
conditionA.await();
System.out.println(Thread.currentThread().getName()+" after await_A info...");
} catch (InterruptedException e1) {
e1.printStackTrace();
} finally {
lock.unlock();
}
} public void await_B(){
try {
lock.lock();
System.out.println(Thread.currentThread().getName()+" await_B time is "+System.currentTimeMillis());
conditionB.await();
System.out.println(Thread.currentThread().getName()+" after_B await info...");
} catch (InterruptedException e1) {
e1.printStackTrace();
} finally {
lock.unlock();
}
} public void signal_A(){
try {
lock.lock();
System.out.println(Thread.currentThread().getName()+" signal_A time is "+System.currentTimeMillis());
conditionA.signal();
} finally {
lock.unlock();
}
} public void signal_B(){
try {
lock.lock();
System.out.println(Thread.currentThread().getName()+" signal_B time is "+System.currentTimeMillis());
conditionB.signal();
} finally {
lock.unlock();
}
} public void run() {
String tname = Thread.currentThread().getName();
if (tname.equals("A")) {
await_A();
} else if (tname.equals("B")) {
await_B();
}
} }
/**
* 测试类
* @author ko
*
*/
public class Test { public static void main(String[] args) {
ReentrantLock lock = new ReentrantLock(); Condition conditionA = lock.newCondition();
Condition conditionB = lock.newCondition();
MyService service = new MyService(lock,conditionA,conditionB); Thread tA = new Thread(service);
tA.setName("A");
tA.start(); Thread tB = new Thread(service);
tB.setName("B");
tB.start(); try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
} service.signal_A();
}
}
A await_A time is 1501482321344
B await_B time is 1501482321346
main signal_A time is 1501482324344
A after await_A info...
可以看到只唤醒了A线程。。。
一对一的生产者消费者
/**
* 生产者和消费者一对一
* @author ko
*
*/
public class MyService { protected ReentrantLock lock = new ReentrantLock();
protected Condition condition = lock.newCondition();
protected boolean hasValue = false; public void set(){
try {
lock.lock();
while (hasValue == true) {
condition.await();
}
System.out.println("★");
hasValue = true;
condition.signal();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} finally {
lock.unlock();
}
} public void get(){
try {
lock.lock();
while (hasValue == false) {
condition.await();
}
System.out.println("☆");
hasValue = false;
condition.signal();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} finally {
lock.unlock();
}
} }
/**
* 生产者
* @author ko
*
*/
public class Producer implements Runnable { protected MyService myService; public Producer(MyService myService) {
super();
this.myService = myService;
} public void run() {
for (int i = 0; i < Integer.MAX_VALUE; i++) {
myService.set();
}
} }
/**
* 消费者
* @author ko
*
*/
public class Customer implements Runnable { protected MyService myService; public Customer(MyService myService) {
super();
this.myService = myService;
} public void run() {
for (int i = 0; i < Integer.MAX_VALUE; i++) {
myService.get();
}
} }
/**
* 测试类
* @author ko
*
*/
public class Test { public static void main(String[] args) {
MyService myService = new MyService();
Producer p = new Producer(myService);
Customer c = new Customer(myService); new Thread(p).start();
new Thread(c).start(); }
}
打印结果
多对多的生产者消费者
/**
* 测试类
* @author ko
*
*/
public class Test { public static void main(String[] args) {
MyService myService = new MyService(); for (int i = 0; i < 10; i++) {
new Thread(new Producer(myService)).start();
new Thread(new Customer(myService)).start();
} }
}
/**
* 生产者和消费者多对多
* @author ko
*
*/
public class MyService { protected ReentrantLock lock = new ReentrantLock();
protected Condition condition = lock.newCondition();
protected boolean hasValue = false; public void set(){
try {
lock.lock();
while (hasValue == true) {
System.out.println("有可能★连续打印");
condition.await();
}
System.out.println("★");
hasValue = true;
condition.signalAll();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} finally {
lock.unlock();
}
} public void get(){
try {
lock.lock();
while (hasValue == false) {
System.out.println("有可能☆连续打印");
condition.await();
}
System.out.println("☆");
hasValue = false;
condition.signalAll();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} finally {
lock.unlock();
}
} }
要注意的是由于现在是多个生产者消费者,所以condition.signal()要改为condition.signalAll(),其它代码不变。
查看打印结果,发现★和☆总是间隔打印,但是 有可能★连续打印 和 有可能☆连续打印 却有可能连续打印,这是因为改为signalAll后唤醒的是所有线程,有可能再次把自己唤醒,所以会出现这种情况。
公平锁与非公平锁
公平锁表示线程获取锁的顺序是按照线程加锁的顺序来分配的,而非公平锁是一种抢占机制,随机的。
还是上面的代码,给ReentrantLock换个有isFair参数的构造方法,new ReentrantLock(isFair) true就是公平锁,false就是非公平锁。再给打印的语句加上线程名,当为true时,线程是按顺序打印,为false时随机打印。
方法getHoldCount()的使用
getHoldCount()表示当前线程获取锁的个数
public class MyService {
protected ReentrantLock lock = new ReentrantLock(true);
public void method1(){
try {
lock.lock();
System.out.println(Thread.currentThread().getName()+"线程保持lock锁的个数:"+lock.getHoldCount()+" method1");
method2();
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
} public void method2(){
try {
lock.lock();
System.out.println(Thread.currentThread().getName()+"线程保持lock锁的个数:"+lock.getHoldCount()+" method2");
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
}
public class ThreadA extends Thread { protected MyService myService; public ThreadA(MyService myService) {
super();
this.myService = myService;
} public void run() {
myService.method1();
} }
public class Test {
public static void main(String[] args) {
MyService myService = new MyService();
new ThreadA(myService).start();
}
}
getQueueLength()方法使用
getQueueLength()表示等待获取lock锁的估计线程个数。
public class MyService {
protected ReentrantLock lock = new ReentrantLock(); public void getql(){
System.out.println("等待获取lock锁的估计线程个数:"+lock.getQueueLength()+" method1");
} public void synmethod(){
System.out.println(Thread.currentThread().getName()+"开始了。。。");
lock.lock();
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
lock.unlock();
}
}
public class ThreadA extends Thread {
protected MyService myService; public ThreadA(MyService myService) {
super();
this.myService = myService;
} public void run() {
myService.synmethod();
}
}
public class Test {
public static void main(String[] args) {
MyService myService = new MyService();
new ThreadA(myService).start();
new ThreadA(myService).start();
new ThreadA(myService).start();
new ThreadA(myService).start();
new ThreadA(myService).start();
while (true) {
myService.getql();
try {
Thread.sleep(900);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
getWaitQueueLength(condition)的用法
getWaitQueueLength(condition) 表示返回等待与此锁相关的给定条件condition的线程估计数。比如有3个线程都执行了同一个condition的await方法,那么调用getWaitQueueLength(condition)返回的就是3.
public class MyService {
protected ReentrantLock lock = new ReentrantLock();
protected Condition condition = lock.newCondition();
protected Condition condition1 = lock.newCondition(); public void synmethod(){
System.out.println(Thread.currentThread().getName()+"开始了。。。");
lock.lock();
System.out.println("进入lock锁与condition关联的的估计线程个数:"+lock.getWaitQueueLength(condition));
System.out.println("进入lock锁与condition1关联的的估计线程个数:"+lock.getWaitQueueLength(condition1));
System.out.println("");
try {
if (!Thread.currentThread().getName().contains("2")) {
condition.await();
}else{
condition1.await();
}
} catch (InterruptedException e) {
e.printStackTrace();
}
lock.unlock();
}
}
public class Test {
public static void main(String[] args) {
MyService myService = new MyService();
new ThreadA(myService).start();
new ThreadA(myService).start();
new ThreadA(myService).start();
new ThreadA(myService).start();
new ThreadA(myService).start();
}
}
hasQueuedThread(thread)和hasQueuedThreads()的使用
hasQueuedThread(thread)返回的是线程thread是否在等待获取lock锁
hasQueuedThreads()返回的是是否有线程正在等待获取lock锁
public class MyService {
protected ReentrantLock lock = new ReentrantLock();
protected Condition condition = lock.newCondition(); public void hasa(Thread thread){
if (lock.hasQueuedThread(thread)) {
System.out.println(thread.getName()+"正在等待获取lock锁。。。");
}else{
System.out.println(thread.getName()+"不在等待获取lock锁。。。");
}
System.out.println("是否有线程在等待获取lock锁:"+lock.hasQueuedThreads());
System.out.println("");
} public void synmethod(){
System.out.println(Thread.currentThread().getName()+"开始了。。。");
lock.lock(); if (Thread.currentThread().getName().contains("1")) {
try {
condition.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
try {
Thread.sleep(15000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
lock.unlock();
}
}
public class Test {
public static void main(String[] args) {
MyService myService = new MyService();
ThreadA ta = new ThreadA(myService);
ta.start();
ThreadA tb = new ThreadA(myService);
tb.start();
while (true) {
myService.hasa(ta);
myService.hasa(tb);
System.out.println(""+ta.getName());
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
hasWaiters(condition)的用法
hasWaiters(condition)表示是否有线程进入了lock锁与condition相关联的等待中。
public class MyService {
protected ReentrantLock lock = new ReentrantLock();
protected Condition condition = lock.newCondition(); public void synmethod(){
System.out.println(Thread.currentThread().getName()+"开始了。。。");
lock.lock(); System.out.println("是否有线程进入了lock锁与condition相关联的等待中:"+lock.hasWaiters(condition));
if (Thread.currentThread().getName().contains("0")) {
try {
condition.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
lock.unlock();
}
}
public class Test {
public static void main(String[] args) {
MyService myService = new MyService(); ThreadA ta = new ThreadA(myService);
ta.start(); try {
Thread.sleep(3500);
} catch (InterruptedException e) {
e.printStackTrace();
} ThreadA tb = new ThreadA(myService);
tb.start(); }
}
方法isFair()、isHeldByCurrentThread()、isLocked()的使用
isFair()判断线程锁是不是公平锁
isHeldByCurrentThread()查询当前线程是否保持此锁定
isLocked()查询此锁定是否由任意线程保持
public class MyService {
protected ReentrantLock lock = new ReentrantLock(true);
protected Condition condition = lock.newCondition(); public void synmethod(){
System.out.println("lock锁是不是公平锁:"+lock.isFair());
System.out.println("lock锁定是否由任意线程保持:"+lock.isLocked());
System.out.println(Thread.currentThread().getName()+"是否保持lock锁定:"+lock.isHeldByCurrentThread());
lock.lock();
System.out.println("lock锁定是否由任意线程保持:"+lock.isLocked());
System.out.println(Thread.currentThread().getName()+"是否保持lock锁定:"+lock.isHeldByCurrentThread());
lock.unlock();
}
}
public class Test {
public static void main(String[] args) {
MyService myService = new MyService(); ThreadA ta = new ThreadA(myService);
ta.start();
}
}
lockInterruptibly()方法的使用
lockInterruptibly()比lock()获取锁之前多了个判断,如果当前线程未被中断,则获取锁定,如果已被中断,则抛出java.lang.InterruptedException异常。
public class MyService {
protected ReentrantLock lock = new ReentrantLock(true);
protected Condition condition = lock.newCondition(); public void synmethod(){
try {
System.out.println(Thread.currentThread().getName()+" begin "+new Date().toString());
for (int i = 0; i < Integer.MAX_VALUE/20; i++) {// 大概会延长7 8s,这里不用sleep延长线程时间,而是这样写,是因为调用线程的interrupt()方法时,如果线程在sleep会报异常
Math.random();
}
if(new Random().nextInt(5)%2==0){// 随机
System.out.println(Thread.currentThread().getName()+" 使用的是lock() ");
lock.lock();
}else{
System.out.println(Thread.currentThread().getName()+" 使用的是lockInterruptibly() ");
lock.lockInterruptibly();
} System.out.println(Thread.currentThread().getName()+" lock 1 "+new Date().toString());
for (int i = 0; i < Integer.MAX_VALUE/20; i++) {// 同上
Math.random();
}
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} finally {
System.out.println(Thread.currentThread().getName()+" lock 2 "+new Date().toString());
if (lock.isHeldByCurrentThread()) {
lock.unlock();
}
System.out.println(Thread.currentThread().getName()+" end "+new Date().toString());
}
}
}
/**
* 测试类
* @author ko
*
*/
public class Test {
public static void main(String[] args) {
MyService myService = new MyService(); ThreadA taa = new ThreadA(myService);
taa.setName("taa");
taa.start(); try {
Thread.sleep(1500);// 等前面的线程都启动好
} catch (InterruptedException e) {
e.printStackTrace();
} System.out.println("taa interrupt "+new Date().toString());
taa.interrupt();// 打标记 这个时候taa还没有进入lock锁里 }
}
代码里采用了随机的处理,有的时候使用的是lock(),有的时候使用的是lockInterruptibly(),多运行几遍就能得到两种结果。
lock.tryLock()、lock.tryLock(timeout, unit)方法使用
lock.tryLock()) 立即返回,获得锁返回true,没获得锁返回false
lock.tryLock(3, TimeUnit.SECONDS) 等待3s,3s后,获得锁返回true,没获得锁返回false
public class MyService {
protected ReentrantLock lock = new ReentrantLock(true);
protected Condition condition = lock.newCondition(); public void synmethod(){
try {
System.out.println(Thread.currentThread().getName()+"进入方法"+new Date().toString()); if (lock.tryLock()) {// 立即返回,获得锁返回true,没获得锁返回false
System.out.println(Thread.currentThread().getName()+"获得锁");
} else {
System.out.println(Thread.currentThread().getName()+"没有获得锁");
} // if (lock.tryLock(3, TimeUnit.SECONDS)) {// 等待3s,3s后,获得锁返回true,没获得锁返回false
// System.out.println(Thread.currentThread().getName()+"获得锁的时间"+new Date().toString());
// Thread.sleep(10000);
// } else {
// System.out.println(Thread.currentThread().getName()+"没有获得锁");
// } System.out.println(Thread.currentThread().getName()+"方法结束"+new Date().toString());
} catch (Exception e) {
e.printStackTrace();
}
}
}
/**
* 测试类
* @author ko
*
*/
public class Test {
public static void main(String[] args) {
MyService myService = new MyService(); ThreadA ta = new ThreadA(myService);
ta.setName("ta");
ta.start(); ThreadA taa = new ThreadA(myService);
taa.setName("taa");
taa.start();
}
}
awaitUninterruptibly()方法使用
当在线程等待的时候,如果外部要中断该线程,不会报InterruptedException异常,而await()会报异常
public class MyService {
protected ReentrantLock lock = new ReentrantLock(true);
protected Condition condition = lock.newCondition(); public void synmethod(){
try {
System.out.println(Thread.currentThread().getName()+"进入方法"+new Date().toString()); lock.lock(); if (new Random().nextInt(5)%2 == 0) {
System.out.println(Thread.currentThread().getName()+"走的是await()");
condition.await();
} else {
System.out.println(Thread.currentThread().getName()+"走的是awaitUninterruptibly()");
condition.awaitUninterruptibly();
} } catch (Exception e) {
e.printStackTrace();
} finally {
System.out.println(Thread.currentThread().getName()+"方法结束"+new Date().toString());
lock.unlock();
}
}
}
/**
* 测试类
* @author ko
*
*/
public class Test {
public static void main(String[] args) {
MyService myService = new MyService(); ThreadA taa = new ThreadA(myService);
taa.setName("taa");
taa.start(); try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
taa.interrupt();// 打标记
}
}
awaitUntil(Time time)方法使用
awaitUntil(Time time)和await()一样会使当前线程进入等待状态,不过它有个截止时间,到了time这个时间,自动唤醒。
public class MyService {
protected ReentrantLock lock = new ReentrantLock(true);
protected Condition condition = lock.newCondition(); public void synmethod(){
try {
System.out.println(Thread.currentThread().getName()+"进入方法"+new Date().toString()); lock.lock(); Calendar calendar = Calendar.getInstance();
calendar.add(Calendar.SECOND, 10);
System.out.println("begin awaitUntil "+new Date().toString());
condition.awaitUntil(calendar.getTime());//
System.out.println("after awaitUntil "+new Date().toString()); } catch (Exception e) {
e.printStackTrace();
} finally {
System.out.println(Thread.currentThread().getName()+"方法结束"+new Date().toString());
lock.unlock();
}
}
}
/**
* 测试类
* @author ko
*
*/
public class Test {
public static void main(String[] args) {
MyService myService = new MyService(); ThreadA taa = new ThreadA(myService);
taa.setName("taa");
taa.start();
}
}
使用Condition实现顺序执行
/**
* 利用condition实现顺序执行
* @author ko
*
*/
public class MyService implements Runnable{
protected ReentrantLock lock;
protected Condition signalCondition;// 在某个线程里负责等待
protected Condition awaitCondition;// 在某个线程里负责唤醒 public MyService(ReentrantLock lock, Condition signalCondition, Condition awaitCondition) {
super();
this.lock = lock;
this.signalCondition = signalCondition;
this.awaitCondition = awaitCondition;
} public void print(Condition signalCondition, Condition awaitCondition) throws InterruptedException{
lock.lock();
for (int j = 0; j < 10; j++) {
for (int i = 1; i < 4; i++) {
System.out.println(Thread.currentThread().getName()+" "+i);
}
System.out.println("");
signalCondition.signal();
awaitCondition.await();
}
lock.unlock();
} public void run() {
try {
print(signalCondition, awaitCondition);
} catch (InterruptedException e) {
e.printStackTrace();
}
} }
myservice
/**
* 测试类
* @author ko
*
*/
public class Test {
public static void main(String[] args) {
ReentrantLock lock = new ReentrantLock();
Condition conditionA = lock.newCondition();
Condition conditionB = lock.newCondition();
Condition conditionC = lock.newCondition(); MyService myService = new MyService(lock, conditionB, conditionA); new Thread(new MyService(lock, conditionB, conditionA),"thread a").start();
new Thread(new MyService(lock, conditionC, conditionB),"thread b").start();
new Thread(new MyService(lock, conditionA, conditionC),"thread c").start(); }
}
test
thread a 1
thread a 2
thread a 3 thread b 1
thread b 2
thread b 3 thread c 1
thread c 2
thread c 3 thread a 1
thread a 2
thread a 3 thread b 1
thread b 2
thread b 3 thread c 1
thread c 2
thread c 3 thread a 1
thread a 2
thread a 3 thread b 1
thread b 2
thread b 3 thread c 1
thread c 2
thread c 3 thread a 1
thread a 2
thread a 3 thread b 1
thread b 2
thread b 3 thread c 1
thread c 2
thread c 3 thread a 1
thread a 2
thread a 3 thread b 1
thread b 2
thread b 3 thread c 1
thread c 2
thread c 3 thread a 1
thread a 2
thread a 3 thread b 1
thread b 2
thread b 3 thread c 1
thread c 2
thread c 3 thread a 1
thread a 2
thread a 3 thread b 1
thread b 2
thread b 3 thread c 1
thread c 2
thread c 3 thread a 1
thread a 2
thread a 3 thread b 1
thread b 2
thread b 3 thread c 1
thread c 2
thread c 3 thread a 1
thread a 2
thread a 3 thread b 1
thread b 2
thread b 3 thread c 1
thread c 2
thread c 3 thread a 1
thread a 2
thread a 3 thread b 1
thread b 2
thread b 3 thread c 1
thread c 2
thread c 3
打印结果