যদি আমি একই ক্লাসে দুটি পদ্ধতি সিঙ্ক্রোনাইজ করি তবে সেগুলি কি একই সাথে চলতে পারে?


164

যদি আমি একই ক্লাসে দুটি পদ্ধতি সিঙ্ক্রোনাইজ করি তবে তারা কি একই বস্তুতে একই সাথে চলতে পারে ? উদাহরণ স্বরূপ:

class A {
    public synchronized void methodA() {
        //method A
    }

    public synchronized void methodB() {
        // method B
    }
}

আমি জানি যে আমি methodA()দুটি ভিন্ন থ্রেডে একই বস্তুতে দু'বার চালাতে পারি না । একই জিনিস methodB()

তবে কি এখনও চলতে থাকা methodB()অবস্থায় আমি বিভিন্ন থ্রেডে methodA()চালাতে পারি? (একই জিনিস)

উত্তর:


148

উভয় পদ্ধতি একই মনিটর লক। অতএব, আপনি একই থ্রেডগুলি একই থ্রেড থেকে বিভিন্ন থ্রেড থেকে চালাতে পারবেন না (অন্যটি শেষ না হওয়া পর্যন্ত দুটি পদ্ধতির একটি অবরুদ্ধ হবে)।


1
আমি এই প্রশ্নে একটি অ্যাড ছিল। ধরুন, উভয় পদ্ধতিই এখন স্থিতিশীল, মেথডএকে ক্লাস ব্যবহার করে বলা হয়, যখন পদ্ধতি বি কে টি 1 তে A.methodA () এবং অবজেক্ট.মোথডাব () এর মতো অবজেক্ট ব্যবহার করে বলা হয়। এখন কি হবে, তারা কি ব্লক করবে ????
amod

2
@ amod0017: obj.methodB()থেকে সমার্থক A.methodB()যখন methodB()হয় static। সুতরাং হ্যাঁ, তারা (ক্লাসের উপর, অবজেক্টের নয়, মনিটরের) ব্লক করবে।
এনপিই

চেষ্টা করে এটিতে ফিরে আসবে। :)
amod

@ এনপিই তাই উভয় পদ্ধতি স্থিতিশীল এবং 2 টি থ্রেড টি 1 এবং টি 2 একই বস্তুতে পদ্ধতিএ () এবং মেথডবি () কল করার চেষ্টা করে তবে কেবল 1 টি (টি 1 বলে) থ্রেড কার্যকর হবে এবং অন্য থ্রেড টি 1 রিলিজ হওয়া পর্যন্ত অপেক্ষা করতে হবে ?
শ্রীপ্রসাদ

8
মনে রাখবেন যে স্থির পদ্ধতিগুলি .classঅবজেক্টে লক ব্যবহার করে । সুতরাং আপনি যদি class A {static synchronized void m() {} }। এবং তারপরে একটি থ্রেড new A().m()এটিকে new A()বস্তুতে লক অর্জন করে calls তারপরে যদি অন্য কোনও থ্রেড A.m()এটিকে মেথডের কোনও সমস্যায় প্রবেশ করে না কারণ এটি যা দেখায় তা A.classঅবজেক্টে লক করা হয় এবং কোনও থ্রেডই এই ধরণের লক রাখেনি । সুতরাং আপনি পদ্ধতিটি ঘোষিত হলেও synchronizedএটি একই সময়ে একই সাথে দুটি পৃথক থ্রেড দ্বারা অ্যাক্সেসযোগ্য । সুতরাং: স্থির পদ্ধতিতে কল করতে কখনই অবজেক্টের রেফারেন্স ব্যবহার করবেন না
অ্যালেক্স সেমেনিয়ুক

113

উদাহরণে মেথডএ এবং মেথডবি হ'ল উদাহরণ পদ্ধতি (স্থির পদ্ধতির বিপরীতে)। ফেলে synchronizedএকটি দৃষ্টান্ত পদ্ধতি মানে উপর থ্রেড বস্তুর উদাহরণস্বরূপ লক ( "স্বকীয় লক") যে পদ্ধতি সামনে থ্রেড যে পদ্ধতি যে কোন কোড নির্বাহ শুরু করতে পারেন উপর বলা হয় অর্জন রয়েছে।

