Java多线程:线程协作、线程池
线程协作
线程通信
应用场景:生产者和消费者问题,生产者生产商品,消费者购买商品,没有商品时等待生产商生产放入商品
分析
线程同步问题。生产者和消费者共享同一资源,相互依赖,互为条件:
- 生产者没有生产产品前,需要通知消费者等待,生产后要通知消费者消费
- 消费者在消费后要通知生产者结束消费,生产新的产品以供消费
- synchronized不足使用:
- synchronized可以同步
- 但无法通信
方法
Java提供了一些Object类的方法解决线程间的通信问题,仅能在同步方法和同步块中使用,否则会抛出异常
wait()
:线程会一直等待,直到其他线程通知,会释放锁wait(long timeout)
:指定等待的毫秒数notify()
:唤醒一个处于等待状态的线notifyAll()
: 唤醒同一个对象上所有地道用wait()
方法的线程,优先级别高的线程优先调度
管程法:利用缓冲区
涉及wait()
时使用while
//管程法
//生产者,消费者,产品,缓冲区
public class TestPC {
public static void main(String[] args) {
SynContainer container = new SynContainer();
//测试,多名生产者和多名消费者
new Producer(container).start();
new Producer(container).start();
new Producer(container).start();
new Consumer(container).start();
new Consumer(container).start();
new Consumer(container).start();
new Consumer(container).start();
}
}
//生产者
class Producer extends Thread{
SynContainer container;
public Producer(SynContainer container){
this.container = container;
}
//生产
@Override
public void run() {
for (int i = 0; i < 100; i++) {
container.push(new Food(i));
System.out.println("生产了:" + i);
}
}
}
//消费者
class Consumer extends Thread{
SynContainer container;
public Consumer(SynContainer container){
this.container = container;
}
@Override
public void run() {
for (int i = 0; i < 100; i++) {
System.out.println("消费了:" + container.pop().id);
}
}
}
//产品
class Food{
int id;
public Food(int id) {
this.id = id;
}
}
//缓冲区
class SynContainer{
//容器大小
Food[] foods = new Food[10];
//容器计数器
int count = 0;
//生产者放入产品
public synchronized void push(Food food){
//容器满了,等待消费者消费
while (count == foods.length){
//通知消费者消费,生产等待
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//没满,生产产品
foods[count] = food;
count++;
//通知消费者消费
this.notifyAll();
}
//消费者消费产品
public synchronized Food pop(){
//判断是否有产品,多名消费者都等待
while (count <= 0){
//等待产品生产,消费者等待
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//可以消费
count--;//出栈,因为生产后索引指向后一个空位
Food food = foods[count];
//消耗了,通知生产者生产
this.notifyAll();
return food;
}
}
信号灯法:标志位
//信号灯法,标志位解决
public class TestPC2 {
public static void main(String[] args) {
Produce produce = new Produce();
new Producer2(produce).start();
new Producer2(produce).start();
new Consumer2(produce).start();
new Consumer2(produce).start();
}
}
//生产者
class Producer2 extends Thread{
Produce id;
public Producer2(Produce id) {
this.id = id;
}
//生产
@Override
public void run() {
for (int i = 0; i < 100; i++) {
this.id.product(i);
}
}
}
//消费者
class Consumer2 extends Thread{
Produce id;
public Consumer2(Produce id) {
this.id = id;
}
//消费
@Override
public void run() {
for (int i = 0; i < 100; i++) {
this.id.consume();
}
}
}
class Produce{
//生产者生产,消费者等待 T
//消费者消费,生产者等待 F
int id;
boolean flag = true;
//生产
public synchronized void product(int id){
if (!flag){
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("生产了" + id);
//通知消费者消费
this.notifyAll();
this.id = id;
this.flag = !this.flag;
}
//消费
public synchronized void consume(){
if (flag){
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("消费了" + id);
//通知生产
this.notifyAll();
this.flag = !this.flag;
}
}
线程池
- 背景:经常创建和销毁、使用量特别 大的资源,如并发情况下的线程,对性能影响很大
- 思路:提前创建好多个线程,放入线程池中,使用时之间获取,使用完放回池中。避免频发创建销毁,实现重复利用
- 优点:
- 提高响应速度(减少了创建新线程的时间)
- 降低资源消耗(重复利用线程池中线程,不需要每次重新创建)
- 便于线程管理:
corePoolSize
:核心池的大小maximumPoolSize
:最大线程数keepAliveTime
:线程没用任务时最多保留多久中止
线程池的使用
- JDK5.0,
ExecutorService
,Executors
ExecutorService
:线程池接口,常用子类ThreadPoolExecutor
void execute(Runnable command)
:执行任务/命令,没返回值,用于执行Runnable<T>Future<T> submit(Callable<T> task)
:执行任务,有返回值,用于执行Callablevoid shutdown()
:用于关闭连接池
Executors
:工具类、线程池的工厂类,用于创建并返回不同类型的线程池
public class TestPool {
public static void main(String[] args) {
//创建服务,创建线程池
//newFixedThreadPool,参数为线程大小
ExecutorService service = Executors.newFixedThreadPool(10);
service.execute(new MyThread());
service.execute(new MyThread());
service.execute(new MyThread());
//关闭
service.shutdown();
}
}
class MyThread implements Runnable{
@Override
public void run() {
System.out.println(Thread.currentThread().getName());
}
}