用两个线程,一个输出字母,一个输出数字,交替输出1A2B3C4D…26Z。
实现逻辑:使用控制方法,控制两个线程交替按顺序打印。
1.synchronized+wait+notify:依赖对象的锁的原理,wait将线程放入等待池,notify唤醒锁资源竞争的线程放入锁池,在对象锁池的才有资格竞争锁资源。
static int num = 1;//从1开始
static char word = 'A';//从A开始
public static void main(String[] args) {
//对象锁
Object o = new Object();
new Thread(() -> {
synchronized (o){
while (num<27){
System.out.print(num);
num ++;
try {
//唤醒另一线程
o.notify();
//阻塞本线程
o.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//为了线程运行结束 不加 则最后执行的是o.wait(); 一直在等待池
o.notify();
}
}).start();
new Thread(() -> {
synchronized (o){
while ('Z' >= word){
try {
System.out.print(word);
word = (char)(word+1);
//唤醒另一线程
o.notify();
//阻塞本线程
o.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//为了线程运行结束 不加 则最后执行的是o.wait(); 一直在等待池
o.notify();
}
}).start();
}
2.ReentrantLock+Condition :依赖等待队列,条件队列,ReentrantLock维护等待队列,Condition维护条件队列,await将线程放入条件队列,signal方法从条件队列获取一个线程放入等待队列,只有等待队列的线程才有资格竞争锁资源。
static int num = 1;
static char word = 'A';
public static void main(String[] args) {
Lock lock = new ReentrantLock();
//创建两个 Condition(条件队列) 管理 两个线程
Condition t1 = lock.newCondition();
Condition t2 = lock.newCondition();
new Thread(() -> {
try {
//加锁
lock.lock();
while (num<27){
System.out.print(num);
num ++;
//唤醒另一线程
t2.signal();
//阻塞当前线程
t1.await();
}
//为了线程运行结束 不加 另一线程一直在条件队列
t2.signal();
}catch (InterruptedException e){
e.printStackTrace();
}finally {
//释放锁
lock.unlock();
}
}).start();
new Thread(() -> {
try {
lock.lock();
while ('Z' >= word){
System.out.print(word);
word = (char)(word+1);
//唤醒另一线程
t1.signal();
//阻塞当前线程
t2.await();
}
//为了线程运行结束 不加 另一线程一直在条件队列
t1.signal();
}catch (InterruptedException e){
e.printStackTrace();
}finally {
//释放锁
lock.unlock();
}
}).start();
}
3.无锁+自旋+volatile:依赖volatile的线程可见性(MESI、读写屏障),自旋根据标志值执行打印。
static int num = 1;
static char word = 'A';
//创建线程可见标志
static volatile boolean flag = true;
public static void main(String[] args) {
Object o = new Object();
new Thread(() -> {
//外层打印次数
while (num<27){
//内层flag为true 打印数字
while(flag){
System.out.print(num);
num ++;
//更新 flag 值为 false 另一线程打印
flag = false;
}
}
}).start();
new Thread(() -> {
//外层打印次数
while ('Z' >= word){
//内层flag为false打印字母
while(!flag){
System.out.print(word);
word = (char)(word+1);
//更新 flag 值为 true 另一线程打印
flag = true;
}
}
}).start();
}
4.LockSupport:基于UNSAFE类的操作,可指定唤醒线程。
static int num = 1;
static char word = 'A';
//声明两个线程
static Thread t1 , t2;
public static void main(String[] args) {
t1 = new Thread(() -> {
while (num<27){
System.out.print(num);
num ++;
//唤醒t2线程
LockSupport.unpark(t2);
//阻塞当前线程
LockSupport.park();
}
});
t2 = new Thread(() -> {
while ('Z' >= word){
//阻塞当前线程 保证数字先输出
LockSupport.park();
System.out.print(word);
word = (char)(word+1);
//唤醒t1线程
LockSupport.unpark(t1);
}
});
t1.start();
t2.start();
}
5.ArrayBlockingQueue:基于阻塞队列容量原理,入队时容量满阻塞,出队时容量为0阻塞。
static int num = 1;
static char word = 'A';
public static void main(String[] args) {
//两容量为1的阻塞队列 结合使用控制两个线程
BlockingQueue t1 = new ArrayBlockingQueue(1);
BlockingQueue t2 = new ArrayBlockingQueue(1);
new Thread(() -> {
while (num<27){
try {
//保证数字先打印
System.out.print(num);
num ++;
//入队 如果超过容量阻塞当前线程
t1.put("t1");
//出队 如果容量为空阻塞当前线程
t2.take();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}).start();
new Thread(() -> {
while ('Z' >= word){
try {
//出队 如果容量为空阻塞当前线程 刚开始执行肯定阻塞 因为没入队
t1.take();
System.out.print(word);
word = (char)(word+1);
//入队 如果超过容量阻塞当前线程
t2.put("t2");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}).start();
}
6.SynchronousQueue: 依赖无容量原理,take和put必须成对出现,单独put阻塞,单独take阻塞,put之后阻塞 take后 唤醒。
static int num = 1;
static char word = 'A';
public static void main(String[] args) {
SynchronousQueue print = new SynchronousQueue();
new Thread(() -> {
while (num<27){
try {
//将数字入队 然后阻塞 等待 出队打印 唤醒
print.put(num);
num ++;
//出队打印 唤醒 另一线程
System.out.print(print.take());
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}).start();
new Thread(() -> {
while ('Z' >= word){
try {
//出队打印 唤醒 另一线程
System.out.print(print.take());
//将字符入队 然后阻塞 等待 出队打印 唤醒
print.put(word);
word = (char)(word+1);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}).start();
}
7.TransferQueue:依赖transfer方法入队元素,必须出队,当前线程才可继续执行。
static int num = 1;
static char word = 'A';
public static void main(String[] args) {
TransferQueue print = new LinkedTransferQueue();
new Thread(() -> {
while (num<27){
try {
//将数字入队 然后阻塞 等待 出队打印 唤醒
print.transfer(num);
num ++;
//出队打印 唤醒 另一线程
System.out.print(print.take());
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}).start();
new Thread(() -> {
while ('Z' >= word){
try {
//出队打印 唤醒 另一线程
System.out.print(print.take());
//将字符入队 然后阻塞 等待 出队打印 唤醒
print.transfer(word);
word = (char)(word+1);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}).start();
}