多线程交替打印1~10的奇偶数
思路
- 搞两条线程,一条线程打印奇数任务,一条线程打印偶数任务。
- 为了防止线程间的无序争抢,使用synchronized锁。
- 为了保证交互打印,使用wait/notify,打印完一条先等待,让别的线程打印。以此循环。
实现1:synchronized方法对象锁
public class MyTask {
public synchronized void printNumber(int i) {
try {
this.notify();
System.out.println(Thread.currentThread().getName() + " " + i);
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public class SwapPrint {
public static void main(String[] args) {
final MyTask myTask = new MyTask();
Thread t1 = new Thread(new Runnable() {
@Override
public void run() {
for (int i = 1 ; i <= 10 ; i+=2) {
myTask.printNumber(i);
}
}
});
t1.setName("Thread t1");
t1.start();
Thread t2 = new Thread(new Runnable() {
@Override
public void run() {
for (int i = 2 ; i <= 10 ; i+=2) {
myTask.printNumber(i);
}
}
});
t2.setName("Thread t2");
t2.start();
}
}
思考:
有的人会搞不懂业务方法里面上来先notify,然后结束调用wait?
- 首先调用notify方法,将另一个线程唤醒。但是另一个线程根本进不来当前的方法(因为有synchronized锁)。
- 后面调用wait方法,将当前线程等待,wait方法会释放锁,另一个线程就可以进来了。
实现2:synchronized(this)对象锁
这种实现和实现1没什么区别,实现1虽然是synchronized修饰方法的方式,实际上还是使用当前对象的锁。
public class MyTask {
public void printNumber(int i) {
try {
synchronized (this) {
this.notify();
System.out.println(Thread.currentThread().getName() + " " + i);
this.wait();
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
思考:
synchronized关键字只是控制当前代码区域,只能有一个线程进入。
wait/notify方法可以让当前线程等待,也可以唤醒其他线程。
实现3:等待队列Condition实现
public class MyTask {
private ReentrantLock rl = new ReentrantLock();
private Condition condition = rl.newCondition();
public void printNumber(int i) {
try {
rl.lock();
condition.signal();
System.out.println(Thread.currentThread().getName() + " " + i);
condition.await();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
rl.unlock();
}
}
}
思考:
- 调用lock方法,当前线程设置同步状态,加入到aqs同步队列中,获取重入锁。
- 调用signal方法,唤醒等待队列中的线程,然后加入到aqs同步队列。
- 调用await方法,将当前线程加入到aqs等待队列。
ABC三个线程如何保证顺序执行
题目
ABC三个线程如何保证顺序执行。三个线程同时启动,然后按照顺序执行,每个线程执行10次。
思路
首先想到了等待队列Condition唤醒部分线程,使用ReentrantLock进行加锁。
初始版实现
/**
* @description A\B\C三个线程顺序执行10次
*
* @author sunpy
* @date 2018年11月28日 下午2:23:45
*/
public class MyTest {
static class MyTask {
private static ReentrantLock rl = new ReentrantLock();
private static Condition conditionA = rl.newCondition();
private static Condition conditionB = rl.newCondition();
private static Condition conditionC = rl.newCondition();
private static int number = 0;
public void execute() {
rl.lock();
try {
while (number < 30) {
if (number % 3 == 0) {
System.out.println(Thread.currentThread().getName() + " - " + number);
number++;
conditionB.signal();
conditionA.await();
}
if (number % 3 == 1) {
System.out.println(Thread.currentThread().getName() + " - " + number);
number++;
conditionC.signal();
conditionB.await();
}
if (number % 3 == 2) {
System.out.println(Thread.currentThread().getName() + " - " + number);
number++;
conditionA.signal();
conditionC.await();
}
}
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
rl.unlock();
}
}
}
public static void main(String[] args) {
final MyTask myTask = new MyTask();
new Thread(new Runnable() {
@Override
public void run() {
myTask.execute();
}
}, "A").start();
Thread.sleep(1000);
new Thread(new Runnable() {
@Override
public void run() {
myTask.execute();
}
}, "B").start();
Thread.sleep(1000);
new Thread(new Runnable() {
@Override
public void run() {
myTask.execute();
}
}, "C").start();
}
}
说明:通过余数判断来执行,需要循环30次,有点不合理。想了下,执行10次,循环10次,但是让线程按照ABC执行就可以了。根据这个想法写出了改进版。
改进版实现
/**
* @description A\B\C三个线程顺序执行10次
*
* @author sunpy
* @date 2018年11月28日 下午2:23:45
*/
public class MyTest {
static class MyTask {
private static ReentrantLock rl = new ReentrantLock();
private static Condition conditionA = rl.newCondition();
private static Condition conditionB = rl.newCondition();
private static Condition conditionC = rl.newCondition();
public void execute(String flag) {
rl.lock();
try {
for (int i = 1 ; i <= 10 ; i++) {
if ("A".equals(flag)) {
System.out.println(Thread.currentThread().getName() + " - " + i);
conditionB.signal();
conditionA.await();
}
if ("B".equals(flag)) {
System.out.println(Thread.currentThread().getName() + " - " + i);
conditionC.signal();
conditionB.await();
}
if ("C".equals(flag)) {
System.out.println(Thread.currentThread().getName() + " - " + i);
conditionA.signal();
conditionC.await();
}
}
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
rl.unlock();
}
}
}
}
Thread.currentThread.getName和this.getName的区别
this.getName
this的意思是代表当前对象的。而this在线程的环境下,代表的是当前线程实例对象本身。所以this.getName是当前线程实例对象的线程名称是什么。
Thread.currentThread.getName
Thread.currentThread.getName意思是在当前代码块中执行的线程名称是什么。
例子
public class CountOperate extends Thread {
public CountOperate() {
System.out.println("CountOperate---begin");
System.out.println("Thread.currentThread().getName()=" + Thread.currentThread().getName());
System.out.println("Thread.currentThread().isAlive()=" + Thread.currentThread().isAlive());
System.out.println("this.getName()=" + this.getName());
System.out.println("this.isAlive()=" + this.isAlive());
System.out.println("CountOperate---end");
}
@Override
public void run() {
System.out.println("run---begin");
System.out.println("Thread.currentThread().getName()=" + Thread.currentThread().getName());
System.out.println("Thread.currentThread().isAlive()=" + Thread.currentThread().isAlive());
System.out.println("this.getName()=" + this.getName());
System.out.println("this.isAlive()=" + this.isAlive());
System.out.println("run---end");
}
}
public class Test {
public static void main(String[] args) {
CountOperate c = new CountOperate();
Thread t1 = new Thread(c);
System.out.println("main begin t1 isAlive=" + t1.isAlive());
t1.start();
System.out.println("main end t1 isAlive=" + t1.isAlive());
}
}
结果:
CountOperate---begin
Thread.currentThread().getName()=main
Thread.currentThread().isAlive()=true
this.getName()=Thread-0
this.isAlive()=false
CountOperate---end
main begin t1 isAlive=false
main end t1 isAlive=true
run---begin
Thread.currentThread().getName()=Thread-1
Thread.currentThread().isAlive()=true
this.getName()=Thread-0
this.isAlive()=false
run---end
说明:
① 首先搞明白该例子中一共有三条线程:
main线程:执行main方法的线程。
Thread-0线程:当前CountOperate线程实例的线程。
Thread-1线程:new Thread类创建的线程。
② CountOperate初始化代码说明:
CountOperate---begin
Thread.currentThread().getName()=main
Thread.currentThread().isAlive()=true
this.getName()=Thread-0
this.isAlive()=false
CountOperate---end
执行构造器,然后当前main方法中执行初始化CountOperate类,所以当前线程的名称是main,状态是true。而当前CountOperate线程实例为Thread-0,没有启动所以状态为false。
③ Thread1线程说明:
main begin t1 isAlive=false
main end t1 isAlive=true
Thread-1线程的线程执行器没有启动,那么状态为false。执行start方法,Thread-1线程的线程执行器启动,那么状态就为true了。
④ run方法执行说明:
run---begin
Thread.currentThread().getName()=Thread-1
Thread.currentThread().isAlive()=true
this.getName()=Thread-0
this.isAlive()=false
run---end
首先执行run方法的线程为Thread-1,所以当前代码块中的线程为Thread1(Thread.currentThread().getName()=Thread-1),状态自然为true。
但是this.getName()是代表当前线程Thread-0的实例,因为CountOperate线程根本没有启动,所以状态为false。