Basit Kilitlenme Örnekleri


92

Yeni başlayanlara iş parçacığı kilitlenmelerini açıklamak istiyorum. Geçmişte, bazıları kod kullanan ve bazıları resim kullanan (ünlü 4 araba gibi ) birçok kilitlenme örneği gördüm . The Dining Philosophers gibi kolayca çıkmaza giren klasik sorunlar da vardır , ancak bunlar gerçek bir aceminin tam olarak kavrayamayacağı kadar karmaşık olabilir.

Kilitlenmelerin ne olduğunu göstermek için en basit kod örneğini arıyorum. Örnek şunları yapmalıdır:

  1. Biraz mantıklı olan "gerçek" bir programlama senaryosuyla ilişkilendirin
  2. Çok kısa, basit ve açık sözlü olun

Ne önerirsiniz?


Bana oldukça basit göründüğü için neden ünlü 4 arabayı kullanmıyorsunuz?
vehomzzz

2
4 araba bir programlama senaryosu değildir ve bir acemi için sorunu 4 arabanın biçimine soyutlamak önemsiz değildir. Bunları kullanıyorum, ancak kilitlenmenin meydana geldiği bir programlama senaryosu göstermek istiyorum.
Roee Adler

Yanıtlar:


139

Belki basit bir banka durumu.

class Account {
  double balance;

  void withdraw(double amount){
     balance -= amount;
  } 

  void deposit(double amount){
     balance += amount;
  } 

   void transfer(Account from, Account to, double amount){
        sync(from);
        sync(to);

        from.withdraw(amount);
        to.deposit(amount);

        release(to);
        release(from);
    }

}

Açıktır ki, aynı anda transfer ( a, b ) ve transfer ( b, a ) ' yı çalıştırmaya çalışan iki iş parçacığı olması durumunda , kaynakları ters sırada almaya çalıştıkları için bir kilitlenme meydana gelecektir.

Bu kod aynı zamanda kilitlenmenin çözümlerine bakmak için de harikadır. Bu yardımcı olur umarım!


1
Siz veya bir başkası bu soruna çözüm getirebilirse mükemmel olur.
Jacky

2
@Jacky Solution, Will Hartung tarafından burada yayınlanmıştır: stackoverflow.com/questions/13326861/avoid-deadlock-example/…
Piotr Chojnacki

2
Söz diziminiz kafam karıştı. Sync () yöntemi nedir? Sync (from); ... release (from); değiştirildi (itibaren) {...}
Ellen Spertus

1
@espertus syncgibi bir şey olabilir: sync(Account & a) { a.mutex.lock(); }.
vladon

1
javaworld.com/article/2075692/java-concurrency/… (Brian Goetz) bu sorunun çözümünü açıklıyor.
lingareddyk

59

Doğa çıkmaz açıklasın,

Çıkmaz: Kurbağa ve Yılan

Fotoğrafçı, "Onların ayrı yollarına gittiklerini görmeyi çok isterdim, ancak çok yorulmuştum" dedi. "Kurbağa her zaman yılanı koparmaya çalışıyordu , ama yılan bırakmadı " .

görüntü açıklamasını buraya girin


59
Sevimli, ancak bir programlama bağlamında kilitlenmelerin nasıl meydana geldiğini açıklamıyor.
jalf

tamam jalf, en azından olumsuz oyu haklı çıkardın. Her neyse, "4 araba" örneğine benzer. Bir çıkmazın nasıl göründüğünün sevimli bir temsili.
Nick Dandoulakis

@Nick Dandoulakis: Mükemmel resimsel sunum. Resim, kilitlenme kavramını açıklıyor
Rasmi Ranjan Nayak,

@NickDandoulakis - İyi bir resim örneği değil imho. Basit bir kod burada yardımcı olacaktır.
Erran Morad

13
Bu nasıl SEVİMLİ olmalı? Zehirli yılanlar ve kurbağalar birbirlerini yiyorlar ve bu korkutucu !!!
vikkyhacks

56

İşte Tayvan'daki bir üniversitenin bilgisayar bilimleri bölümünden kaynak kilitlemeli basit bir java örneği gösteren bir kod örneği . Bu benim için çok "gerçek hayat". Aşağıdaki kod:

/**
 * Adapted from The Java Tutorial
 * Second Edition by Campione, M. and
 * Walrath, K.Addison-Wesley 1998
 */

/**
 * This is a demonstration of how NOT to write multi-threaded programs.
 * It is a program that purposely causes deadlock between two threads that
 * are both trying to acquire locks for the same two resources.
 * To avoid this sort of deadlock when locking multiple resources, all threads
 * should always acquire their locks in the same order.
 **/
public class Deadlock {
  public static void main(String[] args){
    //These are the two resource objects 
    //we'll try to get locks for
    final Object resource1 = "resource1";
    final Object resource2 = "resource2";
    //Here's the first thread.
    //It tries to lock resource1 then resource2
    Thread t1 = new Thread() {
      public void run() {
        //Lock resource 1
        synchronized(resource1){
          System.out.println("Thread 1: locked resource 1");
          //Pause for a bit, simulating some file I/O or 
          //something. Basically, we just want to give the 
          //other thread a chance to run. Threads and deadlock
          //are asynchronous things, but we're trying to force 
          //deadlock to happen here...
          try{ 
            Thread.sleep(50); 
          } catch (InterruptedException e) {}

          //Now wait 'till we can get a lock on resource 2
          synchronized(resource2){
            System.out.println("Thread 1: locked resource 2");
          }
        }
      }
    };

    //Here's the second thread.  
    //It tries to lock resource2 then resource1
    Thread t2 = new Thread(){
      public void run(){
        //This thread locks resource 2 right away
        synchronized(resource2){
          System.out.println("Thread 2: locked resource 2");
          //Then it pauses, for the same reason as the first 
          //thread does
          try{
            Thread.sleep(50); 
          } catch (InterruptedException e){}

          //Then it tries to lock resource1.  
          //But wait!  Thread 1 locked resource1, and 
          //won't release it till it gets a lock on resource2.  
          //This thread holds the lock on resource2, and won't
          //release it till it gets resource1.  
          //We're at an impasse. Neither thread can run, 
          //and the program freezes up.
          synchronized(resource1){
            System.out.println("Thread 2: locked resource 1");
          }
        }
      }
    };

    //Start the two threads. 
    //If all goes as planned, deadlock will occur, 
    //and the program will never exit.
    t1.start(); 
    t2.start();
  }
}

