Java学习十,线程

1.线程的创建实现类

       

在创建的过程中,我们需要了解两个类:1.Thread类 2.Runnable类

1.1 Thread类

     

Thread类中常用的方法:

    

1.2 Runnable接口

   

2. 线程的创建和使用

2.1 Thread类实现

代码如下:

package com_imooc.xiancheng;

class Mytest extends Thread {
    //实现线程,必须重写run()方法
    public void run() {
        System.out.println("线程启动了");
    }
}

public class xiancheng_test {
    public static void main(String[] args) {
        Mytest t = new Mytest();
        /**
         * 启动线程使用的是start()方法
         * 一个线程只能启动一次
         */
        t.start();
    }
}

结果输入如下:

   

可能有的小伙伴会说,这线程和我们之前使用main的方法是一致的呀,没有什么不同的呀,其实我们此时的程序中两个线程,既:主线程和子线程,为了让我们更加清楚的明白些,我们代码修改如下:

package com_imooc.xiancheng;

class Mytest extends Thread {
    //实现线程,必须重写run()方法
    public void run() {
        System.out.println("线程启动了");
    }
}

public class xiancheng_test {
    public static void main(String[] args) {

        System.out.println("我是主线程1");

        Mytest t = new Mytest();
        /**
         * 启动线程使用的是start()方法
         * 一个线程只能启动一次
         */
        t.start();

        System.out.println("我是主线程2");
    }
}

结果输出为:

自此,我们可以很清楚的看出,并不是我们顺序执行的代码,而是先主后子的执行方式;

当然,我们也可以直接用for来多运行几次,代码如下:

package com_imooc.xiancheng;

class Mytests extends Thread {
    public Mytests(String name) {
        super(name);
    }

    @Override
    public void run() {
        for (int i = 0; i < 10; i++) {
            System.out.println(getName()+"正在run的线程:"+i);
        }
    }
}

public class xianchengs_test {
    public static void main(String[] args) {
         Mytests m1=new Mytests("a");
         Mytests m2=new Mytests("b");

         m1.start();
         m2.start();
    }
}

结果为:

可以看出上面例子的输出并不是顺序执行的,由此可见是抢占式的运行;

2.2 Runnable接口实现

   首先你得明白为什么有了Thread类还需要Runnable接口:

    

在明白为什么需要创建Runnable之后,我们创建他的代码:

package com_imooc.xiancheng;

class test implements Runnable {

    //重写run方法可以调用Thread类的静态方法currentThread()
    @Override
    public void run() {
        int i = 0;
        while (i < 10)
            System.out.println(Thread.currentThread().getName() + "正在进行" + i++);
    }
}

public class runnable_test {
    public static void main(String[] args) {
        //启动进程的时候需要调用Thread的构造方法Thread(Runnable r)
        test able = new test();
        Thread t = new Thread(able);
        t.start();

        test able1 = new test();
        Thread t1 = new Thread(able1);
        t1.start();
    }
}

结果:

和之前的结果一样,t和t1都是执行了10次,有没有办法让我们实现两个线程一共执行10次呢??

当然有,方法如下:

package com_imooc.xiancheng;

class test implements Runnable {
    int i = 0;

    //重写run方法可以调用Thread类的静态方法currentThread()
    @Override
    public void run() {
        while (i < 10)
            System.out.println(Thread.currentThread().getName() + "正在进行" + i++);
    }
}

public class runnable_test {
    public static void main(String[] args) {
        //启动进程的时候需要调用Thread的构造方法Thread(Runnable r)
        test able = new test();
        Thread t = new Thread(able);
        t.start();

        Thread t1 = new Thread(able);
        t1.start();
    }
}

结果如下:

在代码中,因为t和t1的开启都是由同一个test类实例而来,所以内存共享数据int i的变化,最终两个线程抢占执行;

最后查看一下线程的生命周期;

        

2.3 sleep方法

    

package com_imooc.xiancheng;