যদি আপনার কাছে দুটি পৃথক উদাহরণ পদ্ধতি রয়েছে যা সিঙ্ক্রোনাইজড হিসাবে চিহ্নিত হয়েছে এবং বিভিন্ন থ্রেড একই পদ্ধতিতে একই পদ্ধতিগুলিকে একই সাথে কল করে, সেই থ্রেডগুলি একই লকের পক্ষে লড়াই করবে। একবার একটি থ্রেড লক হয়ে গেলে অন্যান্য সমস্ত থ্রেড সেই বস্তুর সমস্ত সিঙ্ক্রোনাইজড ইনস্ট্যান্স পদ্ধতি থেকে বন্ধ হয়ে যায়।

দুটি পদ্ধতি একই সাথে চালানোর জন্য তাদের বিভিন্ন লক ব্যবহার করতে হবে, এর মতো:

class A {
    private final Object lockA = new Object();
    private final Object lockB = new Object();

    public void methodA() {
        synchronized(lockA) {
            //method A
        }
    }

    public void methodB() {
        synchronized(lockB) {
            //method B
        }
    }
}

যেখানে সিঙ্ক্রোনাইজড ব্লক সিনট্যাক্স একটি নির্দিষ্ট অবজেক্ট নির্দিষ্ট করার অনুমতি দেয় যা ব্লকটিতে প্রবেশের জন্য এক্সিকিউটেড থ্রেডকে অভ্যন্তরীণ লকটি অর্জন করতে হবে।

বুঝতে গুরুত্বপূর্ণ বিষয়টি হ'ল যদিও আমরা পৃথক পদ্ধতিতে একটি "সিঙ্ক্রোনাইজড" কীওয়ার্ড রাখছি, মূল ধারণাটি পর্দার আড়ালে অন্তর্গত লক।

এখানে কিভাবে হয় জাভা টিউটোরিয়াল সম্পর্ক বর্ণনা

সিঙ্ক্রোনাইজেশন একটি অভ্যন্তরীণ সত্তা চারপাশে নির্মিত হয় যা অন্তর্নিহিত লক বা মনিটর লক হিসাবে পরিচিত। (এপিআই স্পেসিফিকেশন প্রায়শই এই সত্তাকে কেবল "মনিটর" হিসাবে উল্লেখ করে) অন্তর্নিহিত লকগুলি সিঙ্ক্রোনাইজেশনের উভয় দিকগুলিতে ভূমিকা রাখে: কোনও বস্তুর রাজ্যে একচেটিয়া অ্যাক্সেস প্রয়োগ করা এবং দৃশ্যমানতার জন্য প্রয়োজনীয় অপরিহার্য সম্পর্কগুলির আগেই সম্পর্ক স্থাপন করা।

প্রতিটি বস্তুর সাথে এর সাথে যুক্ত একটি অন্তর্গত লক রয়েছে has কনভেনশন অনুসারে, কোনও থ্রেডের জন্য যা কোনও বস্তুর ক্ষেত্রগুলিতে একচেটিয়া এবং ধারাবাহিক অ্যাক্সেসের প্রয়োজন সেগুলিতে অ্যাক্সেস করার আগে অবজেক্টের অভ্যন্তরীণ লকটি অর্জন করতে হবে এবং তার সাথে সম্পন্ন হওয়ার পরে তার অভ্যন্তরীণ লকটি প্রকাশ করতে হবে। কোনও থ্রেড লকটি অর্জন এবং লকটি প্রকাশের সময়ের মধ্যে অভ্যন্তরীণ লকটির মালিক বলে বলা হয়। যতক্ষণ কোনও থ্রেড অভ্যন্তরীণ লকের মালিক, অন্য কোনও থ্রেড একই লকটি অর্জন করতে পারে না। অন্য থ্রেডটি লকটি অর্জন করার চেষ্টা করলে এটি অবরুদ্ধ হবে।

লক করার উদ্দেশ্য ভাগ করা ডেটা রক্ষা করা। উপরের উদাহরণ কোডে প্রদর্শিত আলাদা আলাদা লকগুলি কেবলমাত্র যদি প্রতিটি লক বিভিন্ন ডেটা সদস্যকে সুরক্ষিত রাখে use


