概述
多线程(英语:multithreading),是指从软件或者硬件上实现多个线程并发执行的技术。具有多线程能力的计算机因有硬件支持而能够在同一时间执行多于一个线程,进而提升整体处理性能。( 百度百科 )进程(process):运行中的程序,一个进程中可以包含多个线程。
java多线程中的关键类
Thread类
A thread is a thread of execution in a program. The Java Virtual Machine allows an application to have multiple threads of execution running concurrently.一个线程类的对象就代表着一个可运行的线程。java虚拟机允许多个线程在一个应用程序中同步运行。
Runnable接口
一个线程要执行的代码与Runnable接口有关。Runnable接口的实现类需要实现Runnable 中的run方法,这个方法中的代码就是对应线程需要执行的代码。可以理解为线程的任务。看代码:
package me.paul.example;
public class ThreadDemo {
public static void main(String[] args){
//创建任务
Task task = new Task();
//创建线程,并把任务交给他
Thread t = new Thread(task);
//提示线程,可以开始执行任务,但具体的执行由java虚拟机决定
t.start();
for(int i=0;i<5;i++){
//打印线程的名字加上循环计数i
System.out.println(Thread.currentThread().getName() + " : " + i);
}
}
//在类中定义一个静态内部类名为Task,实现了Runnable接口,将作为线程的执行任务
public static class Task implements Runnable {
public void run() {
for(int i=0;i<5;i++){
System.out.println(Thread.currentThread().getName() + " : " + i);
}
}
}
}
用Runnable接口来指定线程执行只是一种方法,还有其他的方法。
这段代码的结果是:
也有可能是这样:
从这可以看出以下几点
- 线程的执行是不定的。不能确定哪个线程先执行或先执行完。
- 线程的命名,main函数所在的线程名为:main ;自己创建的线程的命名规则为:第一个Thread-0,第二个Thread-1...
- 线程中的任务会被执行完(如果里面是死循环就...)
优点(来自百度百科)
例子
swing:
点击按钮,执行一个耗时长的操作,新建一个线程来执行,图形界面会有更好的体验。package me.paul.example;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JOptionPane;
public class SwingDemo {
private JFrame frame;
private Container container ;
private JButton button;
public SwingDemo(){
frame = new JFrame("SwingDemo");
container = frame.getContentPane();
button = new JButton("点击");
}
public void init(){
frame.setSize(new Dimension(600,400));
button.addActionListener(new ActionListener(){
//点击按钮时所执行的方法
public void actionPerformed(ActionEvent event) {
//这个操作极其傻逼,请勿模仿
String str = "";
for(long i=0;i<999999999;i++){
str += String.valueOf(i);
}
JOptionPane.showMessageDialog(SwingDemo.this.container, str);
}
);
container.add(button);
frame.setVisible(true);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
public static void main(String[] args){
SwingDemo sd = new SwingDemo();
sd.init();
}
}
执行这个程序,点击按钮,窗口就会卡住,不能进行任何操作,甚至不能关闭,因为主线程正在忙。
将actionPerform方法改成这样,窗口就不会卡住。
public void actionPerformed(ActionEvent event) {
//新建一个线程来执行操作,不会造成主线程被阻塞
new Thread(){
public void run() {
String str = "";
for(long i=0;i<999999999;i++){
str += String.valueOf(i);
}
JOptionPane.showMessageDialog(SwingDemo.this.container, str);
}
}.start();
}
例子2:多线程复制文件
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.RandomAccessFile;
public final class CopyUtil {
private static final int DEFAULT_THREAD_NUMBER = 3;//默认采用的线程数
private File src;//源文件
private File target;//目标文件
private long totalLength;//文件的总大小
private long perThreadLength;//每个线程需要复制的文件大小
private int threadNumber;//实际使用的线程数
public CopyUtil(File src, File target) throws IOException {
this(src, target, DEFAULT_THREAD_NUMBER);
}
//构造方法,初始化各个东西
public CopyUtil(File src, File target, int threadNumber) throws IOException{
this.src = src;
if(target.isDirectory()){
target = new File(target,src.getName());
}
target.createNewFile();
this.target = target;
this.threadNumber = threadNumber == 0?DEFAULT_THREAD_NUMBER:threadNumber;
totalLength = src.length();
perThreadLength = totalLength / this.threadNumber;
}
//调用这个方法,将会开始复制
public void copy() throws FileNotFoundException {
long lastThread = totalLength - perThreadLength * (threadNumber - 1);
for (int i = 0; i < threadNumber - 1; i++) {
//创建线程和任务
new Thread(new CopyTask(i, perThreadLength)).start();
}
//创建线程和任务
new Thread(new CopyTask(threadNumber - 1, lastThread)).start();
}
//内部类,代表复制的任务
private class CopyTask implements Runnable {
private RandomAccessFile src;
private RandomAccessFile target;
private long from;
private long to;
CopyTask(int no, long thisSize) throws FileNotFoundException {
this.src = new RandomAccessFile(CopyUtil.this.src, "r");
this.target = new RandomAccessFile(CopyUtil.this.target, "rw");
this.from = no * perThreadLength;
this.to = from + thisSize - 1;
}
public void run() {
try {
target.setLength(totalLength);
src.seek(from);
target.seek(from);
byte[] buffer = new byte[10 * 1024];
int count = 0;
while ((count = src.read(buffer)) > 0){
if(to < src.getFilePointer()){
int toWrite = (int) (to - target.getFilePointer() + 1);
target.write(buffer,0,toWrite);
}else{
target.write(buffer, 0, count);
}
}
target.close();
src.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
//在主函数中调用
public static void main(String[] args) throws FileNotFoundException,IOException{
File src = new File(args[0]);
File target = new File(args[1]);
int threadNumber = Integer.parseInt(args[2]);
CopyUtil cu = new CopyUtil(src,target,threadNumber);
cu.copy();
}
}
编译后运行,
将Downloads下的google-chrome-stable_current_amd64.deb复制到Desktop下,并进行了md5验证,Linux还是挺方便的。
多线程的执行效率问题
在没有缓存的情况下,复制一个880M的文件linux系统的cp指令用时大概20秒
java 一个线程,用时27秒
java 5个线程,用时67秒
多线程未必就一定比单线程高效,可能需要有优化技巧。我一直以为多线程比单线程快,naive