1. Axınların oxunması/yazılması zamanı deadlock
Ən xoşagəlməz səhvlərdən biri — proses ilişib tamamlanmır, halbuki hər şey düzgün edilib kimi görünür. Tez‑tez səbəb — çıxış buferinin dolması və ya xarici prosesin səhvləridir. Hər iki axın (stdout və stderr) oxunmasa, proses nəsə çıxarmağa çalışarkən “kilidlənə” bilər, çünki onun buferindən məlumatı götürən yoxdur.
Problem nümunəsi
ProcessBuilder builder = new ProcessBuilder("java", "-version");
Process process = builder.start();
// Yalnız stdout-u oxuyuruq, stderr-i isə yox sayırıq!
BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()));
String line;
while ((line = reader.readLine()) != null) {
System.out.println(line);
}
process.waitFor();
Əgər əmr nəyisə stderr-ə yazırsa (məsələn, java -version demək olar ki, həmişə ora yazır!), bu axın dolur və proses ilişir.
Necə düzgün
Hər iki axını paralel oxuyun (ayrı thread-lər və ya ExecutorService vasitəsilə):
ProcessBuilder builder = new ProcessBuilder("java", "-version");
Process process = builder.start();
// stdout-u oxuyuruq
Thread stdoutThread = new Thread(() -> {
try (BufferedReader reader = new BufferedReader(
new InputStreamReader(process.getInputStream()))) {
String line;
while ((line = reader.readLine()) != null) {
System.out.println("[stdout] " + line);
}
} catch (IOException e) {
e.printStackTrace();
}
});
// stderr-i oxuyuruq
Thread stderrThread = new Thread(() -> {
try (BufferedReader reader = new BufferedReader(
new InputStreamReader(process.getErrorStream()))) {
String line;
while ((line = reader.readLine()) != null) {
System.err.println("[stderr] " + line);
}
} catch (IOException e) {
e.printStackTrace();
}
});
stdoutThread.start();
stderrThread.start();
process.waitFor();
stdoutThread.join();
stderrThread.join();
Nəticə:
Əgər stderr oxunmasa — proses ilişə bilər.
Əgər stdout oxunmasa — o da ilişə bilər.
Hər iki axını oxuyun — və rahat olun!
2. Kodlaşdırma problemləri
Xarici proseslər mətni sizin Java proqramınızın susmaya görə istifadə etdiyindən fərqli kodlaşdırma ilə yaza bilər. Lazımi kodlaşdırmanı göstərməsəniz, mətn əvəzinə anlaşılmaz simvollar görəcəksiniz (xüsusilə kiril yazısında nəzərə çarpır).
Səhv nümunəsi
BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()));
Bu kod sistemin susmaya görə kodlaşdırmasından istifadə edir. Lakin əgər xarici proses, məsələn, UTF-8 ilə yazırsa, sizdə isə Windows-1251-dirsə, kiril mətnləri anlaşılmaz simvollara çevriləcək.
Necə düzgün
Lazım olan kodlaşdırmanı bilirsinizsə, onu açıq şəkildə verin:
BufferedReader reader = new BufferedReader(
new InputStreamReader(process.getInputStream(), StandardCharsets.UTF_8)
);
Əmin deyilsinizsə — proqramın sənədlərini oxuyun və ya bir neçə variantı yoxlayın.
Faydalı məsləhət: Windows-da konsol utilitləri tez-tez CP866 və ya Windows-1251 istifadə edir, Linux-da isə — UTF-8.
3. Platforma fərqləri
Bir OS-də işləyən komandalar digərində olmaya bilər. Məsələn, ls Linux/Mac-də var, amma Windows-da yoxdur (orada — dir). Komandaların sintaksisi, yol ayrıcıları və dırnaqlar fərqlənir.
Səhv nümunəsi
ProcessBuilder builder = new ProcessBuilder("ls", "-l");
builder.start(); // Windows-da: "ls" tapılmadı!
Necə düzgün
String os = System.getProperty("os.name").toLowerCase();
ProcessBuilder builder;
if (os.contains("win")) {
builder = new ProcessBuilder("cmd", "/c", "dir");
} else {
builder = new ProcessBuilder("ls", "-l");
}
Fayl yolu: “/” və ya “\” əvəzinə File.separator istifadə edin ki, yollarla bağlı problem yaşamayasınız.
4. Giriş hüquqları problemləri
Bəzi komandalar administrator səlahiyyətləri tələb edir (sistem fayllarının silinməsi, şəbəkə sazlamalarının dəyişdirilməsi və s.). Hüquqlar kifayət etmədikdə, əmr səhvlə tamamlanacaq və ya ümumiyyətlə işə düşməyəcək.
Nümunə
ProcessBuilder builder = new ProcessBuilder("rm", "-rf", "/root/secret.txt");
Process process = builder.start();
// ... 'Permission denied' xətasını gözləyirik
Necə düzgün
- Əmriniz üçün yüksəldilmiş hüquqların tələb olunub-olunmadığını yoxlayın.
- Prosesin qaytarma kodunu process.exitValue() vasitəsilə emal edin.
- stderr-i oxuyun — səbəb çox vaxt orada olur (məsələn, “Permission denied”).
5. Resurs sızmaları
Prosesin axınları (InputStream, OutputStream, ErrorStream) bağlanmasa, onlar “asılı” qala, resursları tuta və hətta tamamlanmanı bloklaya bilər. Eynilə, prosesi dayandırmasanız, o, sistemdə “zombi”yə çevrilə bilər.
Səhv nümunəsi
Process process = builder.start();
// ... işləyirik, amma axınları bağlamırıq!
Necə düzgün
Axınlar üçün try-with-resources istifadə edin:
try (BufferedReader reader = new BufferedReader(
new InputStreamReader(process.getInputStream()))) {
// Çıxışı oxuyuruq
}
Proseslə iş bitdikdən sonra onu düzgün dayandırın:
process.destroy(); // Prosesi bitirmək (əgər hələ işləyirsə)
Diqqət: axınlar bağlanmasa — yaddaş sızmaları, ilişmələr və sistem problemləri mümkündür (xüsusən proseslər kütləvi işə salınanda).
6. İnteraktiv qarşılıqlı əlaqədə deadlock
İnteraktiv mübadilə zamanı qarşılıqlı bloklanmaya düşmək asandır: Java cavabı gözləyir, xarici proses isə sizin məlumatlarınızı. Nəticədə hər ikisi “susur”. Və ya siz mesaj göndərmisiniz, amma cavabı oxumursunuz — müəyyən anda xarici proqramın buferi dolur və o, yazmanı dayandırır.
Bunun qarşısını almaq üçün vəzifələri ayırın: bir thread oxumağa, digəri isə yazmağa cavabdeh olsun. Beləliklə, qarşılıqlı əlaqə paralel gedir və heç kim heç kimi gözlətmir. Həmçinin iş bitdikdən sonra açıq axınları saxlamayın — sistemi artıq resursları tutmaqdan yayındırmaq üçün onları bağlayın.
GO TO FULL VERSION