সুতরাং এই উদাহরণে লকটি লকএ is লকবি অবজেক্টগুলিতে রয়েছে এবং ক্লাস এ তে নেই? এটি কি কোনও শ্রেণি স্তরের লক করার উদাহরণ?
নিমরোড

2
@ নিমরোড: এটি লকএ এবং লকবি অবজেক্টগুলিতে লক করছে এবং এ.এর উদাহরণস্বরূপ নয়, এখানে কোনও কিছুই ক্লাসে লক করছে না। ক্লাস-লেভেল লকিংয়ের অর্থ ক্লাস অবজেক্টে লক পাওয়া, এমন কিছু ব্যবহার করে static synchronizedবা করা উচিতsynchronized (A.class)
নাথান হিউজ

এখানে জবাব টিউটোরিয়ালটির লিঙ্কটি এখানে ঠিক কী জবাব দেওয়া হয়েছে তা ব্যাখ্যা করে।
আলবার্তো ডি পাওলা

18

জাভা থ্রেড একটি অর্জন বস্তুর স্তর লক যখন এটি একটি মধ্যে প্রবেশ করে দৃষ্টান্ত সিঙ্ক্রোনাইজ Java পদ্ধতি এবং একটি অর্জন বর্গ স্তর লক যখন এটা মধ্যে প্রবেশ করে স্ট্যাটিক সিঙ্ক্রোনাইজ জাভা পদ্ধতি।

আপনার ক্ষেত্রে, পদ্ধতিগুলি (উদাহরণ) একই বর্গের। সুতরাং যখনই কোনও থ্রেড জাভা সিঙ্ক্রোনাইজড পদ্ধতিতে প্রবেশ করে বা ব্লক করে তখন এটি একটি লক অর্জন করে (বস্তুটি যার উপরে পদ্ধতিটি বলা হয়)। সুতরাং প্রথম পদ্ধতিটি শেষ না হওয়া এবং লক (অবজেক্টে) প্রকাশ না হওয়া অবধি একই পদ্ধতিতে একই পদ্ধতিতে অন্য পদ্ধতি কল করা যাবে না।


আমার যদি ক্লাসের দুটি পৃথক দৃষ্টান্তে দুটি থ্রেড থাকে তবে তারা উভয় পদ্ধতি একই সাথে কার্যকর করতে সক্ষম হবে যে একটি থ্রেডকে একটি সিঙ্ক্রোনাইজড পদ্ধতি এবং অন্যটি দ্বিতীয় কলটিকে দ্বিতীয় সিঙ্ক্রোনাইজড পদ্ধতিতে কল করে। যদি আমার বোধগম্যতা সঠিক হয় তবে আমি কি private final Object lock = new object();কেবল একটি থ্রেডকেই কোনও পদ্ধতিতে কার্যকর করতে সক্ষম হওয়ার জন্য সিঙ্ক্রোনাইজড ব্যবহার করতে পারি ? ধন্যবাদ
যুগ সিং 20

13

আপনার ক্ষেত্রে আপনি ক্লাসের একই উদাহরণে দুটি পদ্ধতি সিঙ্ক্রোনাইজ করেছেন। সুতরাং, এই দুটি পদ্ধতি একই ক্লাস এ এর ​​একই উদাহরণের বিভিন্ন থ্রেডে এক সাথে চলতে পারে না তবে তারা বিভিন্ন শ্রেণীর এ উদাহরণগুলিতে করতে পারে।

class A {
    public synchronized void methodA() {
        //method A
    }
}

হিসাবে একই:

class A {
    public void methodA() {
        synchronized(this){
            // code of method A
        }
    }
}

আমি যদি কোনও লক হিসাবে সংজ্ঞায়িত করি private final Object lock = new Object();এবং এখন lockদুটি পদ্ধতিতে সিঙ্ক্রোনাইজড ব্লক ব্যবহার করি তবে কী আপনার বক্তব্য সত্য হবে? আইএমও যেহেতু অবজেক্টটি সমস্ত অবজেক্টের প্যারেন্ট ক্লাস তাই থ্রেডগুলি ক্লাসের বিভিন্ন উদাহরণে থাকলেও একসাথে কেবলমাত্র একটি সিঙ্ক্রোনাইজড ব্লকের কোড অ্যাক্সেস করতে পারে। ধন্যবাদ।
যুগ সিং

