**
两个线程交替执行
**
最近看到一个面试题,两个线程交替执行10次,线程1输出10次,线程2输出5次,反复交替15次。
方法一
一个主线程一个子线程,子线程运行在主线程内部,交替执行分别输出10次和5次,交替15次
package com.example.xssDemo.xss.util;
/**
* @author liaoc
*/
public class ThreadPractice {
public static void main(String[] args) {
Do ado = new Do();
new Thread(new Runnable() {
@Override
public void run() {
for (int i = 1; i <= 15; i++) {
ado.sub(i);
}
}
}).start();
for (int j = 1; j <= 15; j++) {
ado.main(j);
System.out.println("===========第" + j + "轮完成===================");
}
System.out.println("===================================");
}
static class Do {
private boolean isSun = true;
//isSun为true执行该方法
public synchronized void sub(int i) {
if (!isSun) {
try {
//wait()使当前线程阻塞,前提是 必须先获得锁,一般配合synchronized 关键字使用
//当线程执行wait()方法时候,会释放当前的锁,然后让出CPU,进入等待状态
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
for (int j = 1; j <= 10; j++) {
System.out.println("子线程:" + Thread.currentThread().getName() + "," +
i + "趟," + "第:" + j + "个");
}
isSun = false;
/**
* notify方法只唤醒一个等待(对象的)线程并使该线程开始执行。
* 所以如果有多个线程等待一个对象,这个方法只会唤醒其中一个线程,
* 选择哪个线程取决于操作系统对多线程管理的实现。
* notifyAll 会唤醒所有等待(对象的)线程,尽管哪一个线程将会第一个处理取决于操作系统的实现。
* 如果当前情况下有多个线程需要被唤醒,推荐使用notifyAll 方法
*/
this.notify();
}
//isSun为false执行该方法
public synchronized void main(int i) {
if (isSun) {
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
for (int j = 1; j <= 5; j++) {
System.out.println("主线程:" + Thread.currentThread().getName() + "," + i
+ "趟," + j + "个");
}
isSun = true;
this.notify();
}
}
}
方法二
两个并列的子线程,交替执行分别输出10次和5次,交替15次。
package com.example.xssDemo.xss.util;
/**
* @author liaoc
*/
public class BrotherThread {
public static void main(String[] args) {
Dto dto = new Dto();
/**
* 两个并列线程交替执行,输出10次,供交替20次
*/
new Thread(new Runnable() {
@Override
public void run() {
for (int i = 1; i <= 15; i++) {
dto.sub1(i);
System.out.println("我是:" + Thread.currentThread().getName() + "第" + i + "趟");
}
}
}).start();
new Thread(new Runnable() {
@Override
public void run() {
for (int j = 1; j <= 15; j++) {
dto.sub2(j);
System.out.println("我是:" + Thread.currentThread().getName() + "第" + j + "趟");
}
}
}).start();
}
static class Dto {
private boolean isSun = true;
//isSun为true执行
public synchronized void sub1(int i) {
if (!isSun) {
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
for (int j = 1; j < 10; j++) {
System.out.println(Thread.currentThread().getName() + "的第:" + i + "趟的第" + j + "个");
}
isSun = false;
this.notify();
}
//isSun为false执行
public synchronized void sub2(int i) {
if (isSun) {
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
for (int j = 1; j <= 5; j++) {
System.out.println(Thread.currentThread().getName() + "的第:" + i + "趟的第" + j + "个");
}
isSun = true;
this.notify();
}
}
}
顺便复习一下线程的常用关键字:
1.start()和run()
start()方法是是开启线程的方法,这个方法执行后,一个线程才真正的进入RUNNABLE状态。run()方法是线程中具体执行的业务活动,一般都要重写run()方法
2.stop()方法在结束线程时,会直接终止线程,并立即释放这个线程所持有的锁
3.interrupt()方法是一个实例方法,它通知目标线程中断,也就是设置中断标志位
4.sleep()方法是让当前线程休眠若干时间,它会抛出一个InterruptedException中断异常,sleep()方法不会释放任何对象的锁资源
5.wait()等待方法,是Object类中的方法,当一个对象实例上调用wait()方法后,当前线程就会在这个对象上等待。那么等待什么时候结束呢?线程一直等到其他线程调用了这个对象的notify()方法为止。这样实例对象就成了多个线程间的通信手段。wait()和notify()不能随便调用,它必须包含在对应的synchronize语句中,这俩方法,都需要首先获得目标对象的一个监听器,而wait()和notify()方法在执行后会释放这个监听器。wait()方法会释放目标对象的锁。
6. join()方法表示无限的等待,他会一直阻塞当前线程,只到目标线程执行完毕。join(long millis) 给出了一个最大等待时间,如果超过给定的时间目标线程还在执行,当前线程就不等了,继续往下执行。
7. yeild()方法是个静态方法,一旦执行,他会使当前线程让出CPU。让出CPU不代表当前线程不执行了,还会进行CPU资源的争夺。如果一个线程不重要或优先级比较低,可以调用这个方法,把资源给重要的线程去做。
8. volatile:
并发编程的三个重要特征:原子性、可见性、有序性
原子性:是指在一次或多次操作中,要么所有的操作全部都执行并不会受任何因素的干扰而中断,要么所有的操作都不执行。
可见性:当一个线程对共享变量修改后,其他线程可立即看到最新的值。
有序性:所谓有序性是指程序代码在执行过程中的先后顺序,由于java编译和运行期的优化,会使得代码的执行顺序未必是开发者编写代码的顺序