一.使用线程池的优势
Java中的对象是使用new操作符创建的,如果用new创建大量短生命周期的对象,这种方式性能非常低下。所以为了解决这个问题一些先行者们发明了池技术。 例如对于数据库连接有连接池,一样的对于线程有线程池。 线程池可以极大的简便我们的多线程编程,在需要大量线程的程序编程时,它不仅从编程上简化了,而且性能比new要好。
下面我们这个Demo展示了new和线程池方式创建并允许线程的性能:
任务类:
public class TempThread implements Runnable {// 测试用的Runnable接口实现类
private int id = 0;
@Override
public void run() {// run()方法给id做自增运算
id++;
}
}
主类:
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class ThreadPoolTest {
public static void main(String[] args) {
Runtime run = Runtime.getRuntime();// 创建Runtime对象
//不使用线程池创建1000个对象需要的开销
//*******************************************************************************************************************************************
run.gc();// 运行垃圾回收器,这样可以减少误差
long freeMemory = run.freeMemory();// 获得当前虚拟机的空闲内存
long currentTime = System.currentTimeMillis();// 获得当前虚拟机的时间
for (int i = 0; i < 10000; i++) {// 独立运行1000个线程
new Thread(new TempThread()).start();
}
System.out.println("独立运行1000个线程所占用的内存:" + (freeMemory - run.freeMemory()) + "字节");// 查看内存的变化
System.out.println("独立创建1000个线程所消耗的时间:" + (System.currentTimeMillis() - currentTime) + "毫秒");// 查看时间的变化
//使用线程池技术创建并运行1000个线程需要的开销
//************************************************************************************************************************************************
run.gc();// 运行垃圾回收器
freeMemory = run.freeMemory();// 获得当前虚拟机的空闲内存
currentTime = System.currentTimeMillis();// 获得当前虚拟机的时间
ExecutorService executorService = Executors.newFixedThreadPool(2);// 创建线程池
for (int i = 0; i < 1000; i++) {// 使用线程池运行1000个线程
executorService.submit(new TempThread());
}
System.out.println("使用连接池运行1000个线程所占用的内存:" + (freeMemory - run.freeMemory()) + "字节");// 查看内存的变化
System.out.println("使用连接池创建1000个线程所消耗的时间:" + (System.currentTimeMillis() - currentTime) + "毫秒");// 查看时间的变化
}
}
运行结果:
从结果可以看出:使用线程池可以大大优化普通情况下的多线程编程。
二.了解Object类中线程相关的方法
我们都知道Object类是所有java类的祖先类,在该类中定义了3个与线程操作相关的方法,分别是:
方法名 | 作用 |
---|---|
notify() | 唤醒在此对象监视器上等待的单个线程 |
notifyAll() | 唤醒在此对象监视器上等待的所有线程 |
wait() | 在其他线程调用此对象的notify()方法或notifyAll()方法前,导致当前线程等待 |
wait(long timeout) | 在其他线程调用此对象的notify()或notifyAll()之前,或者超过指定的时间量前,导致当前线程等待 |
wait(long timeout,int nanos) | 在其他线程调用此对象的notify()或notifyAll()方法或者其他某个线程中断当前线程,或者超时,导致当前线程等待 |
下面我们通过使用上面的方法实现线程间的协调工作(实现消费者生产者):
import java.awt.BorderLayout;
import java.awt.EventQueue;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.LinkedList;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.SwingConstants;
import javax.swing.border.EmptyBorder;
import java.awt.Font;
import javax.swing.UIManager;
public class ProducerAndConsumerFrame extends JFrame {
private static final long serialVersionUID = -1644485036183526329L;
private JPanel contentPane;
private final LinkedList<String> list = new LinkedList<String>();
private static final int MAX = 10;
private volatile int count;
private JTextArea producerTextArea;
private JTextArea consumerTextArea;
private JTextArea storageTextArea;
public static void main(String[] args) {
try {
UIManager.setLookAndFeel("com.sun.java.swing.plaf.nimbus.NimbusLookAndFeel");
} catch (Throwable e) {
e.printStackTrace();
}
EventQueue.invokeLater(new Runnable() {
public void run() {
try {
ProducerAndConsumerFrame frame = new ProducerAndConsumerFrame();
frame.setVisible(true);
} catch (Exception e) {
e.printStackTrace();
}
}
});
}
public ProducerAndConsumerFrame() {
setTitle("\u751F\u4EA7\u8005\u548C\u6D88\u8D39\u8005\u95EE\u9898");
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setBounds(100, 100, 450, 300);
contentPane = new JPanel();
contentPane.setBorder(new EmptyBorder(5, 5, 5, 5));
setContentPane(contentPane);
contentPane.setLayout(new BorderLayout(0, 0));
JPanel buttonPanel = new JPanel();
contentPane.add(buttonPanel, BorderLayout.SOUTH);
JButton startButton = new JButton("\u5F00\u59CB\u751F\u4EA7");
startButton.setFont(new Font("微软雅黑", Font.PLAIN, 16));
startButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent arg0) {
do_startButton_actionPerformed(arg0);
}
});
buttonPanel.add(startButton);
JPanel resultPanel = new JPanel();
contentPane.add(resultPanel, BorderLayout.CENTER);
resultPanel.setLayout(new GridLayout(1, 3, 5, 5));
JPanel producerPanel = new JPanel();
resultPanel.add(producerPanel);
producerPanel.setLayout(new BorderLayout(0, 0));
JLabel producerLabel = new JLabel("\u751F\u4EA7\u8005");
producerLabel.setFont(new Font("微软雅黑", Font.PLAIN, 16));
producerLabel.setHorizontalAlignment(SwingConstants.CENTER);
producerPanel.add(producerLabel, BorderLayout.NORTH);
JScrollPane producerScrollPane = new JScrollPane();
producerPanel.add(producerScrollPane, BorderLayout.CENTER);
producerTextArea = new JTextArea();
producerTextArea.setFont(new Font("微软雅黑", Font.PLAIN, 16));
producerScrollPane.setViewportView(producerTextArea);
JPanel consumerPanel = new JPanel();
resultPanel.add(consumerPanel);
consumerPanel.setLayout(new BorderLayout(0, 0));
JLabel consumerLabel = new JLabel("\u6D88\u8D39\u8005");
consumerLabel.setFont(new Font("微软雅黑", Font.PLAIN, 16));
consumerLabel.setHorizontalAlignment(SwingConstants.CENTER);
consumerPanel.add(consumerLabel, BorderLayout.NORTH);
JScrollPane consumerScrollPane = new JScrollPane();
consumerPanel.add(consumerScrollPane, BorderLayout.CENTER);
consumerTextArea = new JTextArea();
consumerTextArea.setFont(new Font("微软雅黑", Font.PLAIN, 16));
consumerScrollPane.setViewportView(consumerTextArea);
JPanel storagePanel = new JPanel();
resultPanel.add(storagePanel);
storagePanel.setLayout(new BorderLayout(0, 0));
JLabel storageLabel = new JLabel("\u4ED3\u5E93");
storageLabel.setFont(new Font("微软雅黑", Font.PLAIN, 16));
storageLabel.setHorizontalAlignment(SwingConstants.CENTER);
storagePanel.add(storageLabel, BorderLayout.NORTH);
JScrollPane storageScrollPane = new JScrollPane();
storagePanel.add(storageScrollPane, BorderLayout.CENTER);
storageTextArea = new JTextArea();
storageTextArea.setFont(new Font("微软雅黑", Font.PLAIN, 16));
storageScrollPane.setViewportView(storageTextArea);
}
protected void do_startButton_actionPerformed(ActionEvent arg0) {
new Thread(new Producer()).start();
new Thread(new Consumer()).start();
}
private class Producer implements Runnable {
public void run() {
for (int i = 0; i < MAX; i++) {// 向仓库中添加商品,MAX是仓库的最大容量
synchronized (list) {// 使用同步块来解决同步问题
String storage = storageTextArea.getText();// 获得文本域内容
String text = producerTextArea.getText(); // 获得文本域内容
if (list.size() == MAX) {// 如果仓库装满就等待
storageTextArea.setText(storage + "仓库已满\n");
try {
list.wait();// 开始等待
} catch (InterruptedException e) {
e.printStackTrace();
}
} else {
String product = "Java编程宝典";
list.add(product);// 向仓库中添加商品
list.notify();// 唤醒等待的线程
producerTextArea.setText(text + "生产:" + product + "\n");
count++;// 仓库中商品的数量加一
storageTextArea.setText(storage + "仓库中还有" + count + "个商品\n");
try {
Thread.sleep(100);// 当前线程休眠0.1秒
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
}
private class Consumer implements Runnable {
public void run() {
for (int i = 0; i < MAX; i++) {
synchronized (list) {
String storage = storageTextArea.getText();
String text = consumerTextArea.getText();
if (list.size() == 0) {
storageTextArea.setText(storage + "仓库已空\n");
try {
list.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
} else {
consumerTextArea.setText(text + "消费:" + list.removeLast() + "\n");
list.notify();
count--;
storageTextArea.setText(storage + "仓库中还有" + count + "个商品\n");
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
}
}
运行效果:
它的运行逻辑也不难理解:
生产者每次循环都会进行判断是否已经放满了,如果满了,就进入等待状态。如果没满就加入并唤醒等待的消费者线程。
消费者每次循环也会进行判断是否已经空了,如果空了就进入等待,如果不为空就消费并唤醒等待的生产者线程。