Multithreading - Study Notes 1

Understanding multi-threading

  • What is a thread

    • A thread is a path of program execution. A process can contain multiple threads.
    • Multi-threaded concurrent execution can improve the efficiency of the program and complete multiple tasks at the same time
  • Multi-threaded application scenarios

    • Starscream shares the screen to multiple computers at the same time
    • Xunlei enables multiple threads to download together
    • QQ video with multiple people at the same time
    • Server handles multiple client requests simultaneously

    The difference between multi-thread parallelism and concurrency

  • Parallelism means that two tasks are running at the same time, that is, while task A is in progress, task B is also in progress. (requires multi-core CPU)

  • Concurrency means that two tasks are requested to run, and the processor can only accept one task, so the two tasks are arranged to be executed in turn. Due to the short time interval, it makes people feel that both tasks are running.

  • For example, when I chat with two netizens, my left hand operates a computer to chat with A, and my right hand uses another computer to chat with B. This is called parallelism.

  • If I use a computer, I will first send a message to A, then immediately send a message to B, then chat with A, and then chat with B. This is called concurrency.

Java program operation principle

  • Java program operation principle

    • The Java command will start the Java virtual machine. Starting the JVM is equivalent to starting an application, that is, starting a process. The process will automatically start a "main thread", and then the main thread will call the main method of a certain class.
  • Is the startup of JVM multi-threaded?

    • JVM startup starts at least the garbage collection thread and the main thread, so it is multi-threaded.

How to implement multi-threaded programs

1.Inherit Thread
  • Define a class that inherits Thread
  • Override run method
  • Write what the new thread needs to do in the run method
  • Create thread object
  • When a new thread is started, the run method will be automatically executed internally.
public class Demo2_Thread {
    
    
	public static void main(String[] args) {
    
    
		MyThread mt = new MyThread();							//4,创建自定义类的对象
		mt.start();												//5,开启线程
		
		for(int i = 0; i < 3000; i++) {
    
    
			System.out.println("bb");
		}
	}

}
class MyThread extends Thread {
    
    									//1,定义类继承Thread
	public void run() {
    
    											//2,重写run方法
		for(int i = 0; i < 3000; i++) {
    
    							//3,将要执行的代码,写在run方法中
			System.out.println("aaaaaaaaaaaaaaa");
		}
	}
}
2. Implement Runnable
  • Define a class that implements the Runnable interface
  • Implement run method
  • Write what the new thread needs to do in the run method
  • Create a custom Runnable subclass object
  • Create a Thread object and pass in Runnable
  • Call start() to start a new thread, and the run() method of Runnable will be automatically called internally.
public class Demo3_Runnable {
    
    
     	public static void main(String[] args) {
    
    
     		MyRunnable mr = new MyRunnable();						//4,创建自定义类对象
     		Thread t = new Thread(mr);								//5,将其当作参数传递给Thread的构造函数
     		t.start();												//6,开启线程
for(int i = 0; i < 3000; i++) {
    
    
			System.out.println("bb");
		}
	}
}

class MyRunnable implements Runnable {
    
    							//1,自定义类实现Runnable接口
	@Override
	public void run() {
    
    											//2,重写run方法
		for(int i = 0; i < 3000; i++) {
    
    							//3,将要执行的代码,写在run方法中
			System.out.println("aaaaaaaaaaaaaaaaaa");
		}
	}
	
}     		

Multithreading (the principle of implementing Runnable)

  • 1. Look at the constructor of the Thread class and pass a reference to the Runnable interface
  • 2. Find the passed target through the init() method and assign it to the target of the member variable.
  • 3. Check the run method and find that there is a judgment in the run method. If the target is not null, the run method of the Runnable interface subclass object will be called.

