美文网首页
多线程基础(六)

多线程基础(六)

作者: Maxinxx | 来源:发表于2019-03-10 22:20 被阅读0次

之前的思路会导致一个问题:
A:如果消费者先抢到了CPU的执行权,就会去消费数据,但是现在的数据是默认值,没有意义,应该等到数据有意义,再去消费。
B:如果生产者先抢到CPU的执行权,就会去产生数据,但是,它产生完数据之后,还继续拥有执行权,它会继续产生数据。这是有问题的,应该等到消费者把数据消费掉,然后再生产。

正常的思路:
A(生产者):先看是否有数据,有就等待,没有就生产,生产完之后通知消费者来消费数据。
B(消费者):先看是否有数据,有就消费,没有就等待,通知生产者生产数据。

为了处理这样的问题,Java就提供了一种机制:等待唤醒机制。

等待唤醒机制

等待唤醒:
Object类中提供了三个方法:

wait():等待
notify():唤醒单个线程
notifyAll():唤醒所有线程

为什么这些方法不定义在Thread类中呢:
回答:因为这些方法的调用必须通过锁对象调用,而我们刚才使用的锁对象是任意锁对象,所以这些方法必须定义在Object类中。

public class Student {
    String name;
    int age;
    boolean flag = false;//默认情况是false,没有数据
}
public class SetThread implements Runnable {
    private Student s;
    private int x = 0;
    
    public SetThread(Student s) {
        this.s =s;
    }

    @Override
    public void run() {
        while(true) {
            synchronized (s) {
                //判断有没有
                if(s.flag) {
                    try {
                        s.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                if(x % 2 == 0) {
                    s.name = "娇娇";
                    s.age = 22;
                }else {
                    s.name = "昕昕";
                    s.age = 23;
                }
                x++;
                
                //修改标记
                s.flag = true;
                s.notify();
            }
        }
    }
}
public class GetThread implements Runnable {
    private Student s;

    public GetThread(Student s) {
        this.s = s;
    }

    @Override
    public void run() {
        while (true) {
            synchronized (s) {
                if(!s.flag) {
                    try {
                        s.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                System.out.println(s.name + "---" + s.age);
                
                s.flag = false;
                s.notify();
            }
        }
    }
}

线程组

线程组:把多个线程组合到一起
它可以对一批线程进行分类管理,Java允许程序直接对线程进行控制。

  • 线程类里面的方法
  1. 得到线程的线程组:public final ThradGroup getThreadGroup()
  2. 创造线程(参数带有线程组):Thread(ThreadGroup group, Runnable target, String name)
  • 线程组里面的方法
  1. 得到该线程组的名字:public final String getName()
  2. 创建一个线程组(构造方法):ThreadGroup(String name)
public class MyRunnable implements Runnable {
    @Override
    public void run() {
        for (int i = 0; i < 100; i++) {
            System.out.println(Thread.currentThread().getName() + ":" + i);
        }
    }
}
public class ThreadGroupDemo {
    public static void main(String[] args) {
        MyRunnable mr = new MyRunnable();
        
        Thread t1 = new Thread(mr, "娇娇");
        Thread t2 = new Thread(mr, "昕昕");
        
        ThreadGroup tg1 = t1.getThreadGroup();
        ThreadGroup tg2 = t2.getThreadGroup();
        
        String name1 = tg1.getName();
        String name2 = tg2.getName();
        
        System.out.println(name1);
        System.out.println(name2);
        //通过结果知道:线程默认情况下属于main线程组
        
        System.out.println(Thread.currentThread().getThreadGroup().getName());
        //通过结果知道:默认情况下,所有的线程都属于同一个组
    }
}
  • 如何修改线程所在的组(步骤如下)?
    创建一个线程组
    创建其他线程的时候,把其他线程的组指定为我们自己新建的线程组
public class ThreadGroupDemo {
    public static void main(String[] args) {
        ThreadGroup tg = new ThreadGroup("这是一个新的组");
        
        MyRunnable mr = new MyRunnable();
        Thread t1 = new Thread(tg, mr, "娇娇");
        Thread t2 = new Thread(tg, mr, "昕昕");
        
        System.out.println(t1.getThreadGroup().getName());
        System.out.println(t2.getThreadGroup().getName());
    }
}

线程池

程序启动一个新线程成本是比较高的,因为它涉及到要与操作系统进行交互,而使用线程池可以很好的提高性能,尤其是当程序中要创建大量生存期很短的线程时,更应该考虑使用线程池。

  • 线程池里的每一个线程代码结束后,并不会死亡,而是再次回到线程池中成为空闲状态,等待下一个对象来使用。
  • 在JDK5之前,我们必须手动实现自己的线程池,从JDK5开始,JAVA内置支持线程池。
  • JDK5新增了一个Executors工厂类来产生线程池,有如下几个方法
  1. public static ExecutorsService newCachedThreadPool():开启具有缓存功能的线程池
  2. public static ExecutorService newFixedThreadPool(int nThreads):创建具有指定数量线程的线程池
  3. public static ExcutorService newSingleThreadExecutor():创建一个具有一个线程的线程池
  • 如何实现线程池的代码?
    A:创建一个线程池对象,控制要创建几个线程对象
    B:这种线程池的线程可以执行Runnable对象或者Callable对象代表的线程。做一个类实现Runnable接口。
    C:调用如下方法即可
    Future<?> submit(Runnable task)
    <T> Future<T> submit(Callable<T> task)
    D:非得结束
    void shutdown()
public class MyRunnable implements Runnable {
    @Override
    public void run() {
        for (int i = 0; i < 100; i++) {
            System.out.println(Thread.currentThread().getName() + ":" + i);
        }
    }
}
public class ExecutorsDemo {
    public static void main(String[] args) {
        //创建一个线程池对象,控制要创建几个线程对象。
        ExecutorService pool = Executors.newFixedThreadPool(2);
        
        pool.submit(new MyRunnable());
        pool.submit(new MyRunnable());
        
        pool.shutdown();
    }
}

相关文章

网友评论

      本文标题:多线程基础(六)

      本文链接:https://www.haomeiwen.com/subject/lotppqtx.html