Java基础-12-多线程2

Java基础-线程

线程进阶

1、多线程(线程间通信-示例代码)

线程间通信:其实就是多个线程在操作同一个资源,但操作动作不同。 以下是一个例子,可是没加同步,存在隐患。

/*
 * 需求:一堆煤(共同资源),两个工人(线程),一个负责搬煤(输入名称),一个负责运煤(输出名字)。
 */
class Res
{
    String name;
    String sex;
}
class Input implements Runnable
{
    private Res r ; 
    Input(Res r)
    {
        this.r = r ;
    }
    public void run()
    {
        int x  = 0 ;
        while(true)
        {
            if(x==0)
            {
                r.name = "mike";
                r.sex = "man";
            }
            else 
            {
                r.name = "LiLi";
                r.sex = "girl";
            }
            x = (x+1)%2;
        }
    }
}
class Output implements Runnable
{
    private Res r ; 
    Output(Res r )
    {
        this.r = r ;
    }
    public void run()
    {
        while(true)
        {
            System.out.println(r.name+"...."+r.sex);
        }
    }
}
class InputOutDemo
{
    public static void main(String[]args)
    {
        Res r = new Res();
        Input in = new Input(r);
        Output out = new Output(r);
        Thread t1 = new Thread(in);
        Thread t2 = new Thread(out);
        t1.start();
        t2.start();
    }
}

输出结果:

mike....man
mike....man
mike....girl
mike....girl
LiLi....man
LiLi....man
LiLi....man
mike....girl
mike....girl
LiLi....man
mike....girl
LiLi....man
LiLi....girl
........

其结果不如人意,这是由于代码没有同步而造成的。

2、多线程(线程间通信-解决安全问题)

基于上一节的例子改进,对两线程执行的共同资源进行了同步上锁。

class Res
{
    String name;
    String sex;
}
class Input implements Runnable
{
    private Res r ; 
    Input(Res r)
    {
        this.r = r ;
    }
    public void run()
    {
        int x  = 0 ;
        while(true)
        {
            synchronized(r)//上锁,同样的对象
            {
                if(x==0)
                {
                    r.name = "mike";
                    r.sex = "man";
                }
                else 
                {
                    r.name = "LiLi";
                    r.sex = "girl";
                }
                x = (x+1)%2;
            }
        }
    }
}
class Output implements Runnable
{
    private Res r ; 
    Output(Res r )
    {
        this.r = r ;
    }
    public void run()
    {
        while(true)
        {
            synchronized(r)//上锁,同样的对象
            {
                System.out.println(r.name+"...."+r.sex);
            }

        }
    }
}
class InputOutDemo
{
    public static void main(String[]args)
    {
        Res r = new Res();
        Input in = new Input(r);
        Output out = new Output(r);
        Thread t1 = new Thread(in);
        Thread t2 = new Thread(out);
        t1.start();
        t2.start();
    }
}

输出结果:

LiLi....girl
mike....man
mike....man
mike....man
mike....man
......

因此结果符合客观的需求了。

3、多线程(线程间通信-等待唤醒机制)

类 Object 是类层次结构的根类。每个类都使用 Object 作为超类。所有对象(包括数组)都实现这个类的方法。在Object类中,wait方法,notify方法,notifyAll方法,都是用在同步中。

wait(long timeout):在其他线程调用此对象的 notify() 方法或 notifyAll() 方法,或者超过指定的时间量前,导致当前线程等待。

wait():在其他线程调用此对象的 notify() 方法或 notifyAll() 方法前,导致当前线程等待。

notify():唤醒在此对象监视器上等待的单个线程。

notifyAll():唤醒在此对象监视器上等待的所有线程。

由上一节课的结果发现,打印出来的结果还是存在不靠谱现象,就是同样的信息重复打印,原因是当input线程输入完信息后,output线程抢到了cpu执行权,进行输出,而output又在input线程未再次输入前又抢到了执行权,如何解决该问题:等待唤醒机制。

以下是上一节课改进后的代码:

class Res
{
    String name;
    String sex;
    boolean flag;
}
class Input implements Runnable
{
    private Res r ; 
    Input(Res r)
    {
        this.r = r ;
    }
    public void run()
    {
        int x  = 0 ;
        while(true)
        {
            synchronized(r)
            {
                if(r.flag)  //在Ipunt同步锁里加了这标示表示该线程执行了一次就要等待Output同步锁里的代码执行一次,两功能交叉同时运行。
                {
                    try
                    {
                        r.wait();
                    }
                    catch(Exception e)
                    {
                    }
                }
                if(x==0)
                {
                    r.name = "mike";
                    r.sex = "man";
                }
                else 
                {
                    r.name = "LiLi";
                    r.sex = "girl";
                }
                x = (x+1)%2;
                r.flag =true;
                r.notify();//唤醒线程
            }
        }
    }
}
class Output implements Runnable
{
    private Res r ; 
    Output(Res r )
    {
        this.r = r ;
    }
    public void run()
    {
        while(true)
        {
            synchronized(r)
            {
                if(!r.flag)//在Output同步锁里加了这标示表示该线程执行了一次就要等待Input同步锁里的代码执行一次,两功能交叉同时运行。
                {
                    try
                    {
                        r.wait();
                    }
                    catch(Exception e)
                    {

                    }
                }
                System.out.println(r.name+"...."+r.sex);
                r.flag = false;
                r.notify();//唤醒线程
            }

        }
    }
}
class InputOutDemo
{
    public static void main(String[]args)
    {
        Res r = new Res();
        Input in = new Input(r);
        Output out = new Output(r);
        Thread t1 = new Thread(in);
        Thread t2 = new Thread(out);
        t1.start();
        t2.start();
    }
}

输出结果:

LiLi....girl
mike....man
LiLi....girl
mike....man
LiLi....girl
.......

wait方法,notify方法和notifyAll方法这些都是使用在同步中,因为要对待有监视器(锁)的线程操作。所以要使用在同步中。因为只有同步才具有锁。 为什么这些操作线程的方法要定义Object类中呢? 因为这些方法在操作同步线程时,都必须要标识他们所操作线程只有的锁。只有同一个锁上的被等待的线程,可以被同一个锁上notify唤醒,不可以对不同锁中的线程进行唤醒。而锁可以是任意对象,所以可以被任意对象调用的方法定义Object类中。

4、多线程(线程间通信-代码优化)

class Res//人物信息类
{
    private String name;
    private String sex;
    private boolean flag = false;       //线程通信标识符,为了让线程交叉有序的切换
    public synchronized void set(String name , String sex)//把需要扔到线程run方法里的代码都封装成带锁的功能
    {
        if(flag)//加了通信机制的同步锁方法
        {
            try
            {
                this.wait();
            }
            catch(Exception e )
            {

            }
        }
        this.name = name;
        this.sex = sex;
        flag = true;
        this.notify();
    }
    public synchronized void out()//把需要扔到线程run方法里的代码都封装成带锁的功能
    {
        if(!flag)//加了通信机制的同步锁方法
        {
            try
            {
                this.wait();
            }
            catch(Exception e )
            {

            }
        }
        System.out.println(name+"....."+sex);
        flag = true;
        this.notify();
    }
}
class Input implements Runnable
{
    private Res r ;
    Input(Res r)
    {
        this.r = r;
    }
    public void run()//在Input的run方法中调用Res类中的加了通信机制的带锁方法(同步方法)。
    {
        int x = 0;
        while(true)
        {
            if(x==0)
            {
                r.set("mike", "man");

            }
            else r.set("lili","girl");
            x= (x+1)%2;
        }
    }
}
class Output implements Runnable
{
    private Res r;
    Output(Res r)
    {
        this.r = r;
    }
    public void  run()//在Output的run方法中调用Res类中的加了通信机制的带锁方法(同步方法)。
    {
        while(true)
        {
            r.out();
        }
    }
}
class InputOutputDemo
{
    public static void main(String[]args)
    {
        Res r = new Res();
        new Thread(new Input(r)).start();
        new Thread(new Output(r)).start();
    }
}

输出结果:

mike.....man
lili.....girl
mike.....man
lili.....girl
mike.....man
lili.....girl
mike.....man
....

5、多线程(线程间通信-生产者消费者)

class Resource
{
    private String name;
    private int count =1;
    private boolean flag = false ;
    public synchronized void set(String name)
    {
        while(flag)//不用if判断是为了让多个生产者线程都要重新进行标识判断
        {
            try
            {
                this.wait();
            }
            catch(Exception e)
            {

            }
        }
        this.name = name +".."+count++;
        System.out.println(Thread.currentThread().getName()+"...生产者.."+this.name);
        flag = true;
        this.notifyAll();//不用notify的原因是由于被wait了的线程都存放在了线程池里,等待按顺序被唤醒,这样有可能没把对消费者的线程给唤醒
    }
    public synchronized void out()
    {
        while(!flag)//不用if判断是为了让多个消费者线程都要重新进行标识判断
        {
            try
            {
                this.wait();
            }
            catch(Exception e)
            {

            }
        }
        System.out.println(Thread.currentThread().getName()+".....消费者..."+this.name);
        flag = false;
        this.notifyAll();//不用notify的原因是由于被wait了的线程都存放在了线程池里,等待按顺序被唤醒,这样有可能没把对生产者的线程给唤醒
    }
}
class Producer implements Runnable 
{
    private Resource res;
    Producer(Resource res)
    {
        this.res = res;
    }
    public void run()
    {
        while (true)
        {
            res.set("+商品+");
        }
    }
}
class Consumer implements Runnable
{
    private Resource res;
    Consumer (Resource res)
    {
        this.res = res;
    }
    public void run()
    {
        while (true)
        {
            res.out();
        }
    }
}
class ProducerConsumerDemo
{
    public static void main(String []args)
    {
        Resource res = new Resource();
        new Thread(new Producer(res)).start();
        new Thread(new Consumer(res)).start();
        new Thread(new Producer(res)).start();
        new Thread(new Consumer(res)).start();
    }
}

