Immutable Design and Java Concurrency | Immutability Explained
Key Takeaways
This video teaches immutable design and Java concurrency, explaining immutability concepts
Full Transcript
Hey everyone, welcome back to another video and if you have been following the series then you know we are discussing the thread safety. So let's see where we are. So as you know in the last video we covered the thread local which is a way to not share the state and the second step is do not mutate. So let's say if you have to share then try to not mutate the state. So in this video we will focus on this bit. We will see how can we utilize the immutability, the concept of immutability, which can be very useful in ensuring the thread safety of a program. So we will cover what is immutability, what are the benefits of using an immutable object and in the end we will learn how can we create our own immutable objects, a simple immutable class. So let's get started. So let's start with the simple definition of an immutable object. What is an immutable object? An immutable object is an object whose state or values or attributes cannot be changed once initialized. And such an object is called immutable object. And this concept, principle or technique, whatever you call it, is called immutability. By now you should have the idea of the object and its corresponding state because we covered this concept in the thread safety video. So let's understand what do we mean by an immutable object and what do we mean by a state that cannot be changed. So for example we have an integer variable A equals to zero. So we initialized an integer variable with a value zero. Now we know we can change the value of this variable let's say to one. That means we are changing the value of an integer variable in this case. So this variable is not an immutable variable. This is a mutable variable. Why? Because the value can be changed. The variable is getting mutated. All right. Let's take an example of a string. So suppose we are creating a string string S equals to A. And we know in Java strings are generally added to the constant pool. So suppose here we have a new string object A and this S is referring to this object. Now, what if I do something like S plus B? So, what we are doing is we are changing the value from A to AB. But, we also know that strings in Java are by default, by design, immutable. What that means is instead of changing this particular object, what Java will do, it will create a new string object AB and now S will refer to this object. All right? So, this link is no longer valid. Although this string object is still in the memory, but nobody is referring to it. We have a new object with value AB and S is now referring to this object. All right? So, here we saw that it did not update the existing object. Instead, it created a new one, all right, in the memory. So, that is the immutable nature of the string. Once initialized, once created, this state or the value is not getting changed. That is the immutable nature of the string object. Now, suppose here we have this point class. Okay, it has two integer variables, two coordinates, X and Y. Then, we have a constructor and then getters and setters. So, suppose we create a point object, point P1 equals to new point, okay? And then we pass, let's say, five and 10. So, we created this point object. Now, on the heap, a new point object will be created, okay, with value, let's say, five and 10. These are the attributes or the state of this P1 object. So, P1 is referring to this object. Now, we can also do something like P1.dot set X, suppose we change it to 11. What that means is, because we are changing the same object via setter, so the new value or the new state will be 11, 10 because we change the X coordinate. So, P1 is now referring to the same object with the updated X coordinate. What that means is in this case there is a single object P1, but we are changing the state or the attribute of that object. That means P1 is a mutable object. Okay, this is not an immutable object. Why? Because the state or the attributes we can change. And similarly, let's say after some time if we do something like set Y to 20, then the object of the same state will be changed to 11, 20. All right? But, it will still refer to the same object. It's just that the state or the attributes are getting changed. Object remains the same in this case. So, such objects are called mutable objects. Okay? Mutable because their state or attributes can change. Now, what if we do something like this? Final point P1 equals to let's say new point and 5, 6. Let's take the same example. The only difference is this time we are adding the final keyword. Now, what would happen in this case? So, again we will have a new point object on the heap with the attributes let's say 5, 6 and P1 is now referring to this object. Now, when we add the final keyword, what would happen? We cannot do something like this. P1 equals to new point, let's say 7, 8. We cannot do this because this is a new object, okay? And this reference variable is now final because of this keyword. What that means is P1 will always refer to the same object, which is this one. So, this is valid. When we add the final keyword to a reference variable, that means we fix the reference variable. So, P1 will keep pointing to this object, but it doesn't mean that we cannot do something like this. Set X, let's say seven. This is still allowed. What that means is, let's say even if we add the final keyword, this is permissible, and the new state of the object will be six, {comma} Sorry, the new state will now become seven {comma} six. Why? Because we updated the X coordinate here. All right. So, just adding the final keyword to the reference variable will not make it immutable. Because we can still change the attributes. We can still change the state. It's just that by adding the final keyword, we are fixing the reference variable. So, this is not immutable. So, what is immutable then? Let's take the same example of point class. How do we make it immutable then? So, suppose, let me clear this. So, here we have the same point object. Let's say P1 equals to we initialize the constructor by passing five and six. Okay, so we have a new object five and six. Now, somehow if we can implement or redesign the class in such a way where we cannot set X and Y values after this point. Okay? That means once created and initialized, if somehow we can make sure that these values will not be changed, then P1 or the point class will be called an immutable object. An immutable class. So, that's what we mean by immutability and immutable classes and immutable objects. Let's move on and understand why immutability is preferred with thread safety, and then we will see a quick demo where we will create our own immutable class. Let's quickly understand why should we prefer immutability as and when we can. So, immutability has gained uh a lot of popularity in recent times, especially with the rise of functional programming, uh distributed systems, uh parallel programming. Because when we have immutable components or entities, we have more predictability. So, how does it relate to thread safety? Well, if you remember from our video on the thread safety, we talked about how the shared immutable state can risk the thread safety problems. And when we have immutable entities, immutable objects, the objects which cannot be changed, then we don't need synchronization between threads. The immutable objects or variables can be shared safely because nobody can change them once initialized. And if something cannot be changed, then this is thread safe. And it is also easier to understand and debug. So, for example, when you are understanding an existing piece of code, especially the concurrent code, and if you know that the objects in this piece of code are immutable objects, then you know the values will not be changed. So, you can just focus on, let's say, the coordination part where multiple threads are doing something. All right? So, it is pretty easier to understand as compared to the code where anything can change. And as I said, immutable objects can be shared freely because there are no unwanted updates. You can be sure that the value will not change. And hence, when you don't have any synchronization between threads, there is no contention. So, we can also get a performance boost depending on the implementation and use case. Now, having said this, there are some considerations that we need to keep in mind when we are designing something immutable because we know immutable means the state or the value will not change. And let's say, if there is any change, it will create a new object. So, suppose if there are frequent modifications, frequent changes to the state, then there will be new objects created repeatedly that can consume more memory. And especially, it will be costly if the objects that we are trying to create or update are large objects, costly objects. So, suppose if we have a costly immutable object, and then there are frequent changes then that means those costly objects large objects will have their own copies new copies I mean which will be way more costly and depending on the use case and the class or the object that we are trying to create as an immutable class it may have some code complexity as well because of course if you want to ensure that this piece of code or certain piece of code remains immutable then you have to put some extra logic in there. Let's move on. And now this is the demo time so we will create our own immutable so here we have the same point class which we covered in the explanation part and it is a very simple class two integer variables a constructor then getters and setters. Let's add main method and we'll see what do we mean by immutable and mutable objects in practice. So suppose we have a 30 one So let's create a thread. Like this. Start. Okay. And suppose we have a new object of point which is new point and we have X and Y 5 and 6. Let's introduce a delay in 30 one of let's say 500 milliseconds. And after this delay what 30 one will do as point P dot get X and P dot get Y. Let's add another thread and then I'll explain what we are doing. So here we have thread T2 and this is going to be T2. And instead of this delay what T2 will do, it will access the same point variable and it will change the X coordinate. So let's say it was five and now it will change to seven. So what we are trying to do here is we have a point class. So this is a single object and you can see that this single object is shared across multiple threads. Now when T1 starts, after a certain delay, it will try to get the value of X and Y. So if you remember or if you notice, uh it would expect the value to be five and six. Okay? And then there could be some decisions based on the data. Here we are simply printing it. The point is T1 expects the value to be five and six. Now we are starting another thread T2 and what this thread T2 is doing, it is changing the state of the same object. It is changing the X coordinate. All right. Now let's run this program and see the output from T1 and T2 perspective. So as you can see, T2 runs first and it changed the value of X coordinate to seven and due to this delay which may represent, let's say, the context switch or some timing related to execution. Due to this delay T1 also gets the same updated value which is seven. Here it is. T1 has point seven {comma} six. So what is happening here? Because of this update, because this point object is not immutable, this is a mutable object. Because of this update T1 is also getting impacted. So that we already know, when something is shared and mutable, there could be a thread safety risk. So that is the problem with mutable objects. Now what if we convert this point class to a immutable class? How do we do that? So, first of all, whenever we create an immutable class, the first rule is you always put a final keyword. That means you close the class for inheritance. Because, for example, even if you make an immutable class, it is possible that someone would come, extend from the class, and it can add its own behavior with new fields, which will break the immutability contract. Okay? So, by adding the final keyword, we close it for the inheritance. That means nobody can inherit from this class and change the behavior. So, if this particular class is inherently immutable, then it means it will remain immutable. So, first thing is you always close it or close the class that you are implementing for inheritance by adding the final keyword to the class declaration. The second thing is try to initialize all the attributes from the constructor. So, for example, here we have two attributes, and we are correctly initializing the attributes using constructor. So, we are passing all the variables to the constructor. And when we call the constructor to create the object, it will atomically, you can say, uh create a new object by populating all the required attributes. Okay? Then you provide the getters, but you do not provide the setters. So, I will remove the setter methods. Because, if you provide the setters, then that means you are providing an access to the object to the calling code, and the calling code can change the value by using the setter method. So, in order to have an immutable object, all right, you do not provide the setter methods. So, what are the steps? First, you add the final keyword, and you close it from the inheritance perspective. Then you initialize or you pass all the required fields to the constructor itself, and then you do not provide any setter methods. Only getters are allowed. Now, with this implementation, you see we are getting a compile-time error here. So, we can simply remove the problematic code. And now you can see, once initialized, once this object is created, there is no way to change this state. Because there are no setters, and you have passed all the required fields in the constructor itself. And this time, if we run this program, it will correctly print the actual values, which is five and six. So, that means this class is now an immutable class. So, this is a very simple example. Let's move on and take a more complicated example. So, for this example, we will have a person class. We will try to create an immutable person class. Okay. So, first of all, let's add a new class, person. Then, now let's think about the attributes this person would have. So, first of all, we will go with a string variable, name. We know strings are by default immutable. Then, we will have an integer primitive, private int age. Primitive variables, we know we cannot change by reference. These are not objects. So, integer variable is also safe. Now, let's say we have a custom object here, address. Like this. We will create the address class shortly, but that means now we have a custom class. So, we need to make sure that it remains immutable. And then, let's take an example of a mutable collection. So, suppose a list of hobbies this person would have. Like this. And let's create the custom class as well. So, we have the custom class address, and for the simplicity, let's take two attributes, city, and state. Okay. So, we know how to make it immutable. We can first of all, we will say make it final. And then, add an all argument constructor like this. And then we need to simply create the getters, not the setters. So, this address class is an immutable class now. And in the person class, we will refer this address class. And here also we know how to make person class immutable. The first step is to make it final. Then, we will add a constructor like this. And then we will add the getters. Now, here is a suggestion. We don't really need to provide the getters for all the fields blindly. We can simply focus on the fields for which we really need a getter method, okay? So, we can selectively provide the getter methods. For the demo example, I am selecting all the fields, but you don't need to do that. Now, let's create a main class. And in this class, we will have And in this class, we will have the main method. And in this main method, we will create a person object. T1 equals to new person. And there is a thread T1. So, we can directly call off platform.name T1 .start like this. Okay? And similarly, we will have another thread, T2. So, the idea is, same as with point example, we will introduce a delay of, let's say, 1 second. And after this, let me, for the simplicity, let me add a two string method here. Like this. And similarly, in the address class as well. Okay. Going back to the launcher, here we will say thread. current thread. dot get name as person. Then we can print the person object, which is P1. But in thread T2, what we will do, let's see if we can break the immutable nature of this person class. So, if we try to access the attributes, we know we can get the age, which is integer variable, so we cannot really change it. It doesn't change this overall state of the object. Same with address, we have made it immutable already. Same with name, which is a string variable, which is by design immutable. But in this case, we have a list of string, and we know list, by default, is a mutable collection. So, if we try to do something like this, hobbies and let's say I add a new hobby, XYZ. And then we put the same logger over here for thread T2. Now, let's check the person object, and we have to provide the name and all these parameters, so we can do that. Name is demo. Age is 23, let's say. And then, we need an address, so we can say new address city state and a list of hobbies. And suppose we are doing something like this add music cricket and let's change it to a temporary variable. All right. So, here we have the complete code. We are creating a person object. We are passing all the required constructor arguments. Thread T1 is simply accessing the person object and printing it, but thread T2 is trying to update the hobbies. And how is it able to do so? Because we have provided the getter, and you see from the getter, we are simply returning the hobbies, which is this list, and we know by default this is mutable. Okay, this collection can change. And that's why if we go back to the launcher, it is able to do so. It is able to add a new value to it. Now, if we try to run it, let's see what happens. So, you see for T2, we can see the hobbies as music, cricket which was the initial state but then XYZ also because it was changed here. And similarly, the same impact we can see on thread T1 as well because it is also printing the same set of hobbies. The reason we already know. So, what is happening here? We are thinking that this person class is immutable because we designed it to be immutable. We followed all the principle by adding the final keyword, passing all the arguments to the constructor, not providing the setters. But, the thing is here we are using a mutable collection, something that can change, and from the getter we are returning the value as it is. That means if you're not careful here, the caller can access the same reference, and the caller can change the state, which is happening here. And this is an important point. Because just by adding the final keyword and passing all the constructor arguments and providing getters only, a class is not guaranteed to be an immutable class. We have to do something else, especially when we are dealing with mutable objects like this. So, for example, whatever we accepted in the constructor argument, we know this is a mutable component, it is a mutable collection, and here we are setting the value as it is. Similarly, from the getter method we are returning the actual object, and that's why the caller is able to access the reference and change the value. That is breaking the immutable nature of the person object. So, how do we fix it? Well, the pattern is whenever you are initializing the object, if you see a mutable collection, just to be safe, you always create a copy of it. Either you create a copy of it or somehow you convert it into immutable or an unmodifiable collection. So, what I mean by that is in this case where we have the address, we know this is immutable, so that is fine. But, otherwise, what we generally do, we do something like this, address copy equals to get a copy of address which is passed here. And then we do something like this, this.address equal to copy. In the case of objects, because when we create the copy, the object this particular object will be accessing or will have the reference to this copy, not to this object. So, if any caller has reference to this object and if that caller changes the state of this address, this particular person class will remain unaffected. And similarly for the hobbies, what we will generally do, we will create a copy of hobbies. Then, we will set the copy here. All right? So, that is what we generally do whenever we are dealing with mutable components while designing the immutable class. Now, how can we create the copies? Well, there are different ways. You can use deep cloning. All right? So, you can use something like serialization and deserialization or any third-party library for deep cloning so that you clone each and every attribute of the object that you are passing. Or you can also use something called copy constructor. Now, we will not cover copy constructor in this video. So, as we are accepting a mutable collection here, we know we have to create a copy so that any changes done by the caller should not impact the immutable nature of the person class. How do we do that? As I said, there are a couple of ways, copy constructor, deep cloning, any third-party utility. So, what are we going to do? How do we fix it? Well, we can use the latest features of Java. I am using JDK 21. And uh so, what I can do, let's say there is a method in the list class, copy of. So, what this copy of method does, if we check the documentation, it returns an unmodifiable list. So, we are getting a copy which cannot be modified. Now, if the given collection is subsequently modified, let's say first of all, this is an unmodifiable list, but somehow if the caller has the reference and the caller tries to change or modify the list then the documentation says that the returned list will not reflect such modifications. All right, just by changing this code if we launch the program again, let's see what happens. So here as you can see this time that T1 and T2 both are showing the initial state of the hobbies music and cricket. Here even though in thread T2 we are changing the hobbies by adding a new value XYZ but because the contract says that the changes will not be reflected in the original collection so you see we don't have that value added to the actual collection. Now this person class is truly immutable. So here we are utilizing the latest features of Java so that we can easily get the unmodifiables copy. And because this is already an unmodifiables copy of the list so we don't need to create another copy in the getter method, all right? But in cases let's say where you have the mutable nature of a custom object let's say then you can create a copy from the getter method as well. Now we can do one more thing actually in this case coming back to the address class you see this is a pojo and we have to like make it immutable by adding the final keyword and doing all this. There is an easy way. What we can do let me comment this code because I will commit this to the feature branch. What we can do we can simply create a record. Like this string city and string state. Records are by default immutable just like strings records are immutable by design. What that means is now we don't need to add any final keyword or provide only getters. This is now inherently immutable. So now you see this is even better because just by creating a record, we are making the address inherently immutable. All right? This is the guarantee provided by the records. And you see we don't need to change anything here, but if we relaunch it, we don't see any changes to the output, but the thing is this person class is now immutable and we have made this immutable by using the latest features of Java, which is list.copyOf and by converting a simple pojo to record. So, for example, when you are creating your own immutable class, uh think about it if you can convert some pojos to let's say record so that they are by design immutable. And whenever you are dealing with let's say mutable collection or mutable objects, always make sure that you create a copy of it, make it unmodifiable if it's possible. All right? And try to make sure that your getter methods are always returning a copy so that any code which is calling the getter method of this object cannot change the state of your object. So, here we covered a lot of details related to immutability, uh how can we make immutable classes, what are the things that we need to consider and watch out. And that's it for now. See you in the next video. Thanks for watching.
Original Description
Code - https://github.com/therealdumbprogrammer/java-concurrency/tree/master/src/main/java/org/example/day11
Useful playlists
Concurrency - https://www.youtube.com/playlist?list=PLpxcSt9FGVVF8e3na1S1tmFd_LW6x08d9
Lambdas & Streams - https://www.youtube.com/playlist?list=PLpxcSt9FGVVGl_IwSP1o4AmSrQc6z4SFt
Collection Framework - https://www.youtube.com/playlist?list=PLpxcSt9FGVVHp2QywnxaGNQGMErthxRfU
Core Java - https://www.youtube.com/playlist?list=PLpxcSt9FGVVHbN-b6igbG7qI3XmsGAfsu
---------------------------------------------------------------------------
In this video, we explore the powerful concept of Java Concurrency and how using Immutable Classes can make your multi-threaded programs more robust and thread-safe.
What you'll learn:
1) The fundamentals of Java Concurrency and how multiple threads interact
2) Why Immutable Objects are inherently thread-safe and how to implement them
3) How immutability simplifies concurrent programming and eliminates the need for complex synchronization
4) Practical examples showing how to create Immutable Classes in Java, including the use of records and modern Java features (JDK 21 compliant)
5) Best practices for applying immutability in multi-threaded applications
00:00 Intro
00:45 Immutability explained
07:28 Benefits
9:03 Considerations
10:05 Demo
15:54 Advanced scenario
--------------------------------------------------------
#coding #java #programming #techexplained #multithreadinginjava #multithreading #concurrency
Watch on YouTube ↗
(saves to browser)
Sign in to unlock AI tutor explanation · ⚡30
Playlist
Playlist UUjEfGki6QSKs0mL6-h2pm3Q · TheCodeAlchemist · 3 of 50
1
2
▶
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
#java threadlocal #coding #programming #education #softwareengineer #shorts
TheCodeAlchemist
ThreadLocal values #java #coding #codingtutorial #programming #programmer #education #shorts
TheCodeAlchemist
Immutable Design and Java Concurrency | Immutability Explained
TheCodeAlchemist
#java concurrency and immutability #coding #programming #100k #shorts #javaprogramming
TheCodeAlchemist
MASTER HTTP Basic Authentication in Spring Boot in Just 1 Hour | Step-by-Step Tutorial
TheCodeAlchemist
#springsecurity #java #coding #programming #springboot #education #javaprogramming #shorts
TheCodeAlchemist
Encoding passwords in #springsecurity #springboot #java #programming #coding #security
TheCodeAlchemist
#springboot #coding #springsecurity #shorts #java #programming
TheCodeAlchemist
SECURE Your App with Roles and Permissions in Spring Security!
TheCodeAlchemist
#springsecurity roles & permissions #java #programming #coding #shorts #springboot
TheCodeAlchemist
#java #springboot #spring #springsecurity #coding #programming #shorts
TheCodeAlchemist
Mastering Pre-Authentication with API Keys Like a PRO
TheCodeAlchemist
What is an Event Streaming Platform #kafka #java #coding #youtubeshorts
TheCodeAlchemist
#apachekafka #coding #code #java #javadevelopment #programming #youtubeshorts
TheCodeAlchemist
Running Kafka in KRaft Mode without Zookeeper
TheCodeAlchemist
#tutorial #kafka #coding #javadevelopment #java #programming #youtubeshorts
TheCodeAlchemist
Kafka Producer and Consumer with Java: Hands-On Tutorial
TheCodeAlchemist
How to Use Kafka Consumer Groups in Java | Beginner-Friendly Demo
TheCodeAlchemist
#kafka consumer groups #kafkatutorial #java #programming #coding #shorts #apachekafka
TheCodeAlchemist
Sticky vs Hash Partitioner in Kafka: Full Guide + Java Consumer Group Demo
TheCodeAlchemist
Step-by-Step Kafka Transactions Demo
TheCodeAlchemist
The DEVELOPER'S Guide to AI and ML: Fundamentals
TheCodeAlchemist
LLMs Explained: Tokens, Embeddings, and API Basics
TheCodeAlchemist
Your first OpenAI API App - Step-by-Step Guide
TheCodeAlchemist
#chatgpt #llm #openai #tutorial #technology #tech #programming
TheCodeAlchemist
JVM Bytecode Made Simple: Essential Concepts
TheCodeAlchemist
Master #java Bytecode #jvm #jvminternals #programming #coding #shorts
TheCodeAlchemist
#jvm operand #stack #explained #java #coding #programming
TheCodeAlchemist
JVM Internals: JVM Opcodes and Java ClassFile Explained
TheCodeAlchemist
Java Bytecode Deep Dive | What JVM Sees That You Don’t
TheCodeAlchemist
#java #bytecode constant pool #programming #coding #youtubeshorts
TheCodeAlchemist
Inside the JVM: Class Loading Explained
TheCodeAlchemist
Java Developers: You MUST Understand These 5 JVM Memory Areas
TheCodeAlchemist
User Signup with Email Verification 🔥 Spring Boot + Spring Security
TheCodeAlchemist
How to Build a Secure Password Reset Flow | Spring Security
TheCodeAlchemist
#springboot #springsecurity #passwordreset #java #programming #javadeveloper #programmingshorts
TheCodeAlchemist
JWT Simplified | What Developers Must Know About Token-Based Auth
TheCodeAlchemist
#jwt #security #springsecurity #springboot #java #programming #coding #codingtutorial #codingtips
TheCodeAlchemist
#jwt #jwtauthentication #authentication #security #websecurity #springsecurity #springboot #java
TheCodeAlchemist
Master Spring Security JWT in 1 Hour
TheCodeAlchemist
Want to Master Payment Processing? Watch This Now
TheCodeAlchemist
#paymentgateways #java #coding #programming
TheCodeAlchemist
#education #paymentgateways #payments #paypaltutorial #shorts #programming #programmingshorts
TheCodeAlchemist
Stripe Payments with Spring Boot | Full Hands-On Tutorial
TheCodeAlchemist
#paymentgateways with #springboot #java #coding #programmingshorts #programming
TheCodeAlchemist
#java #javacoding #coding #paymentgateways #payments #springboot #springboottutorial
TheCodeAlchemist
#java #coding #programming #jvm #codingtips #programmingshorts
TheCodeAlchemist
Can Spring Boot Apps Really Deploy in Minutes on Kubernetes?
TheCodeAlchemist
#java on #kubernetes with #springboot #programming #coding #programmingshorts
TheCodeAlchemist
Spring Boot + Postgres on Kubernetes | Cloud-Native Series
TheCodeAlchemist
Related Reads
📰
📰
📰
📰
Follow-up: The ArxivLens Protocol: Transforming Research Nois
Dev.to AI
On July 1, 2026, arXiv will spin out from Cornell University, its home for the past 25 years, to become an independent nonprofit organization. Major funding support from Simons Foundation and Schmidt Sciences. Ditching the red for their website. [N]
Reddit r/MachineLearning
CS-NRRM™ Official Publications: Paper 1 and Paper 2 Are Now Available
Medium · Data Science
Found a potential mistake in an ICLR 2026 blogpost [D]
Reddit r/MachineLearning
Chapters (6)
Intro
0:45
Immutability explained
7:28
Benefits
9:03
Considerations
10:05
Demo
15:54
Advanced scenario
🎓
Tutor Explanation
DeepCamp AI