2
Sorun, bunun gerçekten bir "gerçek hayat" örneği olmamasıdır. Bu "kaynak 1" ve "kaynak 2" ile ilgili ve bunu gerçek bir programlama problemiyle ilişkilendirmek güzel olurdu (yani, pratikte doğrudan kullanılabilir, problem alanına atıfta bulunarak vb.)
Jay

7
Bence güzel bir örnek. Teşekkürler.
James Raitsev

Bu kod birkaç farklı kitapta yayınlanmış gibi görünüyor ... stackoverflow.com/a/11338853/112705
Dan J

15

Method1 () ve method2 () iki veya daha fazla iş parçacığı tarafından çağrılacaksa, iyi bir kilitlenme olasılığı vardır çünkü eğer evre 1, method1 () yürütülürken String nesnesi üzerinde kilit elde ederse ve 2 numaralı evre, method2'yi yürütürken Tamsayı nesnesinde kilit elde ederse () her ikisi de birbirlerinin Tamsayı üzerindeki kilidi serbest bırakmasını ve daha fazla ilerlemek için Dize'yi bekleyecek, ki bu asla olmayacak.

public void method1() {
    synchronized (String.class) {
        System.out.println("Acquired lock on String.class object");

        synchronized (Integer.class) {
            System.out.println("Acquired lock on Integer.class object");
        }
    }
}

public void method2() {
    synchronized (Integer.class) {
        System.out.println("Acquired lock on Integer.class object");

        synchronized (String.class) {
            System.out.println("Acquired lock on String.class object");
        }
    }
}

Hızlı ve basit. Güzel.
user1068352

13

Karşılaştığım basit kilitlenme örneklerinden biri.

public class SimpleDeadLock {
   public static Object l1 = new Object();
   public static Object l2 = new Object();
   private int index;
   public static void main(String[] a) {
      Thread t1 = new Thread1();
      Thread t2 = new Thread2();
      t1.start();
      t2.start();
   }
   private static class Thread1 extends Thread {
      public void run() {
         synchronized (l1) {
            System.out.println("Thread 1: Holding lock 1...");
            try { Thread.sleep(10); }
            catch (InterruptedException e) {}
            System.out.println("Thread 1: Waiting for lock 2...");
            synchronized (l2) {
               System.out.println("Thread 2: Holding lock 1 & 2...");
            }
         }
      }
   }
   private static class Thread2 extends Thread {
      public void run() {
         synchronized (l2) {
            System.out.println("Thread 2: Holding lock 2...");
            try { Thread.sleep(10); }
            catch (InterruptedException e) {}
            System.out.println("Thread 2: Waiting for lock 1...");
            synchronized (l1) {
               System.out.println("Thread 2: Holding lock 2 & 1...");
            }
         }
      }
   }
}

Bu örneği beğendim. Peki SimpleDeadLock sınıfı neden Thread'den çıkar? Bu gerekli değil.
Charmin

1
Bu, şu yanıtla hemen hemen aynı şey: stackoverflow.com/a/1385868/1310566 . Ve bunun private int indexorada ne işi var?
Simon Forsberg

6

İşte C ++ 11'de basit bir örnek.

#include <mutex>    // mutex
#include <iostream> // cout 
#include <cstdio>   // getchar
#include <thread>   // this_thread, yield
#include <future>   // async
#include <chrono>   // seconds

using namespace std;
mutex _m1;
mutex _m2;

// Deadlock will occur because func12 and func21 acquires the two locks in reverse order

void func12()
{
    unique_lock<mutex> l1(_m1);
    this_thread::yield(); // hint to reschedule
    this_thread::sleep_for( chrono::seconds(1) );
    unique_lock<mutex> l2(_m2 );
}

void func21()
{
    unique_lock<mutex> l2(_m2);
    this_thread::yield(); // hint to reschedule
    this_thread::sleep_for( chrono::seconds(1) );
    unique_lock<mutex> l1(_m1);
}

int main( int argc, char* argv[] )
{
    async(func12);
    func21();
    cout << "All done!"; // this won't be executed because of deadlock
    getchar();
}

5

Lütfen bu soruya verdiğim cevaba bakınız . Alt satırda, iki iş parçacığının iki farklı kaynak edinmesi gerektiğinde ve bunu farklı sıralarda yaparsanız, kilitlenmelerle karşılaşabilirsiniz.


2
Burada başka bir cevaptan gelen bilgileri çoğaltmanın anlamını gerçekten görmüyorum. Bu cevabın geliştirilebileceğini düşünüyorsanız, kendiniz düzenlemekte özgürsünüz.
djna

Sanırım bu duruma "kilitlemeli ters çevirme" deniyor. Pekala, buna kilitlemeli ters çevirme denildiğini biliyorum, çünkü ona öyle diyorum, ama bence bu aynı zamanda onun için sanat terimi :-)
Steve Jessop

4

Aklıma gelen bir örnek Masa, El Feneri ve Piller senaryosu. Bir el feneri ve bir masanın üstüne yerleştirilmiş bir çift pil düşünün. Bu masaya gidip, başka bir kişi el fenerine sahipken pilleri kaparsanız, ikiniz de eşyalarını masaya ilk önce kimin koyacağını beklerken garip bir şekilde birbirinize bakmaya zorlanacaksınız. Bu bir kilitlenme örneğidir. Siz ve kişi kaynakları bekliyorsunuz ama hiçbiriniz kaynaklarından vazgeçmiyorsunuz.

Benzer şekilde, bir programda, iki veya daha fazla iş parçacığı (siz ve diğer kişi) iki veya daha fazla kilidin (el feneri ve piller) serbest bırakılmasını beklediğinizde ve programdaki koşullar kilitlerin asla serbest kalmayacağı şekilde olduğunda kilitlenme oluşur ( ikiniz de yapbozun bir parçasına sahipsiniz).

Java'yı biliyorsanız, bu sorunu şu şekilde temsil edebilirsiniz:

import java.util.concurrent.locks.*;

public class Deadlock1 {

    public static class Table {

