Siguria e Temave në Java
Siguria e Temave në Java është një temë shumë e rëndësishme. Java ofron mbështetje mjedisore me shumë fije duke përdorur Java Threads, ne e dimë se thread-et e shumta të krijuara nga i njëjti Objekt ndajnë variabla të objektit dhe kjo mund të çojë në mospërputhje të të dhënave kur fillesat përdoren për të lexuar dhe përditësuar të dhënat e përbashkëta.
Siguria e Fijeve
package com.journaldev.threads;
public class ThreadSafety {
public static void main(String[] args) throws InterruptedException {
ProcessingThread pt = new ProcessingThread();
Thread t1 = new Thread(pt, "t1");
t1.start();
Thread t2 = new Thread(pt, "t2");
t2.start();
//wait for threads to finish processing
t1.join();
t2.join();
System.out.println("Processing count="+pt.getCount());
}
}
class ProcessingThread implements Runnable{
private int count;
@Override
public void run() {
for(int i=1; i < 5; i++){
processSomething(i);
count++;
}
}
public int getCount() {
return this.count;
}
private void processSomething(int i) {
// processing some job
try {
Thread.sleep(i*1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
Në programin e mësipërm për ciklin, count rritet me 1 katër herë dhe duke qenë se kemi dy thread, vlera e tij duhet të jetë 8 pasi të dy thread-at të kenë përfunduar ekzekutimin. Por kur do të ekzekutoni programin e mësipërm disa herë, do të vini re se vlera e numërimit varion midis 6,7,8. Kjo po ndodh sepse edhe nëse count++ duket se është një operacion atomik, NUK ai dhe shkakton prishje të të dhënave.
Siguria e Temave në Java
Siguria e fijeve në java është procesi për ta bërë programin tonë të sigurt për t'u përdorur në mjedisin me shumë fije, ka mënyra të ndryshme përmes të cilave ne mund ta bëjmë të sigurt thread-in e programit tonë.
- Sinkronizimi është mjeti më i lehtë dhe më i përdorur për sigurinë e fijeve në java.
- Përdorimi i klasave Atomic Wrapper nga paketa java.util.concurrent.atomic. Për shembull AtomicInteger
- Përdorimi i bravave nga paketa java.util.concurrent.locks.
- Duke përdorur klasat e mbledhjes së sigurt të temave, kontrolloni këtë postim për përdorimin e ConcurrentHashMap për sigurinë e temave.
- Përdorimi i fjalës kyçe të paqëndrueshme me variabla për të bërë që çdo thread të lexojë të dhënat nga memoria, jo të lexojë nga memoria e temave.
Java e sinkronizuar
Sinkronizimi është mjeti me të cilin mund të arrijmë sigurinë e thread-it, JVM garanton që kodi i sinkronizuar do të ekzekutohet vetëm nga një thread në të njëjtën kohë. fjala kyçe Java e sinkronizuar përdoret për të krijuar kodin e sinkronizuar dhe nga brenda përdor kyçjet në Object ose Class për të siguruar që vetëm një thread po ekzekuton kodin e sinkronizuar.
- Sinkronizimi Java punon në mbylljen dhe zhbllokimin e burimit përpara se ndonjë thread të hyjë në kodin e sinkronizuar, ai duhet të marrë bllokimin në Objekt dhe kur ekzekutimi i kodit përfundon, ai zhbllokon burimin që mund të bllokohet nga threads të tjerë. Ndërkohë, temat e tjera janë në gjendje pritjeje për të kyçur burimin e sinkronizuar.
- Ne mund të përdorim fjalë kyçe të sinkronizuara në dy mënyra, njëra është të sinkronizojmë një metodë të plotë dhe një mënyrë tjetër është të krijojmë bllok të sinkronizuar.
- Kur një metodë sinkronizohet, ajo bllokon Objektin, nëse metoda është statike, ajo bllokon Klasën, kështu që është gjithmonë praktika më e mirë të përdoret blloku i sinkronizuar për të kyçur seksionet e vetme të metodës që kanë nevojë për sinkronizim.
- Gjatë krijimit të një blloku të sinkronizuar, ne duhet të sigurojmë burimin në të cilin do të merret bllokimi, ai mund të jetë XYZ.class ose çdo fushë Object e klasës.
synchronized(this)
do të bllokojë objektin përpara se të hyjë në bllokun e sinkronizuar.- Duhet të përdorni nivelin më të ulët të kyçjes, për shembull, nëse ka disa blloqe të sinkronizuara në një klasë dhe njëri prej tyre është duke kyçur Objektin, atëherë blloqet e tjera të sinkronizuara gjithashtu nuk do të jenë të disponueshme për ekzekutimi nga fijet e tjera. Kur bllokojmë një Objekt, ai merr një bllokim në të gjitha fushat e Objektit.
- Sinkronizimi Java siguron integritet të të dhënave për koston e performancës, kështu që duhet të përdoret vetëm kur është absolutisht e nevojshme.
- Sinkronizimi Java funksionon vetëm në të njëjtin JVM, kështu që nëse ju duhet të kyçni disa burime në një mjedis të shumëfishtë JVM, ai nuk do të funksionojë dhe mund t'ju duhet të kujdeseni për një mekanizëm global mbylljeje.
- Sinkronizimi Java mund të rezultojë në bllokime, kontrolloni këtë postim rreth bllokimit në java dhe si t'i shmangni ato.
- Fjala kyçe e sinkronizuar Java nuk mund të përdoret për konstruktorët dhe variablat.
- Preferohet të krijohet një objekt privat i rremë për t'u përdorur për bllokun e sinkronizuar në mënyrë që referenca e tij të mos mund të ndryshohet nga ndonjë kod tjetër. Për shembull, nëse keni një metodë vendosëse për Objektin në të cilin po sinkronizoni, referenca e saj mund të ndryshohet nga disa kode të tjera të çon drejt ekzekutimit paralel të bllokut të sinkronizuar.
- Ne nuk duhet të përdorim asnjë objekt që mbahet në një grup konstant, për shembull String nuk duhet të përdoret për sinkronizim sepse nëse ndonjë kod tjetër është gjithashtu i kyçur në të njëjtin varg, ai do të përpiqet të marrë bllokimin në të njëjtin objekt referimi nga Pishina e vargjeve dhe edhe pse të dy kodet nuk kanë lidhje, ato do të bllokojnë njëri-tjetrin.
Këtu janë ndryshimet e kodit që duhet të bëjmë në programin e mësipërm për ta bërë atë të sigurt në fije.
//dummy object variable for synchronization
private Object mutex=new Object();
...
//using synchronized block to read, increment and update count value synchronously
synchronized (mutex) {
count++;
}
Le të shohim disa shembuj sinkronizimi dhe çfarë mund të mësojmë prej tyre.
public class MyObject {
// Locks on the object's monitor
public synchronized void doSomething() {
// ...
}
}
// Hackers code
MyObject myObject = new MyObject();
synchronized (myObject) {
while (true) {
// Indefinitely delay myObject
Thread.sleep(Integer.MAX_VALUE);
}
}
Vini re se kodi i hakerit po përpiqet të bllokojë shembullin myObject dhe pasi të marrë bllokimin, nuk po e lëshon kurrë atë duke shkaktuar bllokimin e metodës doSomething() në pritjen e bllokimit, kjo do të bëjë që sistemi të shkojë në bllokim dhe të shkaktojë Refuzim të Shërbimit ( DoS).
public class MyObject {
public Object lock = new Object();
public void doSomething() {
synchronized (lock) {
// ...
}
}
}
//untrusted code
MyObject myObject = new MyObject();
//change the lock Object reference
myObject.lock = new Object();
Vini re se objekti i bllokimit është publik dhe duke ndryshuar referencën e tij, ne mund të ekzekutojmë bllok të sinkronizuar paralelisht në fije të shumta. Një rast i ngjashëm është i vërtetë nëse keni Objekt privat por keni një metodë vendosëse për të ndryshuar referencën e tij.
public class MyObject {
//locks on the class object's monitor
public static synchronized void doSomething() {
// ...
}
}
// hackers code
synchronized (MyObject.class) {
while (true) {
Thread.sleep(Integer.MAX_VALUE); // Indefinitely delay MyObject
}
}
Vini re se kodi i hakerit po bllokohet në monitorin e klasës dhe nuk e lëshon atë, do të shkaktojë bllokim dhe DoS në sistem. Këtu është një shembull tjetër ku threads të shumtë janë duke punuar në të njëjtin grup vargjesh dhe pasi të përpunohen, duke shtuar emrin e fillit në vlerën e grupit.
package com.journaldev.threads;
import java.util.Arrays;
public class SyncronizedMethod {
public static void main(String[] args) throws InterruptedException {
String[] arr = {"1","2","3","4","5","6"};
HashMapProcessor hmp = new HashMapProcessor(arr);
Thread t1=new Thread(hmp, "t1");
Thread t2=new Thread(hmp, "t2");
Thread t3=new Thread(hmp, "t3");
long start = System.currentTimeMillis();
//start all the threads
t1.start();t2.start();t3.start();
//wait for threads to finish
t1.join();t2.join();t3.join();
System.out.println("Time taken= "+(System.currentTimeMillis()-start));
//check the shared variable value now
System.out.println(Arrays.asList(hmp.getMap()));
}
}
class HashMapProcessor implements Runnable{
private String[] strArr = null;
public HashMapProcessor(String[] m){
this.strArr=m;
}
public String[] getMap() {
return strArr;
}
@Override
public void run() {
processArr(Thread.currentThread().getName());
}
private void processArr(String name) {
for(int i=0; i < strArr.length; i++){
//process data and append thread name
processSomething(i);
addThreadName(i, name);
}
}
private void addThreadName(int i, String name) {
strArr[i] = strArr[i] +":"+name;
}
private void processSomething(int index) {
// processing some job
try {
Thread.sleep(index*1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
Këtu është dalja kur ekzekutoj programin e mësipërm.
Time taken= 15005
[1:t2:t3, 2:t1, 3:t3, 4:t1:t3, 5:t2:t1, 6:t3]
Vlerat e vargut të vargut janë të dëmtuara për shkak të të dhënave të përbashkëta dhe pa sinkronizim. Ja se si mund të ndryshojmë metodën addThreadName() për ta bërë programin tonë të sigurt në lidhje.
private Object lock = new Object();
private void addThreadName(int i, String name) {
synchronized(lock){
strArr[i] = strArr[i] +":"+name;
}
}
Pas këtij ndryshimi, programi ynë funksionon mirë dhe këtu është prodhimi i saktë i programit.
Time taken= 15004
[1:t1:t2:t3, 2:t2:t1:t3, 3:t2:t3:t1, 4:t3:t2:t1, 5:t2:t1:t3, 6:t2:t1:t3]
Kjo është e gjitha për sigurinë e temave në java, shpresoj se keni mësuar rreth programimit të sigurt në fije dhe përdorimit të fjalëve kyçe të sinkronizuara.