多线程小小回顾
synchronized和locked的异同
相同:都用来处理线程安全问题
不同:synchronized机制在执行完相应的同步代码块之后,自动释放同步监视器。
Lock需要手动启动 lock();方法 同步代码执行完成之后,也需要手动释放锁:unlocked()方法。
如何解决线程安全问题?几种方式?
加锁解决。三种方式:1.synchronized同步代码块,
2.synchronized同步方法
3.lock锁 (jdk1.5之后),java.util.concurrent.locks.Lock接口创建他的实现类ReentrantLock类,调用lock()、unlock();方法 加锁/释放锁。
线程通信问题
wait(); 一旦执行此方法,当前线程就会释放同步监视器,进入阻塞状态。
notify(); 一旦执行此方法,就会唤醒当前处于wait状态的线程,如果有多个线程处于wait阻塞状态,会唤醒优先级高的。
notifyAll(); 唤醒所有处于wait阻塞状态的线程。
注意:
wait,notify,notifyALL这三个方法是定义在Java.lang.Object类中的,而不是Thread类的方法。
- 这三个方法必须使用在同步代码块中,lock都不行。
- 这三个方法的调用者必须是同步监视器,也就是同步代码块中的synchronized(同步监视器) 或 同步方法的同步监视器,否则会出现IllegalMonitorStateException异常。(非法监视器状态异常。
Sleep和wait方法的异同。
相同:
- 都可以让当前线程陷入阻塞状态。
不同:
- 1.sleep方法不会释放锁,若是指定睡眠时间,在睡眠时间过后,当前线程会继续执行,wait方法会释放锁,知道notify方法将她唤醒才会继续执行。
- 2.两个方法声明的位置不同,sleep是Thread类中的方法,而wait是Object中的方法。
- 3.sleep方法可以在任何需要的场景下调用。但是wait方法必须是同步监视器调用的,因此必须在同步代码块或者同步方法中调用。
生产者消费者问题
package com.ene.controller;
//生产者消费者问题
public class SXQuestion {
public static void main(String[] args) {
Clerk clerk = new Clerk();
Producer producer = new Producer(clerk);
producer.setName("生产者");
Producer producer2 = new Producer(clerk);
producer2.setName("生产者2");
Custom custom = new Custom(clerk);
custom.setName("消费者");
producer.start();
producer2.start();
custom.start();
}
}
//仓库管理员
class Clerk {
//商品数量
private int productNum = 0;
// //货架容量
// private static final int capacity = 20;
//上架货品
public void produceProduct() {
synchronized (this) {
if (productNum < 20) {
productNum++;
System.out.println(Thread.currentThread().getName() + ":生产第" + productNum + "件商品");
notify();
} else {
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
//卖出货品
public void sellProduct() {
synchronized (this) {
if (productNum > 0) {
System.out.println(Thread.currentThread().getName() + ":消费第" + productNum + "件商品");
productNum--;
notify();
} else {
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
//生产者
class Producer extends Thread {
private Clerk clerk;
public Producer(Clerk clerk) {
this.clerk = clerk;
}
@Override
public void run() {
System.out.println(getName()+"开始生产产品...");
while (true) {
try {
sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
clerk.produceProduct();
}
}
}
//消费者
class Custom extends Thread {
private Clerk clerk;
public Custom(Clerk clerk) {
this.clerk = clerk;
}
@Override
public void run() {
while (true) {
try {
sleep(50);
} catch (InterruptedException e) {
e.printStackTrace();
}
clerk.sellProduct();
}
}
}
创建线程的四种方式:
-
1.继承Thread类,重写run方法,调用start方法;
-
2.实现Runnable接口,重写run方法,创建Thread类对象,实现Runnable接口的实现类作为参数传进去,例如:
class Test1 immplements Runnable{ @Override public void run(){ } } public class Test{ public static void main(String[] args) { Test1 test1 = new Test1(); Thread myThread = new Thread(test1); } } -
3.实现Callable接口(JDK5之后)
创建线程方式三:实现Callable接口 1.创建一个实现Callable的实现类 2.实现call方法,将此线程需要执行的操作声明在次方法中 3.创建Callable接口实现类的对象 4.将此对象作为参数丢到FutureTask构造器中,创建FutureTask对象 5.将FutureTask对象作为对象传递到Thread构造器中,创建Thread对象,start() 6.如果需要方法的返回值,则用futureTask.get()方法去获取package com.ene.dao; import java.util.ArrayList; import java.util.List; import java.util.concurrent.Callable; import java.util.concurrent.FutureTask; class NumThread implements Callable { @Override public Object call() throws Exception { List<Integer> numList = new ArrayList<>(); for(int a = 1;a<10;a++){ System.out.println(a); numList.add(a); } return numList; } } public class CallableTest{ public static void main(String[] args) { //1.创建callable实现类的对象 NumThread numThread = new NumThread(); //2.创建FutureTask对象,把Callable的实现类作为参数丢进去 FutureTask futureTask = new FutureTask(numThread); //3.创建Thread类,把futuretask创建的对象,作为参数丢进去。 //FutureTask实现了RunnableFuture接口,然后RunnableFuture接口又继承了Runnable接口,所以能作为参数传入 //Thread构造方法public Thread(Runnable target) 中去。 Thread thread = new Thread(futureTask); thread.start(); } } -
4.线程池创建线程(JDK5之后)

使用Executors工具类,线程池的工厂类,创建并返回不同类型的线程池。
-
Executors.newCachedThreadPool();创建一个可根据需要创建新线程的线程池
-
Executors.newFixedThreadPool(n);创建一个可重用固定线程数的线程池
-
Executors.newSingleThreadExcutor;创建一个只有一个线程的线程池
-
Executors.newScheduledThreadPool(n);创建一个线程池,他可以安排在给定延迟后运行命令或者定期的执行。
Cached(可根据需要创建)、Fixed(固定的)、Single(单独的)、Scheduled(有安排的)
-
使用步骤:
1.提供指定线程数量的线程池 2.执行指定的线程的操作,需要提供实现Runnable接口的实现类或Callable接口的实现类的对象作为参数。 3.关闭线程池。
![]()
-
设置线程池属性的方法。使用
ThreadPoolExecutor service = (ThreadPoolExeutor )Executors.newFixedThreadPool(10);
只有ThreadPoolExecutor里面定义了需要设置的连接池属性。ExecutorService是接口。上面图里有。


浙公网安备 33010602011771号