class tests implements Runnable {
    //重写run方法可以调用Thread类的静态方法currentThread()
    @Override
    public void run() {
        for (int i = 0; i < 15; i++) {
            System.out.println(Thread.currentThread().getName() + "执行第" + i + "次");
            //sleep()方法需要捕获InterruptedException异常
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

public class runnables_test {
    public static void main(String[] args) {
        //启动进程的时候需要调用Thread的构造方法Thread(Runnable r)
        tests able = new tests();
        Thread t = new Thread(able);
        t.start();

        Thread t1 = new Thread(able);
        t1.start();
    }
}

2.4 join方法

join方法是等待线程执行完以后再去执行下面的;

package com_imooc.xiancheng;

class join_test implements Runnable {
    //重写run方法可以调用Thread类的静态方法currentThread()
    @Override
    public void run() {
        for (int i = 0; i < 10; i++) {
            System.out.println(Thread.currentThread().getName() + "执行中");
        }
    }
}

public class join {
    public static void main(String[] args) {
        //启动进程的时候需要调用Thread的构造方法Thread(Runnable r)
        join_test able = new join_test();
        Thread t = new Thread(able);
        t.start();
        try {
            t.join();
            //代表执行1秒以后不管执行完与否,执行下面的语句;
            //t.join(1111);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        for (int i = 0; i < 10; i++) {
            System.out.println("主程序结束" + i);
        }
    }
}

3.线程的优先级

除了数字可以表示以外,我们也可用优先级的常量来表示:

最后,我们可以通过以下的方法来获取和设置优先级:

代码实例:

package com_imooc.xiancheng;

class youxianji_test implements Runnable {
    private String name;

    public youxianji_test(String name) {
        this.name = name;
    }

    //重写run方法可以调用Thread类的静态方法currentThread()
    @Override
    public void run() {
        for (int i = 0; i < 10; i++) {
            System.out.println(name + "正在进行" + i);
        }
    }
}

public class youxianji {
    public static void main(String[] args) {
        //1.获取主线程的优先级
        int main_number = Thread.currentThread().getPriority();
        System.out.println("主线程的优先级是:" + main_number);

        //2.设置线程的优先级
        youxianji_test able = new youxianji_test("aaa");
        Thread t = new Thread(able);
        t.setPriority(10);  //设置的具体方法
        t.start();

        youxianji_test able1 = new youxianji_test("bbb");
        Thread t1 = new Thread(able1);
        t1.setPriority(1);  //设置的具体方法
        t1.start();
    }
}

结果如下:

值得注意的是:线性的优先级并不是越高,就越要先执行,而是和操作系统,cpu,代码执行顺序等有关的.

4.多线程的同步(synchronized)

现在列举一个银行取款的例子:

bank.java

package com_imooc.xiancheng.synchronized_test;

public class bank {
    private int zhanghao;
    private double money;

    public bank(int zhanghao, double money) {
        this.zhanghao = zhanghao;
        this.money = money;
    }

    public int getZhanghao() {
        return zhanghao;
    }

    public void setZhanghao(int zhanghao) {
        this.zhanghao = zhanghao;
    }

    public double getMoney() {
        return money;
    }

    public void setMoney(double money) {
        this.money = money;
    }

    //存款
    public void cun(double m) {
        double money = getMoney();
        money += m;
        try {
            Thread.sleep(1000);
        } catch (Exception e) {
            e.printStackTrace();
        }
        setMoney(money);
        System.out.println("cun存款后余额为:" + money);
    }

    //取款
    public void qu(double m) {
        double money = getMoney();
        try {
            Thread.sleep(1000);
        } catch (Exception e) {
            e.printStackTrace();
        }
        money = money - m;
        setMoney(money);
        System.out.println("qu取款后余额为:" + money);
    }

    @Override
    public String toString() {
        return
                "账号:" + zhanghao +
                        ", 余额:" + money;
    }
}

运行的main.java

package com_imooc.xiancheng.synchronized_test;

class yue implements Runnable {
    bank bank;
    String type;
    double money;

    public yue(com_imooc.xiancheng.synchronized_test.bank bank, String type, double money) {
        this.bank = bank;
        this.type = type;
        this.money = money;
    }

    @Override
    public void run() {
        if (type.equals("-")) {
            bank.qu(money);
        } else if (type.equals("+")) {
            bank.cun(money);
        }
    }
}

public class tongbu {
    public static void main(String[] args) {
        //创建银行账户
        bank b = new bank(1, 500);
        //创建存取钱两个线程
        yue y = new yue(b, "-", 200);
        yue y1 = new yue(b, "+", 300);
        Thread t = new Thread(y);
        Thread t1 = new Thread(y1);
        //启动线程
        t1.start();
        t.start();
        try {
            t1.join();
            t.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(b.toString());
    }
}

结果输出:

我们可以看出,运行的程序出现了脏读,所以我们需要synchronized(上锁)来保证多线程下数据的一致性;

所以我们的代码中可以直接在public中加入synchronized:

也可以用synchronized (this) {}:

5.线程的通信

 

此时我们模拟一个MQ的实例来说明我们的通讯问题:

queue.java

package com_imooc.xiancheng.tongxun_test;

public class queue {
    private int n;

    public synchronized int getN() {
        System.out.println("输出:" + n);
        return n;
    }

    public synchronized void setN(int n) {
        System.out.println("存入:" + n);
        this.n = n;
    }
}

main.java

package com_imooc.xiancheng.tongxun_test;

class duilie implements Runnable {
    queue queue;
    String type;
    int i = 0;

    public duilie(com_imooc.xiancheng.tongxun_test.queue queue, String type) {
        this.queue = queue;
        this.type = type;
    }

    @Override
    public void run() {
        if (type.equals("set")) {
            while (true) {
                queue.setN(i++);
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        } else if (type.equals("get")) {
            while (true) {
                queue.getN();
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

public class tongxun {
    public static void main(String[] args) {
        queue q = new queue();
        new Thread(new duilie(q, "set")).start();
        new Thread(new duilie(q, "get")).start();
    }
}

结果是:

仅仅从前几个数据,我们就可看出此MQ是有问题的,因为一次入列以后,竟然有两次的出列行为,这是万万不允许的错误,而我们要解决代码的bug,就用到了我们线程间的通信:

queue.java

package com_imooc.xiancheng.tongxun_test;

public class queue {
    private int n;
    boolean flag = false;   //flag=true 存在,可取出; flag=false 不存在,不可取出

    public synchronized int getN() {
        //不存的时候在等待
        if (!flag) {
            try {
                wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        System.out.println("输出:" + n);
        flag = false;   //消费完毕,队列没有数据
        notifyAll();    //启动所有线程(以防止死锁)
        return n;
    }

    public synchronized void setN(int n) {
        //存在的时候等待
        if (flag) {
            try {
                wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        System.out.println("存入:" + n);
        this.n = n;
        flag = true;    //生产完毕,队列有数据
        notifyAll();    //启动所有线程(以防止死锁)
    }
}

结果运行:

此时MQ输入输出正常;

猜你喜欢

转载自blog.csdn.net/feiwutudou/article/details/86233018
今日推荐