Сделать существующий код в Java параллельным / многопоточным
У меня очень простой гусеничный ход. Я хочу, чтобы мой текущий код выполнялся в несколько потоков. Не могли бы вы предоставить мне небольшой учебник или статью, чтобы помочь мне пройти этот тест?
Изначально я являюсь разработчиком.Net, и в.Net у меня нет проблем с выполнением кодов в многопоточном режиме, но, к сожалению, я ничего не знаю о потоках в Java.
Мой сканер является программным обеспечением командной строки, поэтому не беспокойтесь о графическом интерфейсе.
Заранее спасибо.
4 ответа
Java выполняет многопоточность через класс Thread. Один из самых распространенных способов сделать существующий код многопоточным - использовать интерфейс Runnable, чтобы определить, что вы хотите вызвать при запуске потока, а затем запустить его.
public class SomeFunctions
{
public static void FunctionA() {}
public static void FunctionB() {}
public static void FunctionC() {}
}
// ...
Thread t1 = new Thread(new Runnable() {
public void run() {
SomeFunctions.FunctionA();
}
});
t1.start();
// (rinse and repeat for the other functions)
Сухой код, но он должен, по крайней мере, передать общую концепцию. Конечно, как только вы входите в многопоточную среду, у вас возникают проблемы с параллелизмом, и вы должны убедиться, что все правильно синхронизировано и т. Д., Но любой язык будет иметь эти проблемы.
Если вы беспокоитесь о синхронизации, у вас есть несколько инструментов в вашем распоряжении. Самым простым является встроенная в Java функция рекурсивного мьютекса - ключевое слово "synchronized". Более классические средства также доступны через различные классы в пакетах java.util.concurrent и java.util.concurrent.locks, таких как Semaphore и ReadWriteLock.
http://download.oracle.com/javase/6/docs/api/java/util/concurrent/package-summary.html http://download.oracle.com/javase/6/docs/api/java/util /concurrent/locks/package-summary.html
Вы можете взглянуть на мой пример веб-сканера. Извините за длительность.
import java.net.MalformedURLException;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
/**
* A web crawler with a Worker pool
*
* @author Adriaan
*/
public class WebCrawler implements Manager {
private Set<Worker> workers = new HashSet<Worker>();
private List<String> toCrawl = new ArrayList<String>();
private Set<String> crawled = new HashSet<String>();
private Set<String> hosts = new HashSet<String>();
private Set<String> results = new HashSet<String>();
private int maxResults;
public WebCrawler(String url, int numberOfWorkers, int maxResults) {
this.maxResults = maxResults;
toCrawl.add(url);
createWorkers(numberOfWorkers);
}
public void createWorkers(int numberOfWorkers) {
for (int i = 0; i < numberOfWorkers; i++) {
workers.add(new Worker(this));
}
}
private void stopWorkers() {
for (Worker worker : workers) {
worker.terminate();
}
}
public synchronized Job getNewJob() {
while (toCrawl.size() == 0) {
try {
wait();
} catch (InterruptedException e) {
// ignore
}
}
return new EmailAddressCrawlJob().setDescription(toCrawl.remove(0));
}
public synchronized void jobCompleted(Job job) {
// System.out.println("crawled: " + job.getDescription());
crawled.add(job.getDescription());
String host = getHost(job.getDescription());
boolean knownHost = hosts.contains(host);
if (!knownHost) {
System.out.println("host: " + host);
hosts.add(host);
}
for (String url : job.getNewDescriptions()) {
if (!crawled.contains(url)) {
if (knownHost) {
toCrawl.add(toCrawl.size() - 1, url);
} else {
toCrawl.add(url);
}
}
}
for (String result : job.getResults()) {
if (results.add(result)) {
System.out.println("result: " + result);
}
}
notifyAll();
if (results.size() >= maxResults) {
stopWorkers();
System.out.println("Crawled hosts:");
for (String crawledHost : hosts) {
System.out.println(crawledHost);
}
Set<String> uncrawledHosts = new HashSet<String>();
for (String toCrawlUrl : toCrawl) {
uncrawledHosts.add(getHost(toCrawlUrl));
}
System.out.println("Uncrawled hosts:");
for (String unCrawledHost : uncrawledHosts) {
System.out.println(unCrawledHost);
}
}
if (crawled.size() % 10 == 0) {
System.out.println("crawled=" + crawled.size() + " toCrawl="
+ toCrawl.size() + " results=" + results.size() + " hosts="
+ hosts.size() + " lastHost=" + host);
}
}
public String getHost(String host) {
int hostStart = host.indexOf("://") + 3;
if (hostStart > 0) {
int hostEnd = host.indexOf("/", hostStart);
if (hostEnd < 0) {
hostEnd = host.length();
}
host = host.substring(hostStart, hostEnd);
}
return host;
}
public static void main(String[] args) throws MalformedURLException {
new WebCrawler("http://www.nu.nl/", 5, 20);
}
}
работник
**
* A Worker proactively gets a Job, executes it and notifies its manager that
* the Job is completed.
*
* @author Adriaan
*/
public class Worker extends Thread {
private final Manager manager;
private Job job = null;
private boolean isWorking;
public Worker(Manager manager) {
this.manager = manager;
isWorking = true;
start();
}
@Override
public void run() {
System.out.println("Worker " + Thread.currentThread().getId()
+ " starting ");
while (isWorking) {
job = manager.getNewJob();
job.execute();
manager.jobCompleted(job);
}
}
public void terminate() {
isWorking = false;
}
}
Интерфейс менеджера
/**
* Manager interface for Workers
*
* @author Adriaan
*/
public interface Manager {
/**
* Gets a new job
*
* @return
*/
public Job getNewJob();
/**
* Indicates the job is completed
*
* @param job
*/
public void jobCompleted(Job job);
}
работа
import java.util.HashSet;
import java.util.Set;
/**
* A Job is a unit of work defined by a String (the description). During execution the
* job can obtain results and new job descriptions.
*
* @author Adriaan
*/
public abstract class Job {
private String description;
private Set<String> results = new HashSet<String>();
private Set<String> newDescriptions = new HashSet<String>();
/**
* Sets the job description
*
* @param description
* @return this for chaining
*/
public Job setDescription(String description) {
this.description = description;
return this;
}
/**
* Executes the job
*/
public abstract void execute();
/**
* Gets the results obtained
*
* @return
*/
public Set<String> getResults() {
return results;
}
/**
* Gets the now job descriptions obtained
*
* @return
*/
public Set<String> getNewDescriptions() {
return newDescriptions;
}
/**
* Gets the job description
*
* @return
*/
public String getDescription() {
return description;
}
/**
* Allows the implementation to add an obtained result
*
* @param result
*/
void addResult(String result) {
results.add(result);
}
/**
* Allows the implementation to add an obtained description
*
* @param result
*/
void addNewDescription(String newDescription) {
newDescriptions.add(newDescription);
}
}
Работа, которая сканирует страницу для адресов электронной почты:
import java.io.IOException;
import java.io.InputStream;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLConnection;
import java.util.StringTokenizer;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* A Job which crawls HTTP or HTTPS URL's for email adresses, collecting new
* URL's to crawl along the way.
*
* @author Adriaan
*/
public class EmailAddressCrawlJob extends Job {
@Override
public void execute() {
try {
URL url = new URL(getDescription());
if (url != null) {
String text = readText(url);
extractNewDescriptions(text, url);
extractResults(text);
}
} catch (MalformedURLException e) {
System.err.println("Bad url " + getDescription());
}
}
private String readText(URL url) {
URLConnection connection;
try {
connection = url.openConnection();
InputStream input = connection.getInputStream();
byte[] buffer = new byte[1000];
int num = input.read(buffer);
if (num > 0) {
StringBuilder builder = new StringBuilder();
builder.append(new String(buffer, 0, num));
while (num != -1) {
num = input.read(buffer);
if (num != -1) {
builder.append(new String(buffer, 0, num));
}
}
return builder.toString();
}
} catch (IOException e) {
//System.err.println("Could not read from " + url);
}
return "";
}
private void extractNewDescriptions(String text, URL url) {
// URL extracting code from Sun example
String lowerCaseContent = text.toLowerCase();
int index = 0;
while ((index = lowerCaseContent.indexOf("<a", index)) != -1) {
if ((index = lowerCaseContent.indexOf("href", index)) == -1) {
break;
}
if ((index = lowerCaseContent.indexOf("=", index)) == -1) {
break;
}
index++;
String remaining = text.substring(index);
StringTokenizer st = new StringTokenizer(remaining, "\t\n\r\">#");
String strLink = st.nextToken();
if (strLink.startsWith("javascript:")) {
continue;
}
URL urlLink;
try {
urlLink = new URL(url, strLink);
strLink = urlLink.toString();
} catch (MalformedURLException e) {
// System.err.println("Could not create url: " + target
// + " + " + strLink);
continue;
}
// only look at http links
String protocol = urlLink.getProtocol();
if (protocol.compareTo("http") != 0
&& protocol.compareTo("https") != 0) {
// System.err.println("Ignoring: " + protocol
// + " protocol in " + urlLink);
continue;
}
addNewDescription(urlLink.toString());
}
}
private void extractResults(String text) {
Pattern p = Pattern
.compile("([\\w\\-]([\\.\\w])+[\\w]+@([\\w\\-]+\\.)+[A-Za-z]{2,4})");
Matcher m = p.matcher(text);
while (m.find()) {
addResult(m.group(1));
}
}
}
Я знаю, что этот ответ немного многословен, но я подумал, что OP лучше всего поможет с рабочим примером, и я случайно сделал его не так давно.
Очень простая Java-программа, которая даст абстрактное представление о многопоточности.
public class MyThread extends Thread {
String word;
public MyThread(String rm){
word = rm;
}
public void run(){
try {
for(;;){
System.out.println(word);
Thread.sleep(1000);
}
} catch(InterruptedException e) {
System.out.println("sleep interrupted");
}
}
public static void main(String[] args) {
Thread t1=new MyThread("First Thread");
Thread t2=new MyThread("Second Thread");
t1.start();
t2.start();
}
}
И выход будет..
First Thread
Second Thread
First Thread
Second Thread
First Thread
Перейти с этим PPT, он поможет вам с основами..
Используйте Runnable вместо расширения Thread:
Вместо расширения класса Thread лучше реализовать интерфейс Runnable, который отделяет задачу запуска потока от самого потока. Реализуя интерфейс Runnable, метод run() будет вызываться для объекта, переданного конструктору, и мы можем использовать один объект Thread для выполнения нескольких объектов Runnable.
Используйте константу для времени сна:
Вместо жесткого кодирования времени сна (1000 миллисекунд) используйте константу, чтобы сделать ее более читабельной и легкой для изменения. Например, вы можете объявить частный статический финал int SLEEP_TIME = 1000; в начале класса и используйте его в вызове Sleep().
public class MyRunnable implements Runnable {
private String word;
public MyRunnable(String word) {
this.word = word;
}
@Override
public void run() {
try {
while (true) {
System.out.println(word);
Thread.sleep(SLEEP_TIME);
}
} catch (InterruptedException e) {
System.out.println("sleep interrupted");
}
}
private static final int SLEEP_TIME = 1000;
public static void main(String[] args) {
Thread t1 = new Thread(new MyRunnable("First Thread"));
Thread t2 = new Thread(new MyRunnable("Second Thread"));
t1.start();
t2.start();
}
}