        private static Lock Flashlight = new ReentrantLock();
        private static Lock Batteries = new ReentrantLock();        

        public static void giveFlashLightAndBatteries() {
            try {
                Flashlight.lock();
                Batteries.lock();
                System.out.println("Lights on");
            } finally {
                Batteries.unlock();
                Flashlight.unlock();
            }
        }

        public static void giveBatteriesAndFlashLight() {
            try {
                Batteries.lock();
                Flashlight.lock();
                System.out.println("Lights on");
            } finally {
                Flashlight.unlock();
                Batteries.unlock();
            }
        }
    }

    public static void main(String[] args) {
        // This thread represents person one
        new Thread(new Runnable() {
            public void run() { Table.giveFlashLightAndBatteries(); }
        }).start();

        // This thread represents person two
        new Thread(new Runnable() {
            public void run() { Table.giveBatteriesAndFlashLight(); }
        }).start();
    }
}

Bu örneği çalıştırırsanız, bazen işlerin iyi ve doğru bir şekilde yürüdüğünü fark edeceksiniz. Ancak bazen programınız hiçbir şey yazdırmaz. Bunun nedeni, bir kişinin pilleri varken, diğerinin el fenerinin el fenerini açıp kilitlenmeye neden olmasını önleyen el feneri olmasıdır.

Bu örnek, java öğreticilerinde verilen örneğe benzer: http://docs.oracle.com/javase/tutorial/essential/concurrency/deadlock.html

Başka bir örnek de döngü örneğidir:

public class Deadlock2 {

    public static class Loop {
        private static boolean done = false;

        public static synchronized void startLoop() throws InterruptedException {
            while(!done) {
                Thread.sleep(1000);
                System.out.println("Not done");
            }
        }

        public static synchronized void stopLoop() {
            done = true;
        }

    }

    public static void main(String[] args) {
        // This thread starts the loop
        new Thread(new Runnable() {
            public void run() {
                try {
                    Loop.startLoop();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }).start();

        // This thread stops the loop
        new Thread(new Runnable() {
            public void run() {
                Loop.stopLoop();
            }
        }).start();
    }
}

Bu örnek ya tekrar tekrar 'Bitmedi' yazabilir ya da asla 'Bitmedi' yazdıramaz. Birincisi, birinci iş parçacığının sınıf kilidini alması ve ikinci iş parçacığı tarafından 'stopLoop'a erişilmesini engelleyerek onu asla bırakmaması nedeniyle olur. Ve en sonuncusu, ikinci iş parçacığının ilk iş parçacığından önce başlaması, ilk iş parçacığı çalıştırılmadan önce 'tamam' değişkeninin doğru olmasına neden olduğu için olur.


4
public class DeadLock {
    public static void main(String[] args) throws InterruptedException {
        Thread mainThread = Thread.currentThread();
        Thread thread1 = new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    mainThread.join();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });
        thread1.start();
        thread1.join();
    }
}

3

Yemek Filozofları problemini, kilitlenmeleri göstermede daha basit örneklerden biri olarak görüyorum, çünkü 4 kilitlenme gerekliliği çizimle (özellikle döngüsel bekleme) kolayca gösterilebilir.

Gerçek dünya örneklerini yeni başlayanlar için çok daha kafa karıştırıcı buluyorum, ancak şu anda aklıma gelen iyi bir gerçek dünya senaryosu düşünemiyorum (gerçek dünya eşzamanlılığı konusunda nispeten deneyimsizim).


3

Yakın zamanda çiftler arasındaki kavgaların bir çıkmazdan başka bir şey olmadığını fark ettim .. Genellikle süreçlerden birinin sorunu çözmek için çökmesi gerektiği, elbette daha az öncelikli olanı (Boy;)).

İşte benzetme ...

İşlem1: Kız (G) İşlem2: Erkek (B) Kaynak1
: Üzgünüm Kaynak2: Kendi hatasını kabul etmek

Gerekli Koşullar:
1. Karşılıklı Dışlama: Bir seferde G veya B'den yalnızca biri özür dileyebilir veya kendi Hatasını kabul edebilir.
2. Bekle ve Bekle: Bir anda, biri Üzgünüm ve diğeri Kendi hatasını Kabul ediyor, biri kendi hatasını kabul ederek özür dilemeyi bekliyor, diğeri de özür dileyerek kendi hatasını kabul etmekten kurtulmayı bekliyor.
3. Önleme yok: Tanrı bile B veya G'yi Üzgünüm veya Kendi hatasını Kabullenmeden salıvermeye zorlayamaz. Ve gönüllü olarak? Benimle dalga mı geçiyorsun??
4. Genelge Bekle: Yine, özür tutan kişi diğerinin kendi hatasını kabul etmesini bekler ve kendi hatasını kabul eden biri önce özür dilemek ister. Yani döngüsel.

Dolayısıyla, tüm bu koşullar aynı anda yürürlükte olduğunda kilitlenmeler meydana gelir ve bu her zaman bir çift kavgasında geçerlidir;)

Kaynak: http://www.quora.com/Saurabh-Pandey-3/Posts/Never-ending-couple-fights-a-deadlock


3

İki farklı kaynak ve iki iş parçacığının kaynağı serbest bırakmasını bekleyen bir basit kilitlenme örneği. Doğrudan samples.oreilly.com/jenut/Deadlock.java adresinden

 public class Deadlock {
  public static void main(String[] args) {
    // These are the two resource objects we'll try to get locks for
    final Object resource1 = "resource1";
    final Object resource2 = "resource2";
    // Here's the first thread.  It tries to lock resource1 then resource2
    Thread t1 = new Thread() {
      public void run() {
        // Lock resource 1
        synchronized(resource1) {
          System.out.println("Thread 1: locked resource 1");

          // Pause for a bit, simulating some file I/O or something.  
          // Basically, we just want to give the other thread a chance to
          // run.  Threads and deadlock are asynchronous things, but we're
          // trying to force deadlock to happen here...
          try { Thread.sleep(50); } catch (InterruptedException e) {}

          // Now wait 'till we can get a lock on resource 2
          synchronized(resource2) {
            System.out.println("Thread 1: locked resource 2");
          }
        }
      }
    };

    // Here's the second thread.  It tries to lock resource2 then resource1
    Thread t2 = new Thread() {
      public void run() {
        // This thread locks resource 2 right away
        synchronized(resource2) {
          System.out.println("Thread 2: locked resource 2");

          // Then it pauses, for the same reason as the first thread does
          try { Thread.sleep(50); } catch (InterruptedException e) {}

          // Then it tries to lock resource1.  But wait!  Thread 1 locked
          // resource1, and won't release it 'till it gets a lock on
          // resource2.  This thread holds the lock on resource2, and won't
          // release it 'till it gets resource1.  We're at an impasse. Neither
          // thread can run, and the program freezes up.
          synchronized(resource1) {
            System.out.println("Thread 2: locked resource 1");
          }
        }
      }
    };

    // Start the two threads. If all goes as planned, deadlock will occur, 
    // and the program will never exit.
    t1.start(); 
    t2.start();
  }
}