আপনি যদি ক্লাসে "প্রাইভেট ফাইনাল অবজেক্ট লক" সংজ্ঞায়িত করেন এবং এটির সাথে সিঙ্ক্রোনাইজ করেন তবে আপনার প্রতি ক্লাসে লক রয়েছে তা পূরণ করুন, সুতরাং এটি সিঙ্ক্রোনাইজড (এটি) এর মতো আচরণ করবে।
ওলেকসান্ডার_ডিজে

হ্যাঁ, অবজেক্টটি সমস্ত শ্রেণীর জন্য পিতামাতার, তবে আপনার ক্ষেত্রে "লক" উদাহরণটি "উদাহরণ প্রতি শ্রেণীর মালিকানা", সুতরাং, এটি সিঙ্ক্রোনাইজেশনের জন্য "এই" এর মতোই প্রভাব ফেলে।
ওলেকসান্ডার_ডিজে

7

ওরাকল ডকুমেন্টেশন লিঙ্ক থেকে

পদ্ধতিগুলি সিঙ্ক্রোনাইজড করার দুটি প্রভাব রয়েছে:

প্রথমত, একই বস্তুতে ইন্টারলিভের জন্য দুটি সিঙ্ক্রোনাইজড পদ্ধতির অনুরোধ সম্ভব নয়। যখন একটি থ্রেড কোনও অবজেক্টের জন্য একটি সিঙ্ক্রোনাইজড পদ্ধতি কার্যকর করে, অন্য সমস্ত থ্রেড যা একই বস্তু ব্লকের (স্থগিতাদেশ স্থগিতের) জন্য সিনক্রোনাইজড পদ্ধতিগুলিকে অনুরোধ করে যতক্ষণ না বস্তুটির সাথে প্রথম থ্রেডটি সম্পন্ন হয়।

দ্বিতীয়ত, যখন একটি সিঙ্ক্রোনাইজড পদ্ধতিটি প্রস্থান করে, এটি স্বয়ংক্রিয়ভাবে একই বস্তুর জন্য একটি সিঙ্ক্রোনাইজড পদ্ধতির পরবর্তী কোনও অনুরোধের সাথে একটি ঘটনার আগে সম্পর্ক স্থাপন করে। এটি গ্যারান্টি দেয় যে অবজেক্টের অবস্থানে পরিবর্তনগুলি সমস্ত থ্রেডে দৃশ্যমান

এটি আপনার প্রশ্নের জবাব দেবে: একই অবজেক্টে, প্রথম সিঙ্ক্রোনাইজড মেথড এক্সিকিউশন প্রক্রিয়াধীন অবস্থায় আপনি দ্বিতীয় সিঙ্ক্রোনাইজড পদ্ধতিতে কল করতে পারবেন না।

অভ্যন্তরীণ লক এবং লক আচরণ বুঝতে এই ডকুমেন্টেশন পৃষ্ঠাটিতে একবার দেখুন at


6

আপনার কোডটি নীচের কোড হিসাবে মনে করুন:

class A {

public void methodA() {
    synchronized(this){        
      //method A body
    }
}

public void methodB() {
    synchronized(this){
      // method B body
    }
}

সুতরাং, পদ্ধতি স্তরে সিঙ্ক্রোনাইজ করার সহজ অর্থ সিঙ্ক্রোনাইজড (এটি)। যদি কোনও থ্রেড এই শ্রেণীর কোনও পদ্ধতি চালায়, এটি কার্যকর করা শুরু করার আগে লকটি অর্জন করবে এবং পদ্ধতিটির সম্পাদনা শেষ না হওয়া পর্যন্ত এটি ধরে রাখবে।

তবে মেথডিএ () এখনও চলছে কি আমি বিভিন্ন থ্রেডে মেথ بی বি () চালাতে পারি? (একই জিনিস)

আসলে, এটা সম্ভব না!

অতএব, একাধিক থ্রেড একই বস্তুতে একই সাথে সংখ্যক সংখ্যক সিঙ্ক্রোনাইজড পদ্ধতি চালাতে সক্ষম হবে না।


আমি কি যদি একই বর্গের দুটি পৃথক বস্তুর উপর থ্রেড তৈরি করি? এই ক্ষেত্রে আমি যদি একটি থ্রেড থেকে একটি পদ্ধতি এবং দ্বিতীয় থ্রেড থেকে অন্য পদ্ধতিতে কল করি তবে তারা কী একই সাথে কার্যকর হবে না?
যুগ সিং

2
তারা করবে কারণ তারা বিভিন্ন বস্তু। এটি হ'ল, যদি আপনি এটি প্রতিরোধ করতে চান তবে আপনি স্থিতিশীল পদ্ধতিগুলি ব্যবহার করতে পারেন এবং ক্লাসটিকে সিঙ্ক্রোনাইজ করতে পারেন বা একটি শ্রেণীর পরিবর্তনশীল অবজেক্টটিকে লক হিসাবে ব্যবহার করতে পারেন বা ক্লাস সিঙ্গলটন তৈরি করতে পারেন। @ যুগ সিং
মাকারি

4

কেবল সমস্ত স্পষ্টতই, এটি সম্ভব যে উভয় স্ট্যাটিক সিঙ্ক্রোনাইজড এবং নন স্ট্যাটিক সিঙ্ক্রোনাইজড পদ্ধতি একই সাথে বা একযোগে চলতে পারে কারণ একটিতে অবজেক্ট লেভেল লক এবং অন্য শ্রেণির স্তরের লক রয়েছে।


3

কী সিঙ্ক্রোনাইজ যা সহজে ডুবা না সঙ্গে ধারণা এটি প্রভাব ফেলবে শুধুমাত্র যদি পদ্ধতি উপর বলা হয় একই বস্তুর এটি আগে থেকেই উত্তর এবং মন্তব্য হাইলাইট করা হয়েছে - উদাহরণস্বরূপ -

নমুনা প্রোগ্রামের নীচে পরিষ্কারভাবে একই পয়েন্টপয়েন্ট করা হয় -

public class Test {

public synchronized void methodA(String currentObjectName) throws InterruptedException {
    System.out.println(Thread.currentThread().getName() + "->" +currentObjectName + "->methodA in");
    Thread.sleep(1000);
    System.out.println(Thread.currentThread().getName() + "->" +currentObjectName + "->methodA out");
}

public synchronized void methodB(String currentObjectName)  throws InterruptedException {
    System.out.println(Thread.currentThread().getName() + "->" +currentObjectName + "->methodB in");
    Thread.sleep(1000);
    System.out.println(Thread.currentThread().getName() + "->" +currentObjectName + "->methodB out");
}

public static void main(String[] args){
    Test object1 = new Test();
    Test object2 = new Test();
    //passing object instances to the runnable to make calls later
    TestRunner runner = new TestRunner(object1,object2);
    // you need to start atleast two threads to properly see the behaviour
    Thread thread1 = new Thread(runner);
    thread1.start();
    Thread thread2 = new Thread(runner);
    thread2.start();
}
}

class TestRunner implements Runnable {
Test object1;
Test object2;

public TestRunner(Test h1,Test h2) {
    this.object1 = h1;
    this.object2 = h2;
}

@Override
public void run() {
    synchronizedEffectiveAsMethodsCalledOnSameObject(object1);
    //noEffectOfSynchronizedAsMethodsCalledOnDifferentObjects(object1,object2);
}

// this method calls the method A and B with same object instance object1 hence simultaneous NOT possible
private void synchronizedEffectiveAsMethodsCalledOnSameObject(Test object1) {
    try {
        object1.methodA("object1");
        object1.methodB("object1");
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
}

// this method calls the method A and B with different object instances object1 and object2 hence simultaneous IS possible
private void noEffectOfSynchronizedAsMethodsCalledOnDifferentObjects(Test object1,Test object2) {
    try {
        object1.methodA("object1");
        object2.methodB("object2");
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
}
}

যদি পদ্ধতিগুলিকে বিভিন্ন বস্তুর উদাহরণগুলিতে ডাকা হয় তবে একযোগে অ্যাক্সেসের অনুমতি হিসাবে কীভাবে একযোগে অ্যাক্সেসের অনুমতি দেওয়া হয়েছিল তার আউটপুটের পার্থক্যটি লক্ষ্য করুন।

NoEffectOfSynchronizedAsMethodsCalledOnDifferencesObjects () এর সাথে আউটপুট মন্তব্য করেছেন -আউটপুটটি মেথডএ আউট .. মেথডিবি আউট> পদ্ধতিতে আউট পদ্ধতিতে রয়েছে order * NoEffectOfSynchronizedAsMethodsCalledOnDifferencesObjects () * এর সাথে ওউপুট মন্তব্য করেছেন

এবং সিঙ্ক্রোনাইজড এফেক্টিভসস মেথডসক্ল্যাডঅনসেমওবজেক্ট () এর সাথে আউটপুট মন্তব্য করেছে - আউটপুট হাইডলাইট বিভাগে থ্রেড 1 এবং থ্রেড 0 দ্বারা পদ্ধতিআ এর একযোগে অ্যাক্সেস দেখায় -

* সিঙ্ক্রোনাইজড এফেক্টিভএএস মেথডস ক্যালডঅনসেমঅবজেক্ট () * এর সাথে ওপুট

থ্রেডের সংখ্যা বৃদ্ধি করা এটি আরও লক্ষণীয় করে তুলবে।


2

না এটি সম্ভব নয়, যদি এটি সম্ভব হয় তবে উভয় পদ্ধতি একই সাথে একই পরিবর্তনশীল আপডেট করতে পারে যা সহজেই ডেটাটিকে দূষিত করতে পারে।


2

হ্যাঁ, তারা উভয় থ্রেড একই সাথে চলতে পারে। আপনি যদি শ্রেণীর 2 টি অবজেক্ট তৈরি করেন তবে প্রতিটি বস্তুকে কেবল একটি লক থাকে এবং প্রতিটি সিঙ্ক্রোনাইজড পদ্ধতিতে লক প্রয়োজন। সুতরাং আপনি যদি একই সাথে চলতে চান তবে দুটি অবজেক্ট তৈরি করুন এবং তারপরে object অবজেক্টের রেফারেন্সটি ব্যবহার করে চালানোর চেষ্টা করুন।


1

আপনি এটি ক্লাসে নয় এমন বস্তুতে সিঙ্ক্রোনাইজ করছেন। সুতরাং তারা একই জিনিস একই সাথে চালাতে পারবেন না


0

দুটি ভিন্ন থ্রেড একক বস্তুর উপর একটি সাধারণ সিঙ্ক্রোনাইজড পদ্ধতি কার্যকর করে, যেহেতু বস্তুটি সমান হয়, যখন কোনও থ্রেড এটি সিঙ্ক্রোনাইজড পদ্ধতিতে ব্যবহার করে, তখন লকটি বৈচিত্র্যপূর্ণ করতে হবে, যদি লকটি সক্ষম করা থাকে তবে এই থ্রেডটি অপেক্ষা করার স্থানে যাবে, যদি লকটি অক্ষম থাকে তবে এটি বস্তুটিতে অ্যাক্সেস করতে পারে, যখন এটি অ্যাক্সেস করবে এটি লকটিকে সক্ষম করবে এবং লকটি কার্যকর হবে কেবল তখনই এটি কার্যকর হবে। যখন অন্য থ্রেড উপস্থিত হবে, এটি লকটিকে বৈচিত্র্যযুক্ত করবে, যেহেতু এটি সক্ষম করা হয়েছে এটি প্রথম থ্রেডটি তার সম্পাদন সম্পন্ন না করে এবং অবজেক্টের উপর রাখা লকটি প্রকাশ না হওয়া পর্যন্ত অপেক্ষা করবে, লকটি প্রকাশিত হওয়ার পরে দ্বিতীয় থ্রেডটি বস্তুটিতে অ্যাক্সেস অর্জন করবে এবং এটি কার্যকর করা অবধি লকটিকে সক্ষম করবে। সুতরাং মৃত্যুদন্ড কার্যকর করা হবে না, উভয় থ্রেড একের পর এক কার্যকর করা হবে,


1
দয়া করে এই জগাখিটিকে যথাযথভাবে বিরামচিহ্ন এবং মূলধন করুন। 'ভেরিফাই' এর মতো কোনও শব্দ নেই।
মারকুইস লরনে
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.