The difference between two ways to implement multithreading

  • View the difference in source code:

  • a. Inherit Thread: Since the subclass overrides the run() of the Thread class, when calling start(), directly find the run() method of the subclass.

  • b. Implement Runnable: The reference of Runnable is passed in the constructor, and the member variable remembers it. When start() calls the run() method, it internally determines whether the reference of the member variable Runnable is empty. If it is not empty, it will be checked when compiling. Runnable's run() executes the subclass's run() method at runtime.

  • Inherit Thread

  • The advantage is: you can use the methods in the Thread class directly, and the code is simple

  • The disadvantage is: if you already have a parent class, you cannot use this method

  • Implement the Runnable interface

  • The advantage is: even if the thread class you define has a parent class, it doesn’t matter, because you can also implement the interface with the parent class, and the interface can be implemented multiple times.

  • The disadvantage is that you cannot directly use the methods in Thread. You need to obtain the thread object first before you can get the Thread method. The code is complicated.

    Two ways for anonymous inner classes to implement threads

  • Inherit the Thread class

 new Thread() {
    
                                                                   //1.继承Thread类
            public void run() {
    
                                                          //2.重写run方法
                for (int i = 0; i < 1000; i++) {
    
                                         //3.将要执行的代码写在run方法中
                    System.out.println("saaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa");
                }
            }
        }.start();                                                                   //4.开启线程
  • Implement the Runnable interface
        new Thread(new Runnable() {
    
                                                      //1.将Runnable的子类对象传递给Thread
            @Override
            public void run() {
    
                                                          //2.重写run方法
                for (int i = 0; i < 1000; i++) {
    
                                         //3.将要执行的代码写在run方法中
                    System.out.println("bbbbbbbbbbb");
                }
            }
        }).start();                                                                  //4.开启线程

Get thread name and set name

  • Get the name of the thread object through the getName() method
new Thread() {
    
                                                                   //1.继承Thread类
            public void run() {
    
                                                          //2.重写run方法
                for (int i = 0; i < 1000; i++) {
    
                                         //3.将要执行的代码写在run方法中
                    System.out.println("线程名为:"+getName());
                }
            }
        }.start();                                                                   //4.开启线程

Insert image description here

  • You can pass in a String type name through the constructor
new Thread("主线程1") {
    
                                                                   //1.继承Thread类
            public void run() {
    
                                                          //2.重写run方法
                for (int i = 0; i < 1000; i++) {
    
                                         //3.将要执行的代码写在run方法中
                    System.out.println("线程名为:"+getName());
                }
            }
        }.start();                                                                   //4.开启线程

Insert image description here

  • The name of the thread object can be set through the setName(String) method
Thread t1 = new Thread() {
    
                                                       //1.继承Thread类
            public void run() {
    
                                                          //2.重写run方法
                for (int i = 0; i < 1000; i++) {
    
                                         //3.将要执行的代码写在run方法中
                    System.out.println("线程名为:"+getName());
                }
            }
        };                                                                          //4.开启线程

        t1.setName("设置线程名1");
        t1.start();

Insert image description here

Get the object of the current thread——hread.currentThread()

  • hread.currentThread(), obtains the object of the current thread, and the main thread can also obtain it
Thread.currentThread().setName("1001");                                        //获取主函数线程的引用,并改名字
        System.out.println(Thread.currentThread().getName());                      //获取主函数线程的引用,并获取名字

        new Thread(new Runnable() {
    
                                                      //1.将Runnable的子类对象传递给Thread
            @Override
            public void run() {
    
                                                          //2.重写run方法
                for (int i = 0; i < 10; i++) {
    
                                         //3.将要执行的代码写在run方法中
                    System.out.println("bbbbbbbbbbb");
                }
            }
        }).start();                                                                //4.开启线程

Insert image description here

Sleeping thread——Thread.sleep

    • Thread.sleep(milliseconds), controls the current thread to sleep for a number of milliseconds. 1 second = 1000 milliseconds.
  • Thread.sleep(milliseconds, nanoseconds), controls the current thread to sleep for a number of milliseconds 1 second = 1000 milliseconds 1 second = 1000 * 1000 * 1000 nanoseconds 1000000000
 new Thread(new Runnable() {
    
                                                      //1.将Runnable的子类对象传递给Thread
            @Override
            public void run() {
    
                                                          //2.重写run方法
                for (int i = 0; i < 10; i++) {
    
                                         //3.将要执行的代码写在run方法中
                    System.out.println("倒计时"+i+"秒");
                    try {
    
    
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
    
    
                        e.printStackTrace();
                    }
                }
            }
        }).start();                                                                //4.开启线程

Insert image description here

Daemon thread——setDaemon

  • setDaemon(), sets a thread as a daemon thread. This thread will not execute alone. It will automatically exit after all other non-daemon threads have finished executing.
