之前的思路会导致一个问题:
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允许程序直接对线程进行控制。
- 线程类里面的方法
- 得到线程的线程组:public final ThradGroup getThreadGroup()
- 创造线程(参数带有线程组):Thread(ThreadGroup group, Runnable target, String name)
- 线程组里面的方法
- 得到该线程组的名字:public final String getName()
- 创建一个线程组(构造方法):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工厂类来产生线程池,有如下几个方法
- public static ExecutorsService newCachedThreadPool():开启具有缓存功能的线程池
- public static ExecutorService newFixedThreadPool(int nThreads):创建具有指定数量线程的线程池
- 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();
}
}
网友评论