01.Java基础(多线程回顾,对比Linux多线程)

两个线程间通信,实现交替打印

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
...
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 217,734评论 6 505
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,931评论 3 394
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 164,133评论 0 354
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,532评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,585评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,462评论 1 302
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,262评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,153评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,587评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,792评论 3 336
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,919评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,635评论 5 345
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,237评论 3 329
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,855评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,983评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,048评论 3 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,864评论 2 354

推荐阅读更多精彩内容

  • 该文章转自:http://blog.csdn.net/evankaka/article/details/44153...
    加来依蓝阅读 7,353评论 3 87
  • Java多线程学习 [-] 一扩展javalangThread类 二实现javalangRunnable接口 三T...
    影驰阅读 2,957评论 1 18
  • 本文主要讲了java中多线程的使用方法、线程同步、线程数据传递、线程状态及相应的一些线程函数用法、概述等。 首先讲...
    李欣阳阅读 2,454评论 1 15
  • 写在前面的话: 这篇博客是我从这里“转载”的,为什么转载两个字加“”呢?因为这绝不是简单的复制粘贴,我花了五六个小...
    SmartSean阅读 4,730评论 12 45
  • 前几天给高管培训的时候,专门写过使命、愿景和价值观的东西。 今天看到一篇好文,赶紧收集了,再强化一下。 01 使命...
    甲坤阅读 6,970评论 0 7