public static void main(String[] args) {
    
    
     Thread t1 = new Thread() {
    
                                                       //1.继承Thread类
            public void run() {
    
                                                          //2.重写run方法
                for (int i = 0; i < 2; i++) {
    
                                         //3.将要执行的代码写在run方法中
                    System.out.println("线程名为:" + getName());
                    }
                }
        };                                                                          //4.开启线程

     Thread t2 = new Thread() {
    
                                                      //1.将Runnable的子类对象传递给Thread
            @Override
            public void run() {
    
                                                          //2.重写run方法
                for (int i = 0; i < 5; i++) {
    
                                         //3.将要执行的代码写在run方法中
                    System.out.println("倒计时"+i+"秒");
                    try {
    
    
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
    
    
                        e.printStackTrace();
                    }
                }
            }
        };                                                                //4.开启线程

        t2.setDaemon(true);                                                //将t2设置为守护线程

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

Insert image description here
There is a time buffer, so non-daemon threads sometimes execute

Join thread——join

  • join(), the current thread pauses and waits for the specified thread to finish executing before the current thread continues.
  • join(int), you can wait for the specified milliseconds before continuing.
 public static void main(String[] args) {
    
    
     final Thread t1 = new Thread() {
    
                                                       //1.继承Thread类
            public void run() {
    
                                                          //2.重写run方法
                for (int i = 0; i < 5; i++) {
    
                                         //3.将要执行的代码写在run方法中
                    System.out.println("线程名为:" + getName());
                    }
                }
        };                                                                          //4.开启线程

     Thread t2 = new Thread() {
    
                                                      //1.将Runnable的子类对象传递给Thread
            @Override
            public void run() {
    
                                                          //2.重写run方法
                for (int i = 0; i < 5; i++) {
    
                                         //3.将要执行的代码写在run方法中
                  if (i==2){
    
    
                      try {
    
    
                         // t1.join();                                                //匿名内部类在使用它所在方法中的局部变量时必须用final修饰
                          t1.join(1);                                           //插队指定时间,在指定时间执行完后,两条线程继续交替执行
                      } catch (InterruptedException e) {
    
    
                          e.printStackTrace();
                      }
                  }

                    System.out.println("倒计时"+i+"秒");


                }
            }
        };                                                                //4.开启线程
        t1.start();
        t2.start();
    }

Insert image description here

Courtesy thread——yield

  • yield yields cpu
public static void main(String[] args) {
    
    
        new MyThread().start();
        new MyThread().start();                                  
    }
}
    class MyThread extends Thread{
    
    
        public void run(){
    
    
            for (int i = 0 ;i<1000;i++){
    
    
                if (i % 10 ==0){
    
    
                    Thread.yield();

                }
                System.out.println(getName()+"线程"+i);
            }
        }
    }

Insert image description here

Set the priority of the thread-setPriority

  • setPriority() sets the priority of the thread
