两个线程间通信,实现交替打印
public class Thread1 {
public static void main(String[] args) {
final Printer printer = new Printer();
new Thread(new Runnable() {
@Override
public void run() {
while(true){
try {
printer.print1();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}).start();
new Thread(new Runnable() {
@Override
public void run() {
while(true){
try {
printer.print2();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}).start();
}
}
/**
* 等待唤醒机制,两个线程间通信
* @author renzhenming
*
*/
class Printer{
private int flag = 1;
public void print1() throws InterruptedException{
synchronized(this){
if (flag !=1) {
/**
* obj.wait():该方法的调用,使得调用该方法的执行线程(T1)放弃obj的对象锁并阻塞,
* 直到别的线程调用了obj的notifyAll方法、或者别的线程调用了obj的notify方法且JVM选择唤醒(T1),
* 被唤醒的线程(T1)依旧阻塞在wait方法中,与其它的线程一起争夺obj的对象锁,
* 直到它再次获得了obj的对象锁之后,才能从wait方法中返回。
* (除了notify方法,wait还有带有时间参数的版本,在等待了超过所设时间之后,
* T1线程一样会被唤醒,进入到争夺obj对象锁的行列;另外中断可以直接跳出wait方法)
*
* 1.在同步代码块中,用哪个对象锁就用哪个对象调用wait方法,
* 2.为什么wait和notify这些方法要定义在Object类中?因为锁对象可以是任意对象,那么任意对象的类都是Object的子类,Object是任意类的基类,所以将方法定义在Object这个类中就会让任意对象对其进行调用
* sleep和wait方法的区别:a.sleep在同步代码块或同步函数中,不释放锁,(睡着了也会抱着锁睡),wait相反会释放锁 b.sleep方法必须传入参数,参数就是时间,时间到了自动醒来,wait方法可以传入时间参数,也可以不传入时间参数,如果给wait方法传入时间参数,用法与sleep相似,时间到了就停止等待(通常都是用没有参数的wait方法)
*/
this.wait();
}
System.out.print("y");
System.out.print("i");
System.out.print("n");
System.out.print("g");
System.out.print("z");
System.out.print("i");
System.out.print("\r\n");
flag = 2;
/**
* obj.notify():该方法的调用,会从所有正在等待obj对象锁的线程中,唤醒其中的一个(选择算法依赖于不同实现),
* 被唤醒的线程此时加入到了obj对象锁的争夺之中,然而该notify方法的执行线程此时并未释放obj的对象锁,
* 而是离开synchronized代码块时释放。因此在notify方法之后,synchronized代码块结束之前,
* 所有其他被唤醒的,等待obj对象锁的线程依旧被阻塞。
*/
this.notify();
}
}
public void print2() throws InterruptedException{
synchronized (this) {
if (flag != 2) {
wait();
}
System.out.print("z");
System.out.print("h");
System.out.print("e");
System.out.print("n");
System.out.print("m");
System.out.print("i");
System.out.print("n");
System.out.print("g");
System.out.print("\r\n");
flag = 1;
notify();
}
}
}
打印结果
yingzi
zhenming
yingzi
zhenming
yingzi
zhenming
yingzi
zhenming
yingzi
zhenming
yingzi
zhenming
yingzi
zhenming
yingzi
zhenming
yingzi
zhenming
...
三个线程或三个以上通信(试错,期待的结果是逐条打印,实际结果看下边)
按照上边的方式,代码如下:
public class Thread1 {
public static void main(String[] args) {
final Printer printer = new Printer();
new Thread(new Runnable() {
@Override
public void run() {
while(true){
try {
printer.print1();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}).start();
new Thread(new Runnable() {
@Override
public void run() {
while(true){
try {
printer.print2();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}).start();
new Thread(new Runnable() {
@Override
public void run() {
while(true){
try {
printer.print3();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}).start();
}
}
/**
* 等待唤醒机制,两个线程间通信
* @author renzhenming
*
*/
class Printer{
private int flag = 1;
public void print1() throws InterruptedException{
synchronized(this){
if (flag !=1) {
/**
* obj.wait():该方法的调用,使得调用该方法的执行线程(T1)放弃obj的对象锁并阻塞,
* 直到别的线程调用了obj的notifyAll方法、或者别的线程调用了obj的notify方法且JVM选择唤醒(T1),
* 被唤醒的线程(T1)依旧阻塞在wait方法中,与其它的线程一起争夺obj的对象锁,
* 直到它再次获得了obj的对象锁之后,才能从wait方法中返回。
* (除了notify方法,wait还有带有时间参数的版本,在等待了超过所设时间之后,
* T1线程一样会被唤醒,进入到争夺obj对象锁的行列;另外中断可以直接跳出wait方法)
*/
this.wait();
}
System.out.print("y");
System.out.print("i");
System.out.print("n");
System.out.print("g");
System.out.print("z");
System.out.print("i");
System.out.print("\r\n");
flag = 2;
/**
* obj.notify():该方法的调用,会从所有正在等待obj对象锁的线程中,唤醒其中的一个(选择算法依赖于不同实现),
* 被唤醒的线程此时加入到了obj对象锁的争夺之中,然而该notify方法的执行线程此时并未释放obj的对象锁,
* 而是离开synchronized代码块时释放。因此在notify方法之后,synchronized代码块结束之前,
* 所有其他被唤醒的,等待obj对象锁的线程依旧被阻塞。
*/
this.notify();
}
}
public void print2() throws InterruptedException{
synchronized (this) {
if (flag != 2) {
wait();
}
System.out.print("z");
System.out.print("h");
System.out.print("e");
System.out.print("n");
System.out.print("m");
System.out.print("i");
System.out.print("n");
System.out.print("g");
System.out.print("\r\n");
flag = 3;
notify();
}
}
public void print3() throws InterruptedException{
synchronized (this) {
if (flag != 3) {
wait();
}
System.out.print("f");
System.out.print("a");
System.out.print("l");
System.out.print("l");
System.out.print("i");
System.out.print("n");
System.out.print("l");
System.out.print("o");
System.out.print("v");
System.out.print("e");
System.out.print("\r\n");
flag = 1;
notify();
}
}
}
结果:
zhenming
yingzi
zhenming
yingzi
zhenming
yingzi
zhenming
yingzi
zhenming
yingzi
zhenming
yingzi
zhenming
fallinlove
zhenming
...
为什么会出现上边的情况?我们极端分析一下,假设,第一个回合中,首先执行的是第2和第3个线程,也就是print2 print3方法,此时,由于flag不满足执行条件,两个线程都进入了等待状态,然后执行print1打印了一条,设置flag=2,然后notify,此时通常疏忽的开发人员可能会想当然的认为,flag=2 了,那么理所当然应该唤醒线程2了,应该打印print2了,事实上并非如此,由于notify是随机唤醒一个线程,那么它可能唤醒2 也可能唤醒3,假设此时被唤醒的线程是3,由于if语句的特性,不会重新判断执行条件,而是在哪里等待,在哪里醒来,于是直接跳过了判断语句执行了print3的打印内容,所以就出现了这种情况
针对这种问题,我们尝试用while代替if做条件判断,while是循环判断,每次判断都会判断标记,我们对程序做修改后如下:
public class Thread1 {
public static void main(String[] args) {
final Printer printer = new Printer();
new Thread(new Runnable() {
@Override
public void run() {
while(true){
try {
printer.print1();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}).start();
new Thread(new Runnable() {
@Override
public void run() {
while(true){
try {
printer.print2();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}).start();
new Thread(new Runnable() {
@Override
public void run() {
while(true){
try {
printer.print3();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}).start();
}
}
/**
* 等待唤醒机制,两个线程间通信
* @author renzhenming
*
*/
class Printer{
private int flag = 1;
public void print1() throws InterruptedException{
synchronized(this){
while (flag !=1) {
/**
* obj.wait():该方法的调用,使得调用该方法的执行线程(T1)放弃obj的对象锁并阻塞,
* 直到别的线程调用了obj的notifyAll方法、或者别的线程调用了obj的notify方法且JVM选择唤醒(T1),
* 被唤醒的线程(T1)依旧阻塞在wait方法中,与其它的线程一起争夺obj的对象锁,
* 直到它再次获得了obj的对象锁之后,才能从wait方法中返回。
* (除了notify方法,wait还有带有时间参数的版本,在等待了超过所设时间之后,
* T1线程一样会被唤醒,进入到争夺obj对象锁的行列;另外中断可以直接跳出wait方法)
*/
this.wait();
}
System.out.print("y");
System.out.print("i");
System.out.print("n");
System.out.print("g");
System.out.print("z");
System.out.print("i");
System.out.print("\r\n");
flag = 2;
/**
* obj.notify():该方法的调用,会从所有正在等待obj对象锁的线程中,唤醒其中的一个(选择算法依赖于不同实现),
* 被唤醒的线程此时加入到了obj对象锁的争夺之中,然而该notify方法的执行线程此时并未释放obj的对象锁,
* 而是离开synchronized代码块时释放。因此在notify方法之后,synchronized代码块结束之前,
* 所有其他被唤醒的,等待obj对象锁的线程依旧被阻塞。
*/
this.notify();
}
}
public void print2() throws InterruptedException{
synchronized (this) {
while (flag != 2) {
wait();
}
System.out.print("z");
System.out.print("h");
System.out.print("e");
System.out.print("n");
System.out.print("m");
System.out.print("i");
System.out.print("n");
System.out.print("g");
System.out.print("\r\n");
flag = 3;
notify();
}
}
public void print3() throws InterruptedException{
synchronized (this) {
while (flag != 3) {
wait();
}
System.out.print("f");
System.out.print("a");
System.out.print("l");
System.out.print("l");
System.out.print("i");
System.out.print("n");
System.out.print("l");
System.out.print("o");
System.out.print("v");
System.out.print("e");
System.out.print("\r\n");
flag = 1;
notify();
}
}
}
更离谱的问题出现,打印结果:
yingzi
zhenming
fallinlove
三个线程在执行一次之后就全部停了,或者说全部进入了等待状态,为什么会出现这一现象?
假设程序执行开始,先执行了线程2线程3,由于flag条件不满足,两个线程都进入等待状态,此时线程1开始执行,设置flag=2,执行完毕notify唤醒一个线程,并且由于自身条件也陷入等待状态,假设先唤醒的是线程3,由于flag值不对,线程3仍然处于等待状态,这种情况下,三个线程全部陷入等待状态
notify随机唤醒一个线程,是导致这一问题出现的原因,那么可否唤醒全部线程呢,
notifyall就出现了,notifyAll可以唤醒全部线程,然后根据条件的判断执行相应的线程,但是这样做是由弊端的,那就是每次都要将所有线程唤醒,这是JDK1.5之前的处理办法,JDK1.5有新的方法解决这一问题
public class Thread1 {
public static void main(String[] args) {
final Printer printer = new Printer();
new Thread(new Runnable() {
@Override
public void run() {
while(true){
try {
printer.print1();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}).start();
new Thread(new Runnable() {
@Override
public void run() {
while(true){
try {
printer.print2();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}).start();
new Thread(new Runnable() {
@Override
public void run() {
while(true){
try {
printer.print3();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}).start();
}
}
/**
* 等待唤醒机制,两个线程间通信
* @author renzhenming
*
*/
class Printer{
private int flag = 1;
public void print1() throws InterruptedException{
synchronized(this){
while (flag !=1) {
/**
* obj.wait():该方法的调用,使得调用该方法的执行线程(T1)放弃obj的对象锁并阻塞,
* 直到别的线程调用了obj的notifyAll方法、或者别的线程调用了obj的notify方法且JVM选择唤醒(T1),
* 被唤醒的线程(T1)依旧阻塞在wait方法中,与其它的线程一起争夺obj的对象锁,
* 直到它再次获得了obj的对象锁之后,才能从wait方法中返回。
* (除了notify方法,wait还有带有时间参数的版本,在等待了超过所设时间之后,
* T1线程一样会被唤醒,进入到争夺obj对象锁的行列;另外中断可以直接跳出wait方法)
*/
this.wait();
}
System.out.print("y");
System.out.print("i");
System.out.print("n");
System.out.print("g");
System.out.print("z");
System.out.print("i");
System.out.print("\r\n");
flag = 2;
/**
* obj.notify():该方法的调用,会从所有正在等待obj对象锁的线程中,唤醒其中的一个(选择算法依赖于不同实现),
* 被唤醒的线程此时加入到了obj对象锁的争夺之中,然而该notify方法的执行线程此时并未释放obj的对象锁,
* 而是离开synchronized代码块时释放。因此在notify方法之后,synchronized代码块结束之前,
* 所有其他被唤醒的,等待obj对象锁的线程依旧被阻塞。
*/
this.notifyAll();
}
}
public void print2() throws InterruptedException{
synchronized (this) {
while (flag != 2) {
wait();
}
System.out.print("z");
System.out.print("h");
System.out.print("e");
System.out.print("n");
System.out.print("m");
System.out.print("i");
System.out.print("n");
System.out.print("g");
System.out.print("\r\n");
flag = 3;
notifyAll();
}
}
public void print3() throws InterruptedException{
synchronized (this) {
while (flag != 3) {
wait();
}
System.out.print("f");
System.out.print("a");
System.out.print("l");
System.out.print("l");
System.out.print("i");
System.out.print("n");
System.out.print("l");
System.out.print("o");
System.out.print("v");
System.out.print("e");
System.out.print("\r\n");
flag = 1;
notifyAll();
}
}
}
结果:
yingzi
zhenming
fallinlove
yingzi
zhenming
fallinlove
yingzi
zhenming
fallinlove
yingzi
zhenming
fallinlove
yingzi
zhenming
fallinlove
...
JDK1.5互斥锁
所谓互斥锁, 指的是一次最多只能有一个线程持有的锁. 在jdk1.5之前, 我们通常使用synchronized机制控制多个线程对共享资源的访问. 而现在, Lock提供了比synchronized机制更广泛的锁定操作, Lock和synchronized机制的主要区别:
synchronized机制提供了对与每个对象相关的隐式监视器锁的访问, 并强制所有锁获取和释放均要出现在一个块结构中, 当获取了多个锁时, 它们必须以相反的顺序释放. synchronized机制对锁的释放是隐式的, 只要线程运行的代码超出了synchronized语句块范围, 锁就会被释放. 而Lock机制必须显式的调用Lock对象的unlock()方法才能释放锁, 这为获取锁和释放锁不出现在同一个块结构中, 以及以更自由的顺序释放锁提供了可能。
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;
public class Thread1 {
public static void main(String[] args) {
final Printer printer = new Printer();
new Thread(new Runnable() {
@Override
public void run() {
while (true) {
try {
printer.print1();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}).start();
new Thread(new Runnable() {
@Override
public void run() {
while (true) {
try {
printer.print2();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}).start();
new Thread(new Runnable() {
@Override
public void run() {
while (true) {
try {
printer.print3();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}).start();
}
}
/**
* 等待唤醒机制,互斥锁
*
* @author renzhenming
*
*/
class Printer {
ReentrantLock r = new ReentrantLock();
Condition c1 = r.newCondition();
Condition c2 = r.newCondition();
Condition c3 = r.newCondition();
private int flag = 1;
public void print1() throws InterruptedException {
r.lock();
if (flag != 1) {
c1.await();
}
System.out.print("y");
System.out.print("i");
System.out.print("n");
System.out.print("g");
System.out.print("z");
System.out.print("i");
System.out.print("\r\n");
flag = 2;
c2.signal();
r.unlock();
}
public void print2() throws InterruptedException {
r.lock();
if (flag != 2) {
c2.await();
}
System.out.print("z");
System.out.print("h");
System.out.print("e");
System.out.print("n");
System.out.print("m");
System.out.print("i");
System.out.print("n");
System.out.print("g");
System.out.print("\r\n");
flag = 3;
c3.signal();
r.unlock();
}
public void print3() throws InterruptedException {
r.lock();
if (flag != 3) {
c3.await();
}
System.out.print("f");
System.out.print("a");
System.out.print("l");
System.out.print("l");
System.out.print("i");
System.out.print("n");
System.out.print("l");
System.out.print("o");
System.out.print("v");
System.out.print("e");
System.out.print("\r\n");
flag = 1;
c1.signal();
r.unlock();
}
}
结果,按照唤醒顺序执行线程,不再需要使用while判断,if即可
fallinlove
yingzi
zhenming
fallinlove
yingzi
zhenming
fallinlove
yingzi
zhenming
fallinlove
yingzi
zhenming
fallinlove
yingzi
zhenming
fallinlove
yingzi
zhenming
fallinlove
yingzi
zhenming
...