If all goes as planned, deadlock will occur, and the program will never exit.Bu örneği guaranteeçıkmaz yapabilir miyiz ?
Erran Morad

Bu Kyle'ın gönderdiği kodla aynı , neden başka bir yanıttan üç yıl sonra yinelenen bir yanıt ekleyesiniz? (ve üç yıl sonra neden yorum yapmalıyım?)
Simon Forsberg

2

Bir zaman Kilitlenme bir durumda ortaya çıkabilir Girl1ile flört etmek isteyen Guy2bir diğeri tarafından yakalanır, Girl2ve Girl2bir ile flört etmek isteyen Guy1tarafından yakalanan olduğunu Girl1. Her iki kız da birbirini terk etmeyi beklediğinden, duruma kilitlenme denir.

class OuchTheGirls
{
    public static void main(String[] args)
    {
        final String resource1 = "Guy1";
        final String resource2 = "Guy2";

        // Girl1 tries to lock resource1 then resource2
        Thread Girl1 = new Thread(() ->
                                  {
                                      synchronized (resource1)
                                      {
                                          System.out.println("Thread 1: locked Guy1");

                                          try { Thread.sleep(100);} catch (Exception e) {}

                                          synchronized (resource2)
                                          {
                                              System.out.println("Thread 1: locked Guy2");
                                          }
                                      }
                                  });

        // Girl2 tries to lock Guy2 then Guy1
        Thread Girl2 = new Thread(() ->
                                  {
                                      synchronized (resource2)
                                      {
                                          System.out.println("Thread 2: locked Guy2");

                                          try { Thread.sleep(100);} catch (Exception e) {}

                                          synchronized (resource1)
                                          {
                                              System.out.println("Thread 2: locked Guy1");
                                          }
                                      }
                                  });


        Girl1.start();
        Girl2.start();
    }
}

java'da dahili dizgi ve sarmalayıcı nesnelerin işlenme şekli nedeniyle String veya wrapper sınıflarını kilitlemek için iyi bir uygulama değildir. Birden fazla referans dahili olarak aynı nesneyi işaret edecektir. Herhangi bir özel nesneniz yoksa, Object sınıfını kullanmak daha iyidir.
Lokesh

1

Üreticileri-tüketiciler sorun o alabileceğin en sıra yemek filozofların sorunla birlikte muhtemelen basit gibidir. Bunu açıklayan bazı sözde kodlara da sahiptir. Bunlar yeni başlayanlar için çok karmaşıksa, onları kavramak için daha çok çabalamaları gerekir.


1

Konsepti öğrencilerinize tanıtırken kilitlenmenin oluşabileceği olası senaryoları basit listeye alın. Bu, en az iki iş parçacığı ve en az iki kaynak içerir (sanırım). Amaç, birinci iş parçacığının birinci kaynakta bir kilide sahip olduğu ve kaynak iki üzerindeki kilidin serbest bırakılmasını beklediği, aynı zamanda iş parçacığı iki kaynak iki üzerinde bir kilit tuttuğu ve beklediği bir senaryo tasarlamaktır. kaynak bir üzerindeki kilit serbest bırakılacak.

Altta yatan kaynakların ne olduğu gerçekten önemli değil; basitlik uğruna, onları her iki iş parçacığının da yazabileceği bir çift dosya haline getirebilirsiniz.

DÜZENLEME: Bu, tutulan kilitler dışında süreçler arası iletişim olmadığını varsayar.


1

Yemek filozoflarının problemini okurken anlaşılması biraz zor buldum, IMHO çıkmazının aslında kaynak tahsisi ile ilgili olduğunu. 2 Hemşirenin bir görevi tamamlamak için 3 ekipman için savaşması gereken daha basit bir örneği paylaşmak isterim. Java ile yazılmış olmasına rağmen. Kilitlenmenin nasıl gerçekleştiğini simüle etmek için basit bir kilit () yöntemi oluşturulur, böylece diğer programlama dillerinde de uygulanabilir. http://www.justexample.com/wp/example-of-deadlock/


1

Https://docs.oracle.com/javase/tutorial/essential/concurrency/deadlock.html adresinden basit bir örnek

public class Deadlock {

public static void printMessage(String message) {

    System.out.println(String.format("%s %s ", Thread.currentThread().getName(), message));

}

private static class Friend {

    private String name;

    public Friend(String name) {
        this.name = name;
    }

    public void bow(Friend friend) {

        printMessage("Acquiring lock on " + this.name);

        synchronized(this) {
            printMessage("Acquired lock on " + this.name);
            printMessage(name + " bows " + friend.name);
            friend.bowBack(this);
        }

    }

    public void bowBack(Friend friend) {

        printMessage("Acquiring lock on " + this.name);

        synchronized (this) {
            printMessage("Acquired lock on " + this.name);
            printMessage(friend.name + " bows back");
        }

    }

}

public static void main(String[] args) throws InterruptedException {

    Friend one = new Friend("one");
    Friend two = new Friend("two");

    new Thread(new Runnable() {
        @Override
        public void run() {
            one.bow(two);
        }
    }).start();

    new Thread(new Runnable() {
        @Override
        public void run() {
            two.bow(one);
        }
    }).start();
}

}

Çıktı:

Thread-0 Acquiring lock on one 
Thread-1 Acquiring lock on two 
Thread-0 Acquired lock on one 
Thread-1 Acquired lock on two 
Thread-1 two bows one 
Thread-0 one bows two 
Thread-1 Acquiring lock on one 
Thread-0 Acquiring lock on two 