输出结果:

Thread-0...生产者..+商品+..1
Thread-1.....消费者...+商品+..1
Thread-0...生产者..+商品+..2
Thread-1.....消费者...+商品+..2
...

6、多线程(线程间通信-生产者消费者JDK5.0升级版)

上一节课生产者消费者程序问题的总结: * 对于多个生产者和消费者,为什么要定义while判断标记? 原因:让被唤醒的线程再一次判断标记。

  • 为什么定义notifyAll? 因为需要唤醒对方线程。因为只用notify,容易由于线程池的特性而只唤醒了本方线程的情况,导致程序中的所有线程都等待,但该等待不是所谓的死锁。

升级后的Java java.util.concurrent.locks包里的接口Lock

接口Lock:Lock实现提供了比使用 synchronized 方法和语句可获得的更广泛的锁定操作。此实现允许更灵活的结构,可以具有差别很大的属性,可以支持多个相关的Condition对象。

接口Condition:Condition 将 Object 监视器方法(wait、notify 和 notifyAll)分解成截然不同的对象,以便通过将这些对象与任意 Lock 实现组合使用,为每个对象提供多个等待 set(wait-set)。其中,Lock 替代了 synchronized 方法和语句的使用,Condition 替代了 Object 监视器方法的使用。

/*
 * JDK1.5中提供了多线程升级解决方案。将同步synchronized替换成现成lock操作。
 * 将Objcet中的wait,notify,notifyAll,替换了condition对象。该对象可以lock锁进行获取,
 * 该示例中,实现了本方只唤醒对方操作。
 * */
import java.util.concurrent.locks.*;
class Resource
{
    private String name;
    private int count = 1;
    private boolean flag  = false;
    private Lock lock = new ReentrantLock();
    private Condition condition_pro = lock.newCondition();
    private Condition condition_con = lock.newCondition();
    public void set (String name )throws InterruptedException
    {
        lock.lock();//上锁
        try
        {
            while (flag)
            {
                condition_pro.await();  //只冻结t1 , t2 (生产者线程)
            }
            this.name = name+"..."+count++;
            System.out.println(Thread.currentThread().getName()+"....生产者...."+this.name);
            flag = true;
            condition_con.signal();//只唤醒t3,t4(消费者线程)
        }
        finally
        {
            lock.unlock();
        }
    }
    public void out()throws InterruptedException
    {
        lock.lock();
        try
        {
            while(!flag)
            {
                condition_con.await();
            }
            System.out.println(Thread.currentThread().getName()+"....消费者..........."+this.name);
            flag = false;
            condition_pro.signal();//唤醒t1,t2的线程(生产者)
        }
        finally
        {
            lock.unlock();
        }
    }
}
class Consumer implements Runnable
{
    private Resource res;
    Consumer(Resource res)
    {
        this.res = res;
    }
    public void run()
    {
        while(true)
        {
            try
            {
                res.out();
            }
            catch(InterruptedException e)
            {

            }
        }
    }
}
class Producer implements Runnable
{
    private Resource res;
    Producer(Resource res)
    {
        this.res = res;

    }
    public void run()
    {
        while(true)
        {
            try
            {
                res.set("+商品+");
            }
            catch(InterruptedException e)
            {
            }
        }
    }
}
class ProducerConsumerDemo
{
    public static void main(String[]args)
    {
        Resource r = new Resource();
        Producer pro = new Producer(r);
        Consumer con = new Consumer(r);
        Thread t1 = new Thread(pro);
        Thread t2 = new Thread(pro);
        Thread t3 = new Thread(con);
        Thread t4 = new Thread(con);
        t1.start();
        t2.start();
        t3.start();
        t4.start();
    }
}

输出结果:

......
Thread-0....生产者....+商品+...32599
Thread-3....消费者...........+商品+...32599
Thread-1....生产者....+商品+...32600
Thread-2....消费者...........+商品+...32600
Thread-0....生产者....+商品+...32601
Thread-3....消费者...........+商品+...32601
Thread-1....生产者....+商品+...32602
Thread-2....消费者...........+商品+...32602
.....

7、多线程(停止线程)

Thread类里的stop方法已经过时,那么如何停止线程? 只有一种,run方法结束。开启多线程运行,运行代码通常是循环结构。只要控制住循环,就可以让run()方法结束,也就是线程结束。

class StopThread implements Runnable 
{
    private boolean flag = true;
    public synchronized void run()
    {
        while(flag)
        {
            try
            {
                //wait();//注释掉的冻结线程方法。若不注释,程序还是停不下来,线程冻结了。
            }
            catch(Exception e)
            {
                System.out.println(Thread.currentThread().getName()+".....Exception");
            }
            System.out.println(Thread.currentThread().getName()+"....run");
        }
    }
    public void changeFlag()
    {
        flag = false;
    }
}
class StopThreadDemo
{
    public static void main(String []args)
    {
        StopThread st = new StopThread();
        Thread t1 = new Thread(st);
        Thread t2 = new Thread(st);
        t1.start();
        t2.start();
        int num = 0;
        while(true)
        {
            if(num++==60)
            {
                st.changeFlag();
                break;
            }
            System.out.println(Thread.currentThread().getName()+"......."+num);
        }
    }
}

输出结果:

main.......1
Thread-0....run
main.......2
Thread-0....run
.......
main.......59
Thread-0....run
main.......60
Thread-0....run

把注释掉的wait方法重新去掉注释,当没有指定的方式让冻结的线程恢复到运行状态时,这时需要对冻结进行清除,强制让线程恢复到运行状态中来,这就可以操作标记让线程结束。Thread类提供了该方法,interrupt()方法。

8、多线程(守护线程)

setDaemon(boolean on) 方法,将该线程标记为守护线程或用户线程。当正在运行的线程都是守护线程时,Java虚拟机退出。 参数on:如果为true,则将该线程标记为守护线程。

9、多线程(Join方法)

join() :等待该线程终止。也就是该线程直接抢夺cpu执行权。

class Demo implements Runnable
{
    public void run()
    {
        for(int x=0 ; x<5;x++)
        {
            System.out.println(Thread.currentThread().getName()+"...."+x);
        }
    }
}
class JoinDemo
{
    public static void main(String[]args) throws InterruptedException
    {
        Demo d = new Demo();
        Thread t0 = new Thread(d);
        Thread t1 = new Thread(d);
        t0.start();
        t1.join();//抢夺cpu资源
        t1.start();
        for(int x= 0;x<5;x++)
        {
            System.out.println("main..."+x);
        }
        System.out.println("over");

    }
}

join方法:当t0线程执行到了t1线程的join()方法时,t0线程就会等待,等t1线程都执行完,t0才会执行,join可以用来临时加入线程执行。

10、多线程(优先级&yield)

class Demo implements Runnable
{
    public void run()
    {
        for(int x=0 ; x<5;x++)
        {
            System.out.println(Thread.currentThread().getName()+"...."+x);
        }
    }
}
class JoinDemo
{
    public static void main(String[]args) throws InterruptedException
    {
        Demo d = new Demo();
        Thread t0 = new Thread(d);
        Thread t1 = new Thread(d);
        t0.start();
        t1.setPriority(Thread.MAX_PRIORITY);//更改线程的优先级。
        t1.start();
        for(int x= 0;x<5;x++)
        {
            System.out.println("main..."+x);
        }
        System.out.println("over");

    }
}

MAXPRIORITY:线程可以具有的最高优先级。 MINPRIORITY :线程可以具有的最低优先级。 NORM_PRIORITY:分配给线程的默认优先级。 yield() :暂停当前正在执行的线程对象,并执行其他线程。

技巧:编程时想令代码高效,使用匿名类去使用线程技术。

class ThreadTest
{
    public static void main(String[]args)
    {
        new Thread()    //方法1
        {
            public void run()
            {
                for(int x=0 ; x<100;x++)
                {
                    System.out.println(Thread.currentThread().getName()+"..."+x);
                }
            }
        }.start();

        //方法2
        Runnable r = new Runnable()
        {
            public void run()
            {
                for(int x = 0 ; x<100;x++)
                {
                    System.out.println(Thread.currentThread().getName()+"..."+x);
                }
            }
        };
        new Thread(r).start();
    }
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值