问题:java中两个线程交替执行,一个输出奇数,一个输出偶数
方法一:
看到题目时,我第一个想到的是用synchronized加锁,然后使用notify唤醒线程和wait阻塞线程
通过 synchronized 锁定对象,每次只能有一个线程进入,每打印一个数,就释放锁,另一个线程进入,拿到锁,打印,唤醒另一个线程,然后挂起自己。循环反复,实现了一个最基本的打印功能。
public class Text1 {
private static int state=1;
public static void main(String[] args) {
final Text1 text1=new Text1();
new Thread(new Runnable() {
public void run() {
synchronized (text1) {
while(state!=1){//当state!=1是,说明不该线程1打印,则将线程1挂起,让线程2执行打印
try {
text1.wait();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
for(int i=1;i<=100;i=i+2){
System.out.println(i);//当线程1打印之后,该线程2打印了,此时将状态state置为2
state=2; //唤醒线程2,并使用wait交出锁资源,线程2执行打印
try {
text1.notify();
text1.wait();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
//text1.notify();
}
}
}).start();
new Thread(new Runnable() {
public void run() {
synchronized (text1) {
while(state!=2){//当state!=2,说明不该线程2执行,则将线程2挂起,让线程1执行打印
try {
text1.wait();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
for(int i=2;i<=100;i=i+2){
System.out.println(i);//线程2打印完成之后将state置为1,将线程1唤醒,并使用wait交出锁
state=1;
try {
text1.notify();
text1.wait();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
text1.notify();
}
}
}).start();
}
}
方法二:
自旋锁的方式,就是我们没有用锁,相当于自己写了一个自旋锁,利用CAS原理
public class Text1 {
static volatile int state=1;
public static void main(String[] args) {
new Thread(new Runnable() {
public void run() {
for(int i=1;i<=100;i=i+2){
while(state!=1){//当state!=1时,说明此时不该线程1打印,则陷入循环等待线程2将state改为1后立即执行打印
}
System.out.println(i);
state=2;
}
}
}).start();
new Thread(new Runnable() {
public void run() {
for(int i=2;i<=100;i=i+2){
while(state!=2){//当state!=2时,说明此时不该线程2打印,则陷入循环等待线程1执行完打印之后将state该为2(volatial可见性),再执行打印
}
System.out.println(i);
state=1;
}
}
}).start();
}
}
方法三:
使用LockSupport,LockSupport是一个比较底层的工具类,用来创建锁和其他底层工具类的基本线程阻塞原语
LockSupport的特点:
-
不需要synchronized加锁就可以实现线程阻塞和唤醒。
-
LockSupport的unpark()可以先与park()运行,并且线程不会阻塞。
unpark() 唤醒线程 park() 阻塞线程 -
如果一个线程处于等待状态,连续调用了两次park()方法,就会是线程永远无法被唤醒
public class Text1 {
static Thread t1=null;
static Thread t2=null;
public static void main(String[] args) {
t1=new Thread(new Runnable() {
public void run() {
for(int i=1;i<=100;i=i+2){
System.out.println(i);
LockSupport.unpark(t2);//t1打印完唤醒t2
LockSupport.park();//t1阻塞
}
}
},"t1");
t2=new Thread(new Runnable() {
public void run() {
for(int i=2;i<=100;i=i+2){
LockSupport.park(); //阻塞t2
System.out.println(i);
LockSupport.unpark(t1);
}
}
},"t2");
t1.start();
t2.start();
}
}
方法四:
使用Lock和Condition类来完成,Condition是Java提供了来实现等待/通知的类,Condition对象是由lock对象所创建的,但是同一个锁可以创建多个Condition的对象,即创建多个对象监视器。这样的好处就是可以指定唤醒线程。notify或notifyALL唤醒的线程是随机唤醒一个。
public class Text1 {
static Lock lock=new ReentrantLock();
static Condition conditionT1=lock.newCondition();
static Condition conditionT2=lock.newCondition();
public static void main(String[] args) {
new Thread(new Runnable() {
public void run() {
try {
lock.lock();
for(int i=1;i<=100;i=i+2){
System.out.println(i);
conditionT2.signal();
conditionT1.await();
}
conditionT1.signal();
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}finally {
lock.unlock();
}
}
}).start();
new Thread(new Runnable() {
public void run() {
try {
lock.lock();
for(int i=2;i<=100;i=i+2){
System.out.println(i);
conditionT1.signal();
conditionT2.await();
}
conditionT2.signal();
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}finally {
lock.unlock();
}
}
}).start();
}
}
**扩展:**编写一个程序,开启 3 个线程,每个线程分别打印A,B,C,要求线程每次打印都按照A–>B–>C的顺序来打印
如:ABCABCABC…… 依次递归
class ShareDate{
static int num=1;//标志位,t1:1,t2:2,t3:3
//获得lock锁
static Lock lock=new ReentrantLock();
//创建三个condition对象用来await(阻塞)和signal(唤醒)指定的线程
private Condition c1=lock.newCondition();
private Condition c2=lock.newCondition();
private Condition c3=lock.newCondition();
public void printA(){
lock.lock();
try {
//判断
while(num!=1){
c1.await();
}
//干活
System.out.println(Thread.currentThread().getName()+"-A");
//通知
num=2;//先修改标志位
c2.signal();//唤醒第二个线程
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}finally {
lock.unlock();
}
System.out.println();
}
public void printB(){
lock.lock();
try {
while(num!=2){
c2.await();
}
System.out.println(Thread.currentThread().getName()+"-B");
num=3;
c3.signal();
} catch (Exception e) {
// TODO: handle exception
}finally{
lock.unlock();
}
}
public void printC(){
lock.lock();
try {
while(num!=3){
c3.await();
}
System.out.println(Thread.currentThread().getName()+"-C");
num=1;
c1.signal();
} catch (Exception e) {
// TODO: handle exception
}finally{
lock.unlock();
}
}
}
public class Text1 {
public static void main(String[] args) {
final ShareDate shareDate=new ShareDate();
new Thread(new Runnable() {
public void run() {
for(int i=1;i<=10;i++){
shareDate.printA();
}
}
}).start();
new Thread(new Runnable() {
public void run() {
for(int i=1;i<=10;i++){
shareDate.printB();
}
}
}).start();
new Thread(new Runnable() {
public void run() {
for(int i=1;i<=10;i++){
shareDate.printC();
}
}
}).start();
}
}