Konu Dökümü:

2016-03-14 12:20:09
Full thread dump Java HotSpot(TM) 64-Bit Server VM (25.74-b02 mixed mode):

"DestroyJavaVM" #13 prio=5 os_prio=0 tid=0x00007f472400a000 nid=0x3783 waiting on condition [0x0000000000000000]
   java.lang.Thread.State: RUNNABLE

"Thread-1" #12 prio=5 os_prio=0 tid=0x00007f472420d800 nid=0x37a3 waiting for monitor entry [0x00007f46e89a5000]
   java.lang.Thread.State: BLOCKED (on object monitor)
    at com.anantha.algorithms.ThreadJoin$Friend.bowBack(ThreadJoin.java:102)
    - waiting to lock <0x000000076d0583a0> (a com.anantha.algorithms.ThreadJoin$Friend)
    at com.anantha.algorithms.ThreadJoin$Friend.bow(ThreadJoin.java:92)
    - locked <0x000000076d0583e0> (a com.anantha.algorithms.ThreadJoin$Friend)
    at com.anantha.algorithms.ThreadJoin$2.run(ThreadJoin.java:141)
    at java.lang.Thread.run(Thread.java:745)

"Thread-0" #11 prio=5 os_prio=0 tid=0x00007f472420b800 nid=0x37a2 waiting for monitor entry [0x00007f46e8aa6000]
   java.lang.Thread.State: BLOCKED (on object monitor)
    at com.anantha.algorithms.ThreadJoin$Friend.bowBack(ThreadJoin.java:102)
    - waiting to lock <0x000000076d0583e0> (a com.anantha.algorithms.ThreadJoin$Friend)
    at com.anantha.algorithms.ThreadJoin$Friend.bow(ThreadJoin.java:92)
    - locked <0x000000076d0583a0> (a com.anantha.algorithms.ThreadJoin$Friend)
    at com.anantha.algorithms.ThreadJoin$1.run(ThreadJoin.java:134)
    at java.lang.Thread.run(Thread.java:745)

"Monitor Ctrl-Break" #10 daemon prio=5 os_prio=0 tid=0x00007f4724211000 nid=0x37a1 runnable [0x00007f46e8def000]
   java.lang.Thread.State: RUNNABLE
    at java.net.SocketInputStream.socketRead0(Native Method)
    at java.net.SocketInputStream.socketRead(SocketInputStream.java:116)
    at java.net.SocketInputStream.read(SocketInputStream.java:170)
    at java.net.SocketInputStream.read(SocketInputStream.java:141)
    at sun.nio.cs.StreamDecoder.readBytes(StreamDecoder.java:284)
    at sun.nio.cs.StreamDecoder.implRead(StreamDecoder.java:326)
    at sun.nio.cs.StreamDecoder.read(StreamDecoder.java:178)
    - locked <0x000000076d20afb8> (a java.io.InputStreamReader)
    at java.io.InputStreamReader.read(InputStreamReader.java:184)
    at java.io.BufferedReader.fill(BufferedReader.java:161)
    at java.io.BufferedReader.readLine(BufferedReader.java:324)
    - locked <0x000000076d20afb8> (a java.io.InputStreamReader)
    at java.io.BufferedReader.readLine(BufferedReader.java:389)
    at com.intellij.rt.execution.application.AppMain$1.run(AppMain.java:93)
    at java.lang.Thread.run(Thread.java:745)

"Service Thread" #9 daemon prio=9 os_prio=0 tid=0x00007f47240c9800 nid=0x3794 runnable [0x0000000000000000]
   java.lang.Thread.State: RUNNABLE

"C1 CompilerThread3" #8 daemon prio=9 os_prio=0 tid=0x00007f47240c6800 nid=0x3793 waiting on condition [0x0000000000000000]
   java.lang.Thread.State: RUNNABLE

"C2 CompilerThread2" #7 daemon prio=9 os_prio=0 tid=0x00007f47240c4000 nid=0x3792 waiting on condition [0x0000000000000000]
   java.lang.Thread.State: RUNNABLE

"C2 CompilerThread1" #6 daemon prio=9 os_prio=0 tid=0x00007f47240c2800 nid=0x3791 waiting on condition [0x0000000000000000]
   java.lang.Thread.State: RUNNABLE

"C2 CompilerThread0" #5 daemon prio=9 os_prio=0 tid=0x00007f47240bf800 nid=0x3790 waiting on condition [0x0000000000000000]
   java.lang.Thread.State: RUNNABLE

"Signal Dispatcher" #4 daemon prio=9 os_prio=0 tid=0x00007f47240be000 nid=0x378f waiting on condition [0x0000000000000000]
   java.lang.Thread.State: RUNNABLE

"Finalizer" #3 daemon prio=8 os_prio=0 tid=0x00007f472408c000 nid=0x378e in Object.wait() [0x00007f46e98c5000]
   java.lang.Thread.State: WAITING (on object monitor)
    at java.lang.Object.wait(Native Method)
    - waiting on <0x000000076cf88ee0> (a java.lang.ref.ReferenceQueue$Lock)
    at java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:143)
    - locked <0x000000076cf88ee0> (a java.lang.ref.ReferenceQueue$Lock)
    at java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:164)
    at java.lang.ref.Finalizer$FinalizerThread.run(Finalizer.java:209)

"Reference Handler" #2 daemon prio=10 os_prio=0 tid=0x00007f4724087800 nid=0x378d in Object.wait() [0x00007f46e99c6000]
   java.lang.Thread.State: WAITING (on object monitor)
    at java.lang.Object.wait(Native Method)
    - waiting on <0x000000076cf86b50> (a java.lang.ref.Reference$Lock)
    at java.lang.Object.wait(Object.java:502)
    at java.lang.ref.Reference.tryHandlePending(Reference.java:191)
    - locked <0x000000076cf86b50> (a java.lang.ref.Reference$Lock)
    at java.lang.ref.Reference$ReferenceHandler.run(Reference.java:153)

"VM Thread" os_prio=0 tid=0x00007f4724080000 nid=0x378c runnable 

"GC task thread#0 (ParallelGC)" os_prio=0 tid=0x00007f472401f000 nid=0x3784 runnable 

