java的多线程学习,第二记

多线程带来便利的同时,也会带来麻烦

1,操作同一个共享资源的时候,会带来很多意想不到的麻烦。

就像卖票一样,如果不考虑多线程的环境,那肯定是没有问题的。

package secondSteps;

/**
 * 卖票
 * @author liugang
 * @create 2018/12/6 22:43
 **/
public class SellTicket implements Runnable {

    private Integer ticketCount = 100;

    @Override
    public void run() {
        try {
            while (ticketCount>0){
                sellTicket();
                Thread.sleep(1000);
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    private void sellTicket() {
        if (ticketCount>0){
            ticketCount--;
            System.out.println("线程的名称:"+Thread.currentThread().getName()+"卖票 还剩"+ticketCount+"张");
        }else{
            System.out.println("票卖完了");
        }
    }
    //如果不考虑多线程的环境,肯定不会出问题
}
public static void main(String[] args) {
        SellTicket ticket = new SellTicket();
        Thread t1 = new Thread(ticket,"窗口1");
        t1.start();
    }

运行的状况:票是一张一张的少的。

现在开四个线程卖票

public static void main(String[] args) {
        SellTicket ticket = new SellTicket();
        Thread t1 = new Thread(ticket,"窗口1");
        Thread t2 = new Thread(ticket,"窗口1");
        Thread t3 = new Thread(ticket,"窗口1");
        Thread t4 = new Thread(ticket,"窗口1");
        t1.start();
        t2.start();
        t3.start();
        t4.start();
    }

出现这种情况,是cpu高速切换,线程1,还没有执行完,在条件判断的时候,就切换到其他的线程。

多线程解决一个共享资源的占用问题是个头疼的问题

java提供了一定的解决办法。

操作共享资源的时候,不要竞争,一个一个来。比如,春节的时候在火车上厕所,一个人进去了把门锁了,方便之后开门,出来了。

给代码加个锁,就可以搞定这个问题。java提供了关键字synchronized,这个关键字代码块是要上锁的。那上什么锁呢,万物皆可superme

private Object obj = new Object();
private void sellTicket() {
        synchronized (obj){
            if (ticketCount>0){
                ticketCount--;
                System.out.println("线程的名称:"+Thread.currentThread().getName()+"卖票 还剩"+ticketCount+"张");
            }else{
                System.out.println("票卖完了");
            }
        }
    }

对象是有一个标志位的,当前锁定这个标志位,另外一个线程进来了,发现标志位没改变,就动不了。

synchronized (this)//同步代码块, 锁  任何一个对象

锁自己,刚不刚,强不强,也一样的,万物皆可superme

还有可以把方法同步一样,把方法加个synchronized也一样,用哪种方法,就看自己的需求了。

代码块的好处:同步代码弄成最小的范围,提高效率。

同步方法就是 this,就是把当前对象锁了

静态方法锁,顾名思义,静态的方法锁,加个static

package secondSteps;

/**
 * 卖票
 * @author liugang
 * @create 2018/12/6 22:43
 **/
public class SellTicket implements Runnable {

    private static Integer ticketCount = 100;

    private Object obj = new Object();

    @Override
    public void run() {
        try {
            while (ticketCount>0){
                sellTicket();
                Thread.sleep(1000);
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    private synchronized static void sellTicket() {
        //同步代码块 锁 任何一个对象
        synchronized (SellTicket.class) {
            if (ticketCount > 0) {
                ticketCount--;
                System.out.println("线程的名称:" + Thread.currentThread().getName() + "卖票 还剩" + ticketCount + "张");
            } else {
                System.out.println("票卖完了");
            }
        }
    }
    //如果不考虑多线程的环境,肯定不会出问题
}

用static锁的是整个类,这样你new 出来的对象是没关系的,火车上4个人排队上一个厕所。

多个线程要同一个锁才可以。

注意:

1,同步代码块,他可以用任何一个对象作为锁

2,静态方法锁,锁的是整个类,所有对象过来都会阻塞,static是类级别,并不是某个对象。

3,synchronized方法,是锁的对象,这样是不行的。各所各的,是不一样的,是没有意思的。

线程的等待,线程的唤醒:

1,wait   ----->导致获得这个锁的线程,阻塞等待,知道他被唤醒为止

2,notify ----->唤醒在等待这个锁的其他线程之一

3,notifyall  ------->唤醒所有在等待的这个线程

package threeSteps;

/**
 * 以这个类作为锁,让其他的类去等待,去唤醒
 * @author liugang
 * @create 2018/12/6 23:30
 **/
public class Message {

    private String msg;

    public Message(String msg) {
        this.msg = msg;
    }

    public Message() {
    }

    public String getMsg() {
        return msg;
    }

    public void setMsg(String msg) {
        this.msg = msg;
    }
}
package threeSteps;

/**
 * @author liugang
 * @create 2018/12/6 23:32
 **/
public class Waiter implements Runnable{

    private Message msg;

    public Waiter(Message msg) {
        this.msg = msg;
    }

    @Override
    public void run() {
        String name = Thread.currentThread().getName();
        //当前线程,执行到了msg.wait()就会导致,持有这个锁的线程进行等待,一直到他被唤醒为止
        synchronized (msg){
            try {
                System.out.println(name+"等待");
                msg.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}
public static void main(String[] args) {
        Message msg = new Message("锁");
        //同一个对象,同一个锁才行
        Waiter waiter = new Waiter(msg);
        new Thread(waiter,"waiter").start();

        Waiter waiter1 = new Waiter(msg);
        new Thread(waiter1,"waiter1").start();
        
        Notifier notifier = new Notifier(msg);
        new Thread(notifier,"waiter").start();
    }

运行结果如下:

这是因为我把notify改成notifyAll,唤醒所有了

package threeSteps;

/**
 * 进行唤醒
 * @author liugang
 * @create 2018/12/6 23:38
 **/
public class Notifier implements Runnable{

    private Message msg;

    public Notifier(Message msg) {
        this.msg = msg;
    }

    @Override
    public void run() {
        String name = Thread.currentThread().getName();
        synchronized (msg){
            msg.setMsg("唤醒线程工作");
//            msg.notify();
            msg.notifyAll();
        }
    }
}

wait运行到msg.wait()就卡死了,就一直等这了,就像我的联想笔记本动不动就卡死了,

好在有notify唤醒他,而我的笔记本就只能重启由我自己来唤醒他啦。

notify只能唤醒一个哟,另外一个只能一直卡死在那了。

notifyAll就唤醒所有,就像毛主席,是的

posted @ 2018-12-06 23:53  正能量教官  阅读(183)  评论(0编辑  收藏  举报