public static void main(String[] args) {
    
    
        Thread t1 = new Thread() {
    
                                                       //1.继承Thread类
            public void run() {
    
                                                          //2.重写run方法
                for (int i = 0; i < 100; i++) {
    
                                         //3.将要执行的代码写在run方法中
                    System.out.println("线程名为:" + getName()+"____"+i);
                }
            }
        };                                                                          //4.开启线程

        Thread t2 = new Thread() {
    
                                                      //1.将Runnable的子类对象传递给Thread
            @Override
            public void run() {
    
                                                          //2.重写run方法
                for (int i = 0; i < 100; i++) {
    
                                         //3.将要执行的代码写在run方法中
                    System.out.println(getName()+"倒计时" + i + "秒");
                }
            }
        };                                                                //4.开启线程
        t1.setPriority(Thread.MIN_PRIORITY);//设置最小优先级
        t2.setPriority(Thread.MAX_PRIORITY);//设置最大优先级

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

Insert image description here

synchronized code block

  • 1. Under what circumstances is synchronization required?
    • When multiple threads are concurrent and multiple pieces of code are executed at the same time, we hope that the CPU will not switch to other threads during the execution of a certain piece of code. In this case, synchronization is required.
    • If two pieces of code are synchronized, then only one piece of code can be executed at the same time, and the other piece of code will not be executed until one piece of code is executed.
  • 2. Synchronized code blocks
    • Use the synchronized keyword plus a lock object to define a piece of code. This is called a synchronized code block.
    • If multiple synchronized code blocks use the same lock object, then they are synchronized
public class Synchronized {
    
    
    public static void main(String[] agr) {
    
    
        final printer p = new printer();
        new Thread() {
    
    
            public void run() {
    
    
                while (true) {
    
    
                    p.print1();
                }
            }
        }.start();
        new Thread() {
    
    
            public void run() {
    
    
                while (true) {
    
    
                    p.print2();
                }
            }
        }.start();
    }
}

class printer {
    
    
    public void print1() {
    
    
        demo d = new demo();
        synchronized (d) {
    
       //锁对象可以是任意对象,但是被锁的代码需要保证是同一把锁,不能用匿名对象
            System.out.print("北");
            System.out.print("京");
            System.out.print("欢");
            System.out.print("迎");
            System.out.print("您");
            System.out.print("\r\n");
        }
    }
    /*
     * 非静态同步函数的锁是:this
     * 静态的同步函数的锁是:字节码对象
     */
    public void print2() {
    
    
        demo d = new demo();
        synchronized (d) {
    
    
            System.out.print("武");
            System.out.print("汉");
            System.out.print("热");
            System.out.print("干");
            System.out.print("面");
            System.out.print("\r\n");
        }
    }
}

class demo {
    
    

}

Insert image description here

  • Use the synchronized keyword to modify a method. All code in the method is synchronized.
public class Synchronized {
    
    
    public static void main(String[] agr) {
    
    
        final printer p = new printer();
        new Thread() {
    
    
            public void run() {
    
    
                while (true) {
    
    
                    p.print1();
                }
            }
        }.start();
        new Thread() {
    
    
            public void run() {
    
    
                while (true) {
    
    
                    p.print2();
                }
            }
        }.start();
    }
}

/**
 * 非静态的同步方法的锁对象是this
 * 静态的同步方法的锁对象是该类的字节码对象
 */
class printer {
    
    
    public synchronized void print1() {
    
    
        //锁对象可以是任意对象,但是被锁的代码需要保证是同一把锁,不能用匿名对象
        //使用synchronized关键字修饰一个方法, 该方法中所有的代码都是同步的
        System.out.print("北");
        System.out.print("京");
        System.out.print("欢");
        System.out.print("迎");
        System.out.print("您");
        System.out.print("\r\n");

    }

    /*
     * 非静态同步函数的锁是:this
     * 静态的同步函数的锁是:字节码对象
     */
    public void print2() {
    
    

        synchronized (this) {
    
    
            System.out.print("武");
            System.out.print("汉");
            System.out.print("热");
            System.out.print("干");
            System.out.print("面");
            System.out.print("\r\n");
        }
    }
}

Insert image description here

Thread safety issues

  • When multiple threads operate the same data concurrently, thread safety issues may occur.
  • This problem can be solved by using synchronization technology. Synchronize the code that operates the data, instead of multiple threads operating at the same time.
public class Demo2_Synchronized {
    
    
    public static void main(String[] args) {
    
    
        TicketsSeller t1 = new TicketsSeller();
        TicketsSeller t2 = new TicketsSeller();
        TicketsSeller t3 = new TicketsSeller();
        TicketsSeller t4 = new TicketsSeller();

        t1.setName("窗口1");
        t2.setName("窗口2");
        t3.setName("窗口3");
        t4.setName("窗口4");
        t1.start();
        t2.start();
        t3.start();
        t4.start();
    }

}

class TicketsSeller extends Thread {
    
    
    private static int tickets = 100;
    static Object obj = new Object();
    public TicketsSeller() {
    
    
        super();

    }
    public TicketsSeller(String name) {
    
    
        super(name);
    }
    public void run() {
    
    
        while(true) {
    
    
            synchronized(obj) {
    
    
                if(tickets <= 0)
                    break;
                try {
    
    
                    Thread.sleep(10);//线程1睡,线程2睡,线程3睡,线程4睡
                } catch (InterruptedException e) {
    
    

                    e.printStackTrace();
                }
                System.out.println(getName() + "...这是第" + tickets-- + "号票");
            }
        }
    }
}


Insert image description here

Starting a thread multiple times is illegal

public class Demo2_Synchronized {
    
    
    public static void main(String[] args) {
    
    
        TicketsSeller ticketsSeller = new TicketsSeller();

       /* Thread t1 = new Thread(ticketsSeller);//多次启动一个线程是非法的
        t1.start();
        t1.start();
        t1.start();
        t1.start();*/

    }
}

class TicketsSeller implements Runnable {
    
    
    private static int tickets = 100;

    public void run() {
    
    
        while(true) {
    
    
            synchronized(TicketsSeller.class) {
    
    
                if(tickets <= 0)
                    break;
                try {
    
    
                    Thread.sleep(10);//线程1睡,线程2睡,线程3睡,线程4睡
                } catch (InterruptedException e) {
    
    

                    e.printStackTrace();
                }
                System.out.println(Thread.currentThread().getName() + "...这是第" + tickets-- + "号票");
            }
        }
    }
}


deadlock

  • When synchronizing multiple threads, if the synchronization code is nested and uses the same lock, deadlock may occur.

  • Try not to use nested
    Insert image description here

    private static String s1 = "筷子左";
    private static String s2 = "筷子右";

    public static void main(String[] args) {
    
    
        new Thread() {
    
    
            public void run() {
    
    
                while (true) {
    
    
                    synchronized (s1) {
    
    
                        System.out.println(getName() + "...拿到" + s1 + "等待" + s2);
                        synchronized (s2) {
    
    
                            System.out.println(getName() + "...拿到" + s2 + "开吃");
                        }
                    }
                }
            }
        }.start();

        new Thread() {
    
    
            public void run() {
    
    
                while (true) {
    
    
                    synchronized (s2) {
    
    
                        System.out.println(getName() + "...拿到" + s2 + "等待" + s1);
                        synchronized (s1) {
    
    
                            System.out.println(getName() + "...拿到" + s1 + "开吃");
                        }
                    }
                }
            }
        }.start();
    }

Thread safety

Thread safety means that a locking mechanism is used during multi-thread access . When a thread accesses certain data of this class, it is protected and other threads cannot access it until the thread has finished reading, and then other threads can use it. There will be no data inconsistency or data pollution.
Thread insecurity means that data access protection is not provided . It is possible that multiple threads may change data successively, causing the data obtained to be dirty data.

Vector is thread safe, ArrayList is thread unsafe

Vector and ArrayList are both single-column, variable-length collections, and the bottom layer of both is based on arrays.

  • The difference between Vector and ArrayList
    • Initialization capacity: If you use no-parameter construction to create an instance, the default capacity of ArrayList is 0 and the default initial capacity of Vector is 10
    • Expansion mechanism: The expanded capacity of ArrayList is 1.5 times the last time, and the expanded capacity of Vector is 2 times the previous time.
    • Thread safety: ArrayList is thread-unsafe. All methods of Vector are modified with the synchronized keyword and are thread-safe.
    • Efficiency: Vector's methods are all modified using synchronized, which is low in efficiency, while ArrayList's efficiency is high.
      Original link
import java.util.ArrayList;
import java.util.List;
import java.util.Vector;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class VectorTest {
    
    
    private Vector vector = new Vector();
    private List list = new ArrayList();

    public static void main(String[] args) throws InterruptedException {
    
    
        final VectorTest demo = new VectorTest();
        //创建可缓存线程池(CachedThreadPool)的 ExecutorService 对象
        //调用 Executors.newCachedThreadPool() 方法,可以获取一个可根据需求自动调整大小的线程池
        ExecutorService pool = Executors.newCachedThreadPool();
        for(int i=0;i<10000;i++){
    
    
            final int idx = i;
            //将一个 Runnable 对象提交给线程池 pool 进行执行
            //调用 pool.execute(...) 时,线程池会自动从线程池中选择一个空闲的线程来执行你定义的任务逻辑
            pool.execute(new Runnable() {
    
    
                @Override
                public void run() {
    
    
                    try {
    
    
                        demo.addVector(idx);
                        demo.addList(idx);
                    } catch (InterruptedException e) {
    
    
                        e.printStackTrace();
                    }
                }
            });
        }
        Thread.sleep(5000);
        demo.printInfo();
    }

    public void addVector(int data) throws InterruptedException {
    
    
        vector.add(data);
    }

    public void addList(int data) throws InterruptedException {
    
    
        list.add(data);
    }
    public void printInfo(){
    
    
        System.out.println("vector:"+vector.size());
        System.out.println("list:"+list.size());
    }

}

Insert image description here

StringBuffer is thread-safe, StringBuilder is thread-unsafe

Original link

  • The difference between StringBuffer and StringBuilder

    • Thread safety: StringBuffer: Thread safety. StringBuilder: Thread-unsafe.
    • Buffer: StringBuffer Each time you get toString, you will directly use the toStringCache value of the buffer to construct a string. StringBuilder needs to copy the character array every time and then construct a string
    • Performance: StringBuffer is thread-safe, and all its public methods are synchronized. StringBuilder does not lock and synchronize methods, so there is no doubt that the performance of StringBuilder is much greater than that of StringBuffer.
  • StringBuffer is suitable for multi-threaded operations on the same StringBuffer, and StringBuilder is more suitable for single-threaded applications

import java.util.concurrent.CountDownLatch;

public class StringBufferTest {
    
    
    public static void main(String[] args){
    
    
        testStringBuilderAndStringBuffer();
    }

    public static void testStringBuilderAndStringBuffer(){
    
    
        //证明StringBuffer线程安全,StringBuilder线程不安全
        StringBuffer stringBuffer = new StringBuffer();
        StringBuilder stringBuilder = new StringBuilder();
        /**
         * CountDownLatch类可以使一个线程等待其他线程都执行完毕后再执行。
         * 实现原理:
         * 通过计数器来实现,计数器的初始值使线程的数量。每当一个线程执行完毕,计数器的数量就会-1,
         * 当计数器的数量为0的时候,表示线程已经执行完毕,然后在计数器锁上等待的线程就会被唤醒,开始执行。
         */
        CountDownLatch latch1 = new CountDownLatch(1000);
        CountDownLatch latch2 = new CountDownLatch(1000);

        for(int i=0;i<1000;i++){
    
    
            new Thread(new Runnable() {
    
    

                @Override
                public void run() {
    
    
                    try {
    
    
                        stringBuilder.append(1);

                    } catch (Exception e) {
    
    
                        e.printStackTrace();
                    } finally {
    
    
                        //减少锁存器的计数,如果计数达到零,释放所有等待的线程
                        latch1.countDown();
                    }
                }
            }).start();
        }

        for(int i=0;i<1000;i++){
    
    
            new Thread(new Runnable() {
    
    

                @Override
                public void run() {
    
    

                    try {
    
    
                        stringBuffer.append(1);

                    } catch (Exception e) {
    
    
                        e.printStackTrace();
                    } finally {
    
    
                        latch2.countDown();
                    }

                }
            }).start();
        }

        try {
    
    
            latch1.await();
            System.out.println("StringBuilder的长度为:"+stringBuilder.length());
            latch2.await();
            System.out.println("StringBuffer的长度为:"+stringBuffer.length());
        } catch (InterruptedException e) {
    
    
            // 自动生成捕获块
            e.printStackTrace();
        }
    }
}

operation result:
Insert image description here

  • Hashtable is thread-safe, HashMap is thread-unsafe
    • Thread safety: hashmap is not thread-safe, while hashtable is thread-safe, because the internal methods of hashtable basically use synchronized modification (synchronization lock);

    • Execution efficiency: Because of thread safety issues, hashmap is more efficient than hashtable.
      Support for Null key and Null value: hashmap can store null keys and values, but there can only be one null as a key, and there can be multiple null as values. ; Hashtable does not allow null keys and null values, otherwise a nullpointerexception will be thrown;

    • Data structure: Hashmap after JDK1.8 has undergone major changes in resolving hash conflicts. When the length of the linked list is greater than the threshold (default is 8) and the length of the array is greater than 64, the linked list is converted into a red-black tree to reduce search time. . Therefore, the bottom layer of hashmap uses array + linked list + red-black tree, while hashtable does not have such a mechanism and only uses array + linked list;

    • Expansion method: If the initial value of the capacity cannot be specified when creating, the default initial size of hashtable is 11, and each subsequent expansion will be the original 2n+1. The default initialization size of hashmap is 16. Each subsequent expansion will be twice the original size;

    • Specify the capacity: If the initial value of the capacity is given when creating, the hashtable will directly use the size you gave, and the hashmap will expand it to a power of 2 (guaranteed using the tablesizefor() method), so hashmap Always use a power of 2 for the hash table size.
      Original link

  1. Hashtable is thread safe
import java.util.Hashtable;
import java.util.concurrent.CountDownLatch;

/**
 * 通过多个线程向同一个HashTable插入一个key值完全相同的key值,最后在线程都执行完后打印HashTable的大小.size
 * 正确结果应该是:只有 大小输出为 1
 */

public class TestHashTable {
    
    
    public static void main(String[] args) {
    
    
        Hashtable<String,Integer> hashtable = new Hashtable<String,Integer>();
        /*
         * CountDownLatch类可以使一个线程等待其他线程都执行完毕后再执行。
         * 实现原理:
         * 通过计数器来实现,计数器的初始值使线程的数量。每当一个线程执行完毕,计数器的数量就会-1,
         * 当计数器的数量为0的时候,表示线程已经执行完毕,然后在计数器锁上等待的线程就会被唤醒,开始执行。
         */
        CountDownLatch countDownLatch = new CountDownLatch(10000);
        ThreadTable1 threadTable1 = new ThreadTable1();
        threadTable1.setCountDownLatch(countDownLatch);
        threadTable1.setHashTable(hashtable);
        for(int i=0;i<10000;i++)
        {
    
    
            new Thread(threadTable1,"线程"+i).start();
        }
        try {
    
    
            countDownLatch.await();
        } catch (InterruptedException e) {
    
    
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        System.out.println(hashtable.size());
    }
}

class ThreadTable1 implements Runnable
{
    
    
    Hashtable<String, Integer> hashtable;
    CountDownLatch countDownLatch;
    public void setCountDownLatch(CountDownLatch countDownLatch) {
    
    
        this.countDownLatch = countDownLatch;
    }
    public void setHashTable(Hashtable<String, Integer> hashtable) {
    
    
        this.hashtable = hashtable;
    }
    @Override
    public void run() {
    
    
        //将指定的 key映射到此 key value中指定的value。
        hashtable.put("dd", 1);

        //减少锁存器的计数,如果计数达到零,释放所有等待的线程
        countDownLatch.countDown();
    }
}

Insert image description here
2. HashMap is thread-unsafe

import java.util.HashMap;
import java.util.concurrent.CountDownLatch;
/**
 * 通过多个线程向同一个HashMap插入一个key值完全相同的key值,最后在线程都执行完后打印HashMap的大小.size
 * 正确结果应该是:只有 大小输出为 1
 */
public class TestHashMap {
    
    

    public static void main(String[] args) {
    
    
        /*
         * CountDownLatch类可以使一个线程等待其他线程都执行完毕后再执行。
         * 实现原理:
         * 通过计数器来实现,计数器的初始值使线程的数量。每当一个线程执行完毕,计数器的数量就会-1,
         * 当计数器的数量为0的时候,表示线程已经执行完毕,然后在计数器锁上等待的线程就会被唤醒,开始执行。
         */
        CountDownLatch countDownLatch = new CountDownLatch(10000);
        HashMap<String, Integer> hashMap = new HashMap<String, Integer>();
        ThreadMap1 threadMap1 = new ThreadMap1();
        threadMap1.setHashMap(hashMap);
        threadMap1.setCountDownLatch(countDownLatch);
        for (int i = 0; i < 10000; i++) {
    
    
            //启动10个线程
            new Thread(threadMap1,"线程"+i).start();
        }
        try {
    
    
            countDownLatch.await();
        } catch (InterruptedException e) {
    
    
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        System.out.println(hashMap.size());

    }
}

class ThreadMap1 implements Runnable {
    
    
    HashMap<String, Integer> hashMap;
    CountDownLatch countDownLatch;
    public void setCountDownLatch(CountDownLatch countDownLatch) {
    
    
        this.countDownLatch = countDownLatch;
    }
    public void setHashMap(HashMap<String, Integer> hashMap) {
    
    
        this.hashMap = hashMap;
    }

    @Override
    public void run() {
    
    
        //将指定的 key映射到此 key value中指定的value。
        hashMap.put("dd", 1);

        //减少锁存器的计数,如果计数达到零,释放所有等待的线程
        countDownLatch.countDown();

    }
}

Insert image description here

Guess you like

Origin blog.csdn.net/weixin_74239923/article/details/132412974