"GC task thread#1 (ParallelGC)" os_prio=0 tid=0x00007f4724021000 nid=0x3785 runnable 

"GC task thread#2 (ParallelGC)" os_prio=0 tid=0x00007f4724022800 nid=0x3786 runnable 

"GC task thread#3 (ParallelGC)" os_prio=0 tid=0x00007f4724024800 nid=0x3787 runnable 

"GC task thread#4 (ParallelGC)" os_prio=0 tid=0x00007f4724026000 nid=0x3788 runnable 

"GC task thread#5 (ParallelGC)" os_prio=0 tid=0x00007f4724028000 nid=0x3789 runnable 

"GC task thread#6 (ParallelGC)" os_prio=0 tid=0x00007f4724029800 nid=0x378a runnable 

"GC task thread#7 (ParallelGC)" os_prio=0 tid=0x00007f472402b800 nid=0x378b runnable 

"VM Periodic Task Thread" os_prio=0 tid=0x00007f47240cc800 nid=0x3795 waiting on condition 

JNI global references: 16


Found one Java-level deadlock:
=============================
"Thread-1":
  waiting to lock monitor 0x00007f46dc003f08 (object 0x000000076d0583a0, a com.anantha.algorithms.ThreadJoin$Friend),
  which is held by "Thread-0"
"Thread-0":
  waiting to lock monitor 0x00007f46dc006008 (object 0x000000076d0583e0, a com.anantha.algorithms.ThreadJoin$Friend),
  which is held by "Thread-1"

Java stack information for the threads listed above:
===================================================
"Thread-1":
    at com.anantha.algorithms.ThreadJoin$Friend.bowBack(ThreadJoin.java:102)
    - waiting to lock <0x000000076d0583a0> (a com.anantha.algorithms.ThreadJoin$Friend)
    at com.anantha.algorithms.ThreadJoin$Friend.bow(ThreadJoin.java:92)
    - locked <0x000000076d0583e0> (a com.anantha.algorithms.ThreadJoin$Friend)
    at com.anantha.algorithms.ThreadJoin$2.run(ThreadJoin.java:141)
    at java.lang.Thread.run(Thread.java:745)
"Thread-0":
    at com.anantha.algorithms.ThreadJoin$Friend.bowBack(ThreadJoin.java:102)
    - waiting to lock <0x000000076d0583e0> (a com.anantha.algorithms.ThreadJoin$Friend)
    at com.anantha.algorithms.ThreadJoin$Friend.bow(ThreadJoin.java:92)
    - locked <0x000000076d0583a0> (a com.anantha.algorithms.ThreadJoin$Friend)
    at com.anantha.algorithms.ThreadJoin$1.run(ThreadJoin.java:134)
    at java.lang.Thread.run(Thread.java:745)

Found 1 deadlock.

Heap
 PSYoungGen      total 74752K, used 9032K [0x000000076cf80000, 0x0000000772280000, 0x00000007c0000000)
  eden space 64512K, 14% used [0x000000076cf80000,0x000000076d8520e8,0x0000000770e80000)
  from space 10240K, 0% used [0x0000000771880000,0x0000000771880000,0x0000000772280000)
  to   space 10240K, 0% used [0x0000000770e80000,0x0000000770e80000,0x0000000771880000)
 ParOldGen       total 171008K, used 0K [0x00000006c6e00000, 0x00000006d1500000, 0x000000076cf80000)
  object space 171008K, 0% used [0x00000006c6e00000,0x00000006c6e00000,0x00000006d1500000)
 Metaspace       used 3183K, capacity 4500K, committed 4864K, reserved 1056768K
  class space    used 352K, capacity 388K, committed 512K, reserved 1048576K

1

İşte Java'daki basit bir kilitlenme. Kilitlenmeyi göstermek için iki kaynağa ihtiyacımız var. Aşağıdaki örnekte, bir kaynak sınıf kilididir (senkronizasyon yöntemiyle) ve diğeri bir 'i' tamsayısıdır.

public class DeadLock {

    static int i;
    static int k;

    public static synchronized void m1(){
        System.out.println(Thread.currentThread().getName()+" executing m1. Value of i="+i);

        if(k>0){i++;}

        while(i==0){
            System.out.println(Thread.currentThread().getName()+" waiting in m1 for i to be > 0. Value of i="+i);
            try { Thread.sleep(10000);} catch (InterruptedException e) { e.printStackTrace(); }
        }
    }

    public static void main(String[] args) {

        Thread t1 = new Thread("t1") {
            public void run() {
                m1();
            }
        };

        Thread t2 = new Thread("t2") {
            public void run() {
                try { Thread.sleep(100);} catch (InterruptedException e) { e.printStackTrace(); }
                k++;
                m1();
            }
        };

        t1.start();
        t2.start();
    }
}

1
public class DeadLock {

    public static void main(String[] args) {
        Object resource1 = new Object();
        Object resource2 = new Object();
        SharedObject s = new SharedObject(resource1, resource2);
        TestThread11 t1 = new TestThread11(s);
        TestThread22 t2 = new TestThread22(s);
        t1.start();
        t2.start();
    }

}

class SharedObject {
    Object o1, o2;
    SharedObject(Object o1, Object o2) {
        this.o1 = o1;
        this.o2 = o2;
    }
    void m1() {
        synchronized(o1) {
            System.out.println("locked on o1 from m1()");
            synchronized(o2) { 
                System.out.println("locked on o2 from m1()");
            }
        }
    }
    void m2() {
        synchronized(o2) {
            System.out.println("locked on o2 from m2()");
            synchronized(o1) { 
                System.out.println("locked on o1 from m2()");
            }
        }
    }
}

class TestThread11 extends Thread {
    SharedObject s;
    TestThread11(SharedObject s) {
        this.s = s;
    }
    public void run() {
        s.m1();
    }
}

class TestThread22 extends Thread {
    SharedObject s;
    TestThread22(SharedObject s) {
        this.s = s;
    }
    public void run() {
        s.m2();
    }
}

1
Cevabınızı açıklamak için biraz metin ekleyebilir misiniz lütfen?
Kmeixner

1

İşte C #'daki basit bir kilitlenme.

void UpdateLabel(string text) {
   lock(this) {
      if(MyLabel.InvokeNeeded) {
        IAsyncResult res =  MyLable.BeginInvoke(delegate() {
             MyLable.Text = text;
            });
         MyLabel.EndInvoke(res);
        } else {
             MyLable.Text = text;
        }
    }
}

Bir gün bunu GUI iş parçacığından çağırırsanız ve başka bir iş parçacığı da onu çağırırsa, kilitlenebilirsiniz. Diğer evre EndInvoke'a ulaşır, GUI iş parçacığının kilidi tutarken temsilciyi yürütmesini bekler. Aynı kilit üzerindeki GUI iş parçacığı blokları, diğer iş parçacığının onu serbest bırakmasını bekler - ki bu, GUI iş parçacığının diğer iş parçacığının beklediği temsilciyi yürütmek için hiçbir zaman mevcut olmayacağı için olmayacaktır. (tabii ki buradaki kilit kesinlikle gerekli değildir - belki EndInvoke de değildir, ancak biraz daha karmaşık bir senaryoda, arayan tarafından başka nedenlerle bir kilit elde edilebilir ve bu da aynı kilitlenmeyle sonuçlanabilir.)


0
package test.concurrent;
public class DeadLockTest {
   private static long sleepMillis;
   private final Object lock1 = new Object();
   private final Object lock2 = new Object();

   public static void main(String[] args) {
       sleepMillis = Long.parseLong(args[0]);
       DeadLockTest test = new DeadLockTest();
       test.doTest();
   }

   private void doTest() {
       Thread t1 = new Thread(new Runnable() {
           public void run() {
               lock12();
           }
       });
       Thread t2 = new Thread(new Runnable() {
           public void run() {
               lock21();
           }
       });
       t1.start();
       t2.start();
   }

   private void lock12() {
       synchronized (lock1) {
           sleep();
           synchronized (lock2) {
               sleep();
           }
       }
   }

   private void lock21() {
       synchronized (lock2) {
           sleep();
           synchronized (lock1) {
               sleep();
           }
       }
   }

   private void sleep() {
       try {
           Thread.sleep(sleepMillis);
       } catch (InterruptedException e) {
           e.printStackTrace();
       }
   }
}
To run the deadlock test with sleep time 1 millisecond:
java -cp . test.concurrent.DeadLockTest 1

0
public class DeadlockProg {

    /**
     * @Gowtham Chitimi Reddy IIT(BHU);
     */
    public static void main(String[] args) {
        // TODO Auto-generated method stub
        final Object ob1 = new Object();
        final Object ob2 = new Object();
        Thread t1 = new Thread(){
            public void run(){
                synchronized(ob1){
                    try{
                        Thread.sleep(100);
                    }
                    catch(InterruptedException e){
                        System.out.println("Error catched");
                    }
                    synchronized(ob2){

                    }
                }

            }
        };
        Thread t2 = new Thread(){
            public void run(){
                synchronized(ob2){
                    try{
                        Thread.sleep(100);
                    }
                    catch(InterruptedException e){
                        System.out.println("Error catched");
                    }
                    synchronized(ob1){                      
                    }
                }               
            }
        };
        t1.start();
        t2.start();
    }

}

0
package ForkBlur;

public class DeadLockTest {
  public static void main(String args[]) {

    final DeadLockTest t1 = new DeadLockTest();
    final DeadLockTest t2 = new DeadLockTest();

    Runnable r1 = new Runnable() {

        @Override
        public void run() {
            try {

                synchronized (t1) {
                    System.out
                            .println("r1 has locked t1, now going to sleep");
                    Thread.sleep(100);
                    System.out
                            .println("r1 has awake , now going to aquire lock for t2");
                    synchronized (t2) {
                        Thread.sleep(100);
                    }
                }
            } catch (Exception e) {
                e.printStackTrace();
            }

        }
    };

    Runnable r2 = new Runnable() {

        @Override
        public void run() {
            try {

                synchronized (t2) {
                    System.out
                            .println("r2 has aquire the lock of t2 now going to sleep");
                    Thread.sleep(100);
                    System.out
                            .println("r2 is awake , now going to aquire the lock from t1");
                    synchronized (t1) {
                        Thread.sleep(100);
                    }
                }
            } catch (Exception e) {
                e.printStackTrace();
            }

        }
    };

    new Thread(r1).start();
    new Thread(r2).start();
  }
}

0

Ultra Simple Working DeadLock Örneği oluşturdum: -

package com.thread.deadlock;

public class ThreadDeadLockClient {

    public static void main(String[] args) {
        ThreadDeadLockObject1 threadDeadLockA = new ThreadDeadLockObject1("threadDeadLockA");
        ThreadDeadLockObject2 threadDeadLockB = new ThreadDeadLockObject2("threadDeadLockB");

        new Thread(new Runnable() {

            @Override
            public void run() {
                threadDeadLockA.methodA(threadDeadLockB);

            }
        }).start();

        new Thread(new Runnable() {

            @Override
            public void run() {
                threadDeadLockB.methodB(threadDeadLockA);

            }
        }).start();
    }
}

package com.thread.deadlock;

public class ThreadDeadLockObject1 {

    private String name;

    ThreadDeadLockObject1(String name){
        this.name = name;
    }

    public  synchronized void methodA(ThreadDeadLockObject2 threadDeadLockObject2) {
        System.out.println("In MethodA "+" Current Object--> "+this.getName()+" Object passed as parameter--> "+threadDeadLockObject2.getName());
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        threadDeadLockObject2.methodB(this);
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }   
}

package com.thread.deadlock;

public class ThreadDeadLockObject2 {

    private String name;

    ThreadDeadLockObject2(String name){
        this.name = name;
    }

    public  synchronized void methodB(ThreadDeadLockObject1 threadDeadLockObject1) {
        System.out.println("In MethodB "+" Current Object--> "+this.getName()+" Object passed as parameter--> "+threadDeadLockObject1.getName());
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        threadDeadLockObject1.methodA(this);
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}

Yukarıdaki örnekte 2 iş parçacığı, iki farklı nesnenin senkronize edilmiş yöntemlerini yürütmektedir. Eşitlenmiş yöntemA, threadDeadLockA nesnesi tarafından çağrılır ve senkronize edilmiş yöntem B, threadDeadLockB nesnesi tarafından çağrılır. MethodA'da bir threadDeadLockB başvurusu iletilir ve methodB'de bir threadDeadLockA başvurusu geçirilir. Şimdi her iş parçacığı başka bir nesneye kilitlenmeye çalışır. MethodA'da, threadDeadLockA'da bir kilit tutan iş parçacığı, threadDeadLockB nesnesinde kilitlenmeye çalışıyor ve benzer şekilde, methodB'de, threadDeadLockB üzerinde bir kilit tutan iş parçacığı, threadDeadLockA'ya kilitlenmeye çalışıyor. Böylece her iki iş parçacığı da sonsuza kadar bir kilitlenme yaratmayı bekleyecektir.


0

2'den fazla olan bir örnek kullanarak daha net açıklamama izin verin iş parçacığı .

Sırasıyla L1, L2, ..., Ln kilitlerini tutan n tane iş parçacığına sahip olduğunuzu varsayalım. Şimdi diyelim ki, 1. evreden başlayarak, her iş parçacığı komşu iş parçacığının kilidini almaya çalışıyor. Dolayısıyla, iş parçacığı 1, L2'yi almaya çalıştığı için engellenir (L2, iş parçacığı 2'ye ait olduğundan), iş parçacığı 2, L3 için engellenir vb. İplik n, L1 için bloke edilir. Hiçbir iş parçacığı çalıştırılamadığı için bu artık bir kilitlenmedir.

class ImportantWork{
   synchronized void callAnother(){     
   }
   synchronized void call(ImportantWork work) throws InterruptedException{
     Thread.sleep(100);
     work.callAnother();
   }
}
class Task implements Runnable{
  ImportantWork myWork, otherWork;
  public void run(){
    try {
      myWork.call(otherWork);
    } catch (InterruptedException e) {      
    }
  }
}
class DeadlockTest{
  public static void main(String args[]){
    ImportantWork work1=new ImportantWork();
    ImportantWork work2=new ImportantWork();
    ImportantWork work3=new ImportantWork();
    Task task1=new Task(); 
    task1.myWork=work1;
    task1.otherWork=work2;

    Task task2=new Task(); 
    task2.myWork=work2;
    task2.otherWork=work3;

    Task task3=new Task(); 
    task3.myWork=work3;
    task3.otherWork=work1;

    new Thread(task1).start();
    new Thread(task2).start();
    new Thread(task3).start();
  }
}

Yukarıdaki örnekte, Runnables görev1, görev2 ve görev3'ü tutan üç iş parçacığı olduğunu görebilirsiniz . İfadeden önce, sleep(100)iş parçacıkları call()yönteme girdiklerinde (varlığından dolayı synchronized) üç çalışma nesnesinin kilitlerini alırlar . Ancak callAnother(), komşu iş parçacığının nesnesini denedikleri anda , kilitlenirler ve bir kilitlenmeye yol açar, çünkü bu nesnelerin kilitleri çoktan alınmıştır.


0
CountDownLatch countDownLatch = new CountDownLatch(1);
ExecutorService executorService = ExecutorService executorService = Executors.newSingleThreadExecutor();
executorService.execute(() -> {
    Future<?> future = executorService.submit(() -> {
        System.out.println("generated task");
    });
    countDownLatch.countDown();
    try {
        future.get();
    } catch (InterruptedException e) {
        e.printStackTrace();
    } catch (ExecutionException e) {
         e.printStackTrace();
    }
});


countDownLatch.await();
executorService.shutdown();

0

Tek bir iş parçacığı ile kilitlenmenin sinsi bir yolu, aynı (özyinelemeli olmayan) muteksi iki kez kilitlemeye çalışmaktır. Bu aradığınız basit örnek olmayabilir, ancak bu tür vakalarla zaten karşılaştığım kesin.

#include <mutex>
#include <iostream>

int main()
{
  std::mutex m;
  m.lock();
  m.lock();
  std::cout << "Expect never to get here because of a deadlock!";
}

0

İşte çok zaman geçirdikten sonra çıkmaza ilişkin ayrıntılı örneğim . Umarım yardımcı olur :)

package deadlock;

public class DeadlockApp {

    String s1 = "hello";
    String s2 = "world";

    Thread th1 = new Thread() {
        public void run() {
            System.out.println("Thread th1 has started");
            synchronized (s1) { //A lock is created internally (holds access of s1), lock will be released or unlocked for s1, only when it exits the block Line #23
                System.out.println("Executing first synchronized block of th1!");
                try {
                    Thread.sleep(1000);
                } catch(InterruptedException ex) {
                    System.out.println("Exception is caught in th1");
                }
                System.out.println("Waiting for the lock to be released from parrallel thread th1");
                synchronized (s2) { //As another has runned parallely Line #32, lock has been created for s2
                    System.out.println(s1 + s2);
                }

            }
            System.out.println("Thread th1 has executed");
        }
    };


    Thread th2 = new Thread() {
        public void run() {
            System.out.println("Thread th2 has started");
            synchronized (s2) { //A lock is created internally (holds access of s2), lock will be released or unlocked for s2, only when it exits the block Line #44
                System.out.println("Executing first synchronized block of th2!");
                try {
                    Thread.sleep(1000);
                } catch(InterruptedException ex) {
                    System.out.println("Exception is caught in th2");
                }
                System.out.println("Waiting for the lock to be released from parrallel thread th2");
                synchronized (s1) { //As another has runned parallely Line #11, lock has been created for s1
                    System.out.println(s1 + s2);
                }

            }
            System.out.println("Thread th2 has executed");
        }
    };

    public static void main(String[] args) {
        DeadlockApp deadLock = new DeadlockApp();
        deadLock.th1.start();
        deadLock.th2.start();
        //Line #51 and #52 runs parallely on executing the program, a lock is created inside synchronized method
        //A lock is nothing but, something like a blocker or wall, which holds access of the variable from being used by others.
        //Locked object is accessible, only when it is unlocked (i.e exiting  the synchronized block)
        //Lock cannot be created for primitive types (ex: int, float, double)
        //Dont forget to add thread.sleep(time) because if not added, then object access will not be at same time for both threads to create Deadlock (not actual runtime with lots of threads) 
        //This is a simple program, so we added sleep90 to create Deadlock, it will execute successfully, if it is removed. 
    }

    //Happy coding -- Parthasarathy S
}
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.