Multi Threading学了怎么用?

Thread:

thread: a thread of execution in a program.
process: it refers to an executing application.

Java Thread is like a virtual CPU that can execute your Java code — inside your Java application. when a Java application is started its main method is executed by the main thread - a special thread that is created by the Java VM to run your application. it is not bind with cores number.

JVM: a process needs memory, it create heap, stack, metaspace

thread class:contains many native methods, direct talk to JVM/tightly couple to JVM

Why is Main Thread so important?
It is created automatically when your program is started.
this thread effects the other ‘child’ threads
it performs various shutdown actions

How to Create a Java Thread?

1.By implementing the Runnable interface.
2.By extending the Thread
源码: Runnable interface:

@FunctionalInterface
public interface Runnable {
	public abstract void run();
}

源码:Thread class:

class Thread implements Runnable {
	public Thread(Runnable target) {
        init(null, target, "Thread-" + nextThreadNum(), 0);
    }
    public synchronized void start() {...}
}

way1: Create thread in java using Runnable:
1.1create a class that implements Runnable interface.
1.2override run() method of Runnable interface. run(0 method introduces concurrent thread in to our program
1.3 new Thread, new a MyThread instance
put the instance into thread.
The execution of thread will end when it reaches end of the run method.

MyThread.java

class MyThread implements Runnable{
 	public void run(){
		 for (int i = 0; i < 5; i++) {
		      System.out.println(i);
		}
	}
 
	public static void main(String [] args){ 
	      MyThread myth= new MyThread();
	      Thread t = new Thread(myth);
	      t.start();
	}
}

way2: Java extends thread example
2.1create a class that extends Thread
2.2override run() method
2.3new an instance of MyThread class, instance.start();
The run() method is what is executed by the thread after you call start().

class MyThread extends Thread{
	 public void run() {
	      System.out.println("thread started running..");
	 }
 
	public static void main(String [] args){ 
	      MyThread myth= new MyThread();
	      //Thread t = new Thread(myth);
	      myth.start();
	}
}

两种方法的区别:

1.If we extend the Thread class, our class cannot extend any other class because Java doesn’t support multiple inheritance.
if we implement the Runnable interface, our class can still extend other base classes.

2.We can achieve basic functionality of a thread by extending Thread class because it provides some inbuilt methods like yield(), interrupt() etc. that are not available in Runnable interface.

3.Using Runnable will give you an object that can be shared amongst multiple threads.

在这里插入图片描述
https://blog.csdn.net/weixin_41896265/article/details/111247963

=====================

Java Thread Pool:

1.Java Thread pool
It represents a group of worker threads that are waiting for the job and reuse many times.
在这里插入图片描述

2.Advantage of Java Thread Pool 优点:

1.Pre populate / creating threads, better performance It saves time because there is no need to create new thread.
2.control the memory usage
第一:降低资源消耗。通过reuse重复利用已创建的线程降低线程创建和销毁造成的消耗。
第二:提高响应速度fast。当任务到达时,任务可以不需要等到线程创建就能立即执行。
第三:提高线程的可管理性manage。线程是稀缺资源,如果无限制的创建,不仅会消耗系统资源,还会降低系统的稳定性,使用线程池可以进行统一的分配,调优和监控。

3.Usage
It is used in Servlet and JSP where container creates a thread pool to process the request.

4.Creation of Java Thread Pool using Runnable & ExecutorService

step1. new WorkerThread implements Runnable interface
step2. new ThreadPool

ExecutorService executor = Executors.newFixedThreadPool(3);//creating a pool of 3 threads  

step3. assign 5 tasks to 3 workers/tasks
3.1 new workers,
3.2 calling execute method of ExecutorService

for (int i = 0; i < 5; i++) {  
	Runnable worker = new WorkerThread("" + i);   //new workers, 
	executor.execute(worker);//calling execute method of ExecutorService  
}  

WorkerThread.java

import java.util.concurrent.ExecutorService;  
import java.util.concurrent.Executors;  
class WorkerThread implements Runnable {  
    private String message;  
    public WorkerThread(String s){  
        this.message=s;  
    }  
     public void run() {  
        System.out.println(Thread.currentThread().getName()+" (Start) message = "+message);  
        processmessage();//call processmessage method that sleeps the thread for 2 seconds  
        System.out.println(Thread.currentThread().getName()+" (End)");//prints thread name  
    }  
    private void processmessage() {  
        try {  Thread.sleep(2000);  } catch (InterruptedException e) { e.printStackTrace(); }  
    }  
}  

TestThreadPool.java

public class TestThreadPool {  
     public static void main(String[] args) {  
        ExecutorService executor = Executors.newFixedThreadPool(3);//creating a pool of 5 threads  
        for (int i = 0; i < 5; i++) {  
            Runnable workerThread = new WorkerThread("" + i);  
            executor.execute(workerThread);//calling execute method of ExecutorService  
          }  
        executor.shutdown();  
        while (!executor.isTerminated()) {   }  
  
        System.out.println("Finished all threads");  
    }  
 }  

pool-1-thread-1 (Start) message = 0
pool-1-thread-2 (Start) message = 1
pool-1-thread-3 (Start) message = 2
pool-1-thread-2 (End)
pool-1-thread-2 (Start) message = 3
pool-1-thread-1 (End)
pool-1-thread-2 (End)
pool-1-thread-1 (Start) message = 4
pool-1-thread-3 (End)
pool-1-thread-1 (End)
Finished all threads

5.Creation of Java Thread Pool using Callable & ExecutorService
step1. new MyCallable implements Callable interface, return String
step2. new ThreadPool
step3. assign 100 tasks to 10 workers
3.1 new MyCallable-s,
3.2 calling submit() method of ExecutorService, submit MyCallable-s to work!!!

 		Callable<String> callable = new MyCallable();
        for(int i=0; i< 100; i++){
            //submit Callable tasks to be executed by thread pool
            Future<String> future = executor.submit(callable);
        }

Here is a simple example of Java Callable task that returns the name of thread executing the task after one second. We are using Executor framework to execute 100 tasks in parallel and use Java Future to get the result of the submitted tasks.

public class MyCallable implements Callable<String> {
    @Override
    public String call() throws Exception {
        Thread.sleep(1000);
        //return the thread name executing this callable task
        return Thread.currentThread().getName();
    }
    
    public static void main(String args[]){
        //Get ExecutorService from Executors utility class, thread pool size is 10
        ExecutorService executor = Executors.newFixedThreadPool(10);
        //create a list to hold the Future object associated with Callable
        List<Future<String>> list = new ArrayList<Future<String>>();
        //Create MyCallable instance
        Callable<String> callable = new MyCallable();
        for(int i=0; i< 100; i++){
            //submit Callable tasks to be executed by thread pool
            Future<String> future = executor.submit(callable);
            //add Future to the list, we can get return value using Future
            list.add(future);
        }
        for(Future<String> fut : list){
            try {
                //print the return value of Future, notice the output delay in console
                // because Future.get() waits for task to get completed
                System.out.println(new Date()+ "::"+fut.get());
            } catch (InterruptedException | ExecutionException e) {
                e.printStackTrace();
            }
        }
        //shut down the executor service now
        executor.shutdown();
    }
}

notice the delay in output because java Future get() method waits for the java callable task to complete.

reference:

Java Future
Java Callable tasks return java.util.concurrent.Future object. Using Java Future object, we can find out the status of the Callable task and get the returned Object. It provides get() method that can wait for the Callable to finish and then return the result.

6.几种类型的线程池
在这里插入图片描述

  1. ExecutorService newSingleThreadPool = Executors.newSingleThreadExecutor();
  2. ExecutorService fixedThreadPool = Executors.newFixedThreadPool(3);//nThreads
  3. ExecutorService cachedThreadPool = Executors.newCachedThreadPool();//无界线程池
  4. ExecutorService scheduledThreadPool = Executors.newScheduledThreadPool(1);//corePoolSize核心线程池固定,大小无限的线程池。
    ExecutorService newSingleThreadScheduledThreadPool = Executors.newSingleThreadScheduledExecutor();
public ThreadPoolExecutor​(int corePoolSize,
                          int maximumPoolSize,
                          long keepAliveTime,
                          TimeUnit unit,
                          BlockingQueue<Runnable> workQueue,
                          ThreadFactory threadFactory)
  public ScheduledThreadPoolExecutor(int corePoolSize) {
        super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS,
              new DelayedWorkQueue());
    }

reference:

  1. ExecutorService newSingleThreadPool = Executors.newSingleThreadExecutor();
public static void main(String[] args) {
        ExecutorService singleThreadPool= Executors.newSingleThreadExecutor();
        for(int i=1;i<=5;i++){
            int index=i;
            singleThreadPool.execute(new Runnable(){
                @Override
                public void run() {
                    try{
                        System.out.println("第"+index+"个任务"+Thread.currentThread().getName());
                        Thread.sleep(2000);
                    }catch(InterruptedException e) {
                        e.printStackTrace();
                    }
                }  });
        }
    }
 public static void main(String[] args) {
        ExecutorService singleThreadPool = Executors.newSingleThreadExecutor();
        for(int i=1;i<=5;i++){
            int index=i;
            singleThreadPool.execute(() -> {
                try{
                    System.out.println("第"+index+"个任务"+Thread.currentThread().getName());
                    Thread.sleep(2000);
                }catch(InterruptedException e) {
                    e.printStackTrace();
                }
            });
        }

第1个任务pool-1-thread-1
第2个任务pool-1-thread-1
第3个任务pool-1-thread-1
第4个任务pool-1-thread-1
第5个任务pool-1-thread-1

  1. ExecutorService fixedThreadPool = Executors.newFixedThreadPool(3);//nThreads

public class ThreadPoolExecutorTest {
    public static void main(String[] args) {
        ExecutorService  fixedThreadPool =Executors. newFixedThreadPool(3);
        for (int i =1; i<=5;i++){
            final int index=i ;
            fixedThreadPool.execute(new Runnable(){
                @Override
                public void run() {
                    try {
                        System.out.println("第" +index + "个任务:" +Thread.currentThread().getName());
                        Thread.sleep(2000);
                    }  catch(InterruptedException  e ) {
                        e .printStackTrace();
                    }
                }

            });
        }
    }
}

第2个任务:pool-1-thread-2
第3个任务:pool-1-thread-3
第1个任务:pool-1-thread-1 //2000 ms later…
第4个任务:pool-1-thread-2
第5个任务:pool-1-thread-3

由于设置最大线程数为3,所以在输出三个数后等待2秒后才继续输出。

  1. ExecutorService cachedThreadPool = Executors.newCachedThreadPool();//无界线程池
public class ThreadPoolExecutorTest {
    public static void main(String[]  args ) {
        ExecutorService cachedThreadPool = Executors.newCachedThreadPool();
        for(int i =1;i<=5;i++){
            final int index=i ;
            try{
                Thread.sleep(1000);
            }catch(InterruptedException  e ) {
                e.printStackTrace();
            }
            cachedThreadPool.execute(() -> System.out.println("第" +index +"个任务:" +Thread.currentThread().getName()));
        }
    }
}

第1个任务:pool-1-thread-1
第2个任务:pool-1-thread-1
第3个任务:pool-1-thread-1
第4个任务:pool-1-thread-1
第5个任务:pool-1-thread-1

当执行第二个任务时第一个任务已经完成,会复用执行第一个任务的线程,而不用每次新建线程。
??这个例子的结果,看起来和newSingleThreadPool没有差别?

  1. ExecutorService scheduledThreadPool = Executors.newScheduledThreadPool(1);//corePoolSize核心线程池固定,大小无限的线程池。
    ExecutorService newSingleThreadScheduledThreadPool = Executors.newSingleThreadScheduledExecutor();

4.1 延迟执行
new一个线程池,返回的是ScheduledExecutorService;

ScheduledExecutorService scheduledThreadPool= Executors.newScheduledThreadPool(3);

方法用的是:

scheduledThreadPool.schedule(new Runnable(){ }

schedule

public static void main(String[] args) {
        ScheduledExecutorService scheduledThreadPool= Executors.newScheduledThreadPool(3);
        scheduledThreadPool.schedule(new Runnable(){
            @Override
            public void run() {
                System.out.println("延迟三秒");
            }
        }, 3, TimeUnit.SECONDS);
    }
public static void main(String[] args) {
        ScheduledExecutorService scheduledThreadPool= Executors.newScheduledThreadPool(3);
        scheduledThreadPool.schedule(() -> System.out.println("延迟三秒"), 3, TimeUnit.SECONDS);
    }

延迟三秒

4.2 定期执行

.scheduleAtFixedRate()

    ScheduledExecutorService scheduledThreadPool= Executors.newScheduledThreadPool(3);
        scheduledThreadPool.scheduleAtFixedRate(new Runnable(){
            @Override
            public void run() {
                System.out.println("延迟1秒后每三秒执行一次");
            }
        },1,3, TimeUnit.SECONDS);
    }

initialDelay:1 s
period:3 s

public static void main(String[] args) {
    ScheduledExecutorService scheduledThreadPool= Executors.newScheduledThreadPool(3);
        scheduledThreadPool.scheduleAtFixedRate(() -> System.out.println("延迟1秒后每三秒执行一次"),1,3, TimeUnit.SECONDS);
    }
}

延迟1秒后每三秒执行一次
延迟1秒后每三秒执行一次
延迟1秒后每三秒执行一次 …

7. 线程池的工作过程

线程池刚创建时,里面没有一个线程。任务队列是作为参数传进来的。

public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue());
}
public static ExecutorService newSingleThreadExecutor() {
return new FinalizableDelegatedExecutorService
(new ThreadPoolExecutor(1, 1,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue()));
}
public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue());
}

不过,就算队列里面有任务,线程池也不会马上执行它们。当提交一个新任务到线程池时,线程池会做如下判断:

如果正在运行的线程数量小于 corePoolSize,那么马上创建线程运行这个任务;

如果正在运行的线程数量大于或等于 corePoolSize,那么将这个任务放入队列

如果这时候队列满了,而且正在运行的线程数量小于 maximumPoolSize,那么还是要创建非核心线程立刻运行这个任务;

如果队列满了,而且正在运行的线程数量大于或等于 maximumPoolSize,那么会采取任务拒绝策略,如下:

当一个线程完成任务时,它会从队列中取下一个任务来执行。

当一个线程无事可做,超过一定的时间(keepAliveTime)时,线程池会判断,如果当前运行的线程数大于corePoolSize,那么这个线程就被停掉。所以线程池的所有任务完成后,它最终会收缩到corePoolSize 的大小。

8. 线程池的关闭

ThreadPoolExecutor提供了两个方法,用于线程池的关闭,分别是shutdown()和shutdownNow(),其中:shutdown():不会立即终止线程池,而是要等所有任务缓存队列中的任务都执行完后才终止,但再也不会接受新的任务。
shutdownNow():立即终止线程池,并尝试打断正在执行的任务,并且清空任务缓存队列,返回尚未执行的任务。
reference:大神博客

9.Risks in using Thread Pools

1.Deadlock : While deadlock can occur in any multi-threaded program, thread pools introduce another case of deadlock, one in which all the executing threads are waiting for the results from the blocked threads waiting in the queue due to the unavailability of threads for execution.
2.Thread Leakage: Thread Leakage occurs if a thread is removed from the pool to execute a task but not returned to it when the task completed. As an example, if the thread throws an exception and pool class does not catch this exception, then the thread will simply exit, reducing the size of the thread pool by one. If this repeats many times, then the pool would eventually become empty and no threads would be available to execute other requests.
3.Resource Thrashing :If the thread pool size is very large then time is wasted in context switching between threads. Having more threads than the optimal number may cause starvation problem leading to resource thrashing as explained.

10. Deadlock
Deadlock in java is a part of multithreading. Deadlock can occur in a situation when a thread is waiting for an object lock, that is acquired by another thread and second thread is waiting for an object lock that is acquired by first thread. Since, both threads are waiting for each other to release the lock, the condition is called deadlock.

public class DeadLock {
    public static void main(String[] args) {
        final String resource1 = "ratan jaiswal";
        final String resource2 = "vimal jaiswal";
        // t1 tries to lock resource1 then resource2  
        Thread t1 = new Thread() {
            public void run() {
                synchronized (resource1) {
                    System.out.println("Thread 1: locked resource 1");

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

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

        // t2 tries to lock resource2 then resource1  
        Thread t2 = new Thread() {
            public void run() {
                synchronized (resource2) {
                    System.out.println("Thread 2: locked resource 2");

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

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

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

lambda简化一下:

public class DeadLock {
    public static void main(String[] args) {
        final String resource1 = "ratan jaiswal";
        final String resource2 = "vimal jaiswal";
        // t1 tries to lock resource1 then resource2
        Thread t1 = new Thread(() -> {
            synchronized (resource1) {
                System.out.println("Thread 1: locked resource 1");

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

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

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

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

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


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

Thread 1: locked resource 1
Thread 2: locked resource 2

synchronized
A synchronized block in Java is synchronized on some object. All synchronized blocks synchronized on the same object can only have one thread executing inside them at a time. All other threads attempting to enter the synchronized block are blocked until the thread inside the synchronized block exits the block.
Thread的几个重要方法:

a、**start()**方法,调用该方法开始执行该线程;
b、**stop()**方法,调用该方法强制结束该线程执行;
c、**join()**方法,调用该方法等待该线程结束。
d、**sleep()**方法,调用该方法该线程进入等待。
e、**run()**方法,调用该方法直接执行线程的run()方法,但是线程调用start()方法时也会运行run()方法,区别就是一个是由线程调度运行run()方法,一个是直接调用了线程中的run()方法!!

看到这里,可能有些人就会问啦,那wait()和notify()呢?
注意,其实wait()与notify()方法是Object的方法,不是Thread的方法!!
同时,wait()与notify()会配合使用,分别表示线程挂起和线程恢复。

这里还有一个很常见的问题,wait()与sleep()的区别,简单来说wait()会释放对象锁
sleep() 不会释放对象锁。 reference

锁的是谁?

// Only one thread can execute at a time.
// sync_object is a reference to an object
// whose lock associates with the monitor.
// The code is said to be synchronized on
// the monitor object
synchronized(sync_object) {
// Access shared variables and other
// shared resources
}

1.synchronize the Sender object inside the run() method of the ThreadedSend class.

class Sender {
    public void send(String msg) {
        System.out.println("Sending\t"  + msg );
        try {
            Thread.sleep(1000);
        }
        catch (Exception e) {
            System.out.println("Thread  interrupted.");
        }
        System.out.println("\n" + msg + "Sent");
    }
}
// Class for send a message using Threads 
class ThreadedSend extends Thread {
    private String msg;
    Sender  sender;
    // Recieves a message object and a string 
    // message to be sent 
    ThreadedSend(String m,  Sender obj) {
        msg = m;
        sender = obj;
    }

    public void run() {
        // Only one thread can send a message 
        // at a time. 
        synchronized(sender) {// synchronizing the snd object
            sender.send(msg);
        }
    }
}
// Driver class 
public class SyncDemo {
    public static void main(String args[]) {
        Sender snd = new Sender();
        ThreadedSend S1 = new ThreadedSend( " Hi " , snd );
        ThreadedSend S2 = new ThreadedSend( " Bye " , snd );
        // Start two threads of ThreadedSend type 
        S1.start();
        S2.start();
        // wait for threads to end 
        try {
            S1.join();
            S2.join();
        }
        catch(Exception e) {
            System.out.println("Interrupted");
        }
    }
} 

Output:
Sending Hi
Hi Sent
Sending Bye
Bye Sent

s1.join();
s1调用了join()方法,那么就必须等待s1执行完毕之后再执行s2,
s2调用了join()方法,那么就必须等待s2执行完毕之后再执行main线程
这个过程有点像单线程。

2.define the whole send() block as synchronized
it would produce the same result.
Then we don’t have to synchronize the Message object inside the run() method in ThreadedSend class.

class Sender {
    //synchronized with method 
    public synchronized void send(String msg) {
        System.out.println("Sending\t"  + msg );
        try {
            Thread.sleep(1000);
        }
        catch (Exception e) {
            System.out.println("Thread  interrupted.");
        }
        System.out.println("\n" + msg + "Sent");
    }
}

3.synchronize only part of a method. Java synchronized blocks inside methods makes this possible.

class Sender {
    public void send(String msg) {
        synchronized(this) {  //synchronized part method
            System.out.println("Sending\t" + msg);
            try {
                Thread.sleep(1000);
            } catch (Exception e) {
                System.out.println("Thread  interrupted.");
            }
            System.out.println("\n" + msg + "Sent");
        }
    }
}

synchronized (this)

  • We get a lock associated with the object pointed to “this”. When we use this block, we mean that we are willing to wait until the thread using this monitor, releases it.
    This makes sense to use the lock, if you change the data object (the object’s variables)??
    1.对当前类的Class对象加锁
    1.1对synchronized括号内的Class对象加锁
    1.2 修饰静态方法

2.对当前实例对象加锁
2.1修饰实例方法,对当前实例对象加锁
2.2修饰代码块,对synchronized括号内的对象加锁1
2.2修饰代码块,对synchronized括号内的对象加锁2
reference:

1.对当前类的Class对象加锁
1.1对synchronized括号内的Class对象加锁

public class SafeThread {
    public void testPrint(){
        //1 syn class instance 对该类所有实例对象枷锁
        // 1.1.SafeThread.class--> syn class instance
        synchronized(SafeThread.class){
            System.out.println("Enter testPrint method !");
            try {
                Thread.sleep(2000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("Exit testPrint method !");
        }
    }
}

1.2修饰静态方法

public class SafeThread {
//1 syn class instance 对该类所有实例对象枷锁
    // 1.2.static --> syn class instance
    public static synchronized  void testPrint2(){
        System.out.println("Enter testPrint method !");
        try {
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("Exit testPrint method !");
    }
}

调用线程

 //1.创建当前对象实例,并使用对象实例初始化线程。
    public class ThreadRead1 extends Thread{
        SafeThread safe = null;
        //constructor 1
        public ThreadRead1(){
        }
        //constructor 2
        public ThreadRead1(SafeThread o){
            safe = o;
        }
        public void run() {
            safe.testPrint();
        }
    }

    public class ThreadStart {
        public static void main(String[] para){
            SafeThread safe = new SafeThread();
            for(int i=0;i<3;i++){
                ThreadRead1 t1 = new ThreadRead1(safe);//call constructor to new 当前对象实例
                t1.start(); //call constructor to new 当前对象实例 to start();
            }
        }
    }

    //2.在线程中创建类实例
    public class ThreadRead1 extends Thread{
        SafeThread safe = null;
        public ThreadRead1(){
        }
        public void run() {
            safe = new SafeThread();//创建类实例
            safe.testPrint();//call method
        }
    }

    public class ThreadStart {
        public static void main(String[] para){
            for(int i=0;i<3;i++){
                ThreadRead1 t1 = new ThreadRead1();
                t1.start();//use class instance to start()
                            // call run(), so call testPrint() method
            }
        }
    }


2.对当前实例对象加锁
2.1修饰实例方法,对当前实例对象加锁

public class SafeThread {
//2以类的当前实例对象作为锁对象
    // 2.1 给方法添加synchronized
    // 创建内部同步代码块,以 [类的当前实例对象] 作为锁的对象。

    public synchronized void testPrintCurInstance(){
        System.out.println("Enter testPrint method !");
        try {
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("Exit testPrint method !");
    }
}

2.2修饰代码块,对synchronized括号内的对象加锁1

public class SafeThread {
//2 以类的当前实例对象作为锁对象
    // 2.2 给代码块添加synchronized
    public void testPrintCurInstance1() {
        synchronized (this) {
            System.out.println("Enter testPrint method !");
            try {
                Thread.sleep(2000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("Exit testPrint method !");
        }
    }
}

2.2修饰代码块,对synchronized括号内的对象加锁2

public class SafeThread {
//2 以类的当前实例对象作为锁对象
    // 2.3 给代码块添加synchronized
    Object a = new Object();
    public void testPrintCurInstance2(){
        synchronized(a){
            System.out.println("Enter testPrint method !");
            try {
                Thread.sleep(2000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("Exit testPrint method !");
        }
    }
}

上面这三种写法都是以类的当前实例对象作为锁对象,所以线程调用写法如下:

    public class ThreadRead2 extends Thread{
        SafeThread safe = null;
        public ThreadRead2(){
        }
        public ThreadRead2(SafeThread o){
            safe = o;
        }
        public void run() {
            safe.testPrint();
        }
    }
public class ThreadStart {
    public static void main(String[] args) {
        SafeThread s=new SafeThread();
        for (int i = 0; i < 3; i++) {
            ThreadRead2 t1=new ThreadRead2(s);
            //System.out.println(Thread.currentThread().getName());
            t1.start();
        }
    }
}

synchronized是对类的当前实例进行加锁,防止其他线程同时访问该类的当前实例的所有synchronized块。
static synchronized是控制类的所有实例的访问, 限制线程同时访问jvm中该类的所有实例对应的代码快。

在类中某方法或某代码块中有 synchronized,那么在生成一个该类实例后,该类也就有一个监视块,放置线程并发访问该实例synchronized保护块,这个保护块只对当前实例有效

而static synchronized则是所有该类的实例公用一个监视块,放置线程并发访问该类所有实例的保护块,synchronized相当于 this.synchronized。

Quesion:Synchronized block and Synchronized method diff?
A synchronized method provides a lock corresponding to object-level or Class level (i.e class level means static method), whereas, synchronized block provides a lock on any object depending on the parameter.

join()
多线程中的join()方法
先看一段代码,代码的输出顺序是什么?

public static void main(String[] args) throws Exception {
        Thread t1 = new Thread(() -> {
            try {
                Thread.sleep(3000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("线程A执行");
        });

        Thread t2 = new Thread(() -> {
            try {
                Thread.sleep(2000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("线程B执行");
        });

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

        System.out.println("我是主线程");
    }

我是主线程
线程B执行
线程A执行

稍微改一下代码:

public static void main(String[] args) throws Exception {
        Thread t1 = new Thread(() -> {
            try {
                Thread.sleep(3000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("线程A执行");
        });

        Thread t2 = new Thread(() -> {
            try {
                Thread.sleep(2000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("线程B执行");
        });

        t1.start();
        t1.join();//
        t2.start();

        System.out.println("我是主线程");
    }

线程A执行
我是主线程
线程B执行

上面那段加了join()的代码,首先开启线程A,紧接着线程A调用了join()方法进入阻塞状态,那么线程必须等待线程A执行结束之后再往下执行,线程A执行完毕,线程B开启,进入睡眠,主线程执行,线程B睡眠结束,执行;

join()
简单说,谁调用这个方法,就让调用此方法的线程进入阻塞状态,等待我执行完毕之后,再往下执行;

并发编程中的三个问题:

1.可见性visibility

一个线程对共享变量进行修改,另一个线程马上得到修改后的数据
/*
1.1创建一个共享变量
1.2创建一条线程不断读取共享变量
1.3创建一条线程修改共享变量
*/

public class Visibility {
        //1.创建一个共享变量
    private static boolean flag=true;
    public static void main(String[] args) throws InterruptedException {
        //2.创建一条线程不断读取共享变量
        new Thread(()->{
            while(flag){ //下面线程的修改,上面线程没有立即得到结果
                //System.out.println("the  thread is still run");
            }
        }).start();
        Thread.sleep(2000);//为什么加入了这句,线程就无法停止?
        //3.创建一条线程修改共享变量
        new Thread(()->{
            flag=false;
                //System.out.println();
            System.out.println("线程修改了变量的值为false");
        }).start();
    }
}

两秒之后第二个线程修改了变量值,
第一个线程并没有得到修改后的结果。
这就是可见性问题。
小结:并发情况下,一个线程对共享变量的修改,另外一个线程没有立即得到结果。

2.原子性
要么所有操作都执行,要么所有的操作都不执行

/*
1。定义一个共享变量number
2。对number进行1000对++操作
3。使用5个线程来进行
*/
public class Atomicity {
    //1.定义一个变量number
    private static int number=0;
    public static void main(String[] args) throws InterruptedException {
        Runnable increment=new Runnable() {
            @Override
            public void run() {
                for (int i = 0; i < 1000; i++) {
                    number++;
                }
            }
        };
 
        for (int i = 0; i < 5; i++) {
            Thread t=new Thread(increment); 
            t.start();
        }
        
        System.out.println("number"+number);
    }
}
//number4792
//number5000

上面代码,可能5个for循环没跑完就。
为了让5个线程跑完,使用join方法。依然会出现原子性问题。

 
public class Atomicity {
    //1.定义一个变量number
    private static int number=0;
    public static void main(String[] args) throws InterruptedException {
        Runnable increment=new Runnable() {
            @Override
            public void run() {
                for (int i = 0; i < 1000; i++) {
                    number++;
                }
            }
        };
        List<Thread> list=new ArrayList<>();
        for (int i = 0; i < 5; i++) {
            Thread t=new Thread(increment);
            t.start();
            list.add(t);
        }
        for(Thread t:list){
            t.join();
        }
        System.out.println("number"+number);
    }
}
//number4792
//number5000

小结:并发编程时,会出现原子性问题。
//number++;反汇编,javap -v-p

分了4条字节码bytecode,get读取number的值,读取常量1,add++,put写number的值回去
//当一个线程对变量操作到一半的时候,另外的线程也有可能操作共享变量number
//干扰了前一个线程的操作

3.有序性

是指程序中代码的执行顺序。java在编译和运行时会对代码进行优化。会导致程序最终的执行顺序不一定是编写的顺序。
jcstress工具测试多种结果的频率
比如
结果0:223次
结果1:24次
结果4:245次

public class Ordering {
    int num=0;
    boolean ready=false;
    //@Actor
    public void actor1(I_Result r){
        if(ready){
            r.r1=num+num;
        }else {
            r.r1 = 1;
        }
    }

    //@Actor
    public void actor2(I_Result r){
        num=2;
        ready=true; //这两句会被重新排序,导致ready先被修改成true,然后actor1先运行num+num
    }
}

下面这两句会被重新排序,导致ready先被修改成true,
然后传给actor1运行,num+num=0;

public void actor2(I_Result r){
	num=2;
 	ready=true;  
}

主内存和工作内存的数据交互

synchronized能够解决三大特性问题

1.保证原子性
synchronized保证只有一个线程能拿到锁,能够进入同步代码块。

public class Atomicity {
    //1.定义一个变量number
    private static int number=0;
    private static Object obj=new Object();//++
    public static void main(String[] args) throws InterruptedException {
        Runnable r=new Runnable() {
            @Override
            public void run() {
                for (int i = 0; i < 1000; i++) {
                	synchronized(obj){//++
                    	number++;
                    }
                }
            }
        };
        List<Thread> list=new ArrayList<>();
        for (int i = 0; i < 5; i++) {
            Thread t=new Thread(r);
            t.start();
            list.add(t);
        }
        for(Thread t:list){
            t.join();
        }
        System.out.println("number"+number);
    }
}

2.保证可见性
// 可见性问题,是由于第二个线程修改了主内存中的值,
// 第一个线程仍然在运行自己工作内存中的值
解决方法:
2.1.volatile 关键字,让第一个线程【立刻】读到主内存中修改后的值。

private static volatile boolean flag=true; 

2.2.循环中加一synchronized
lock原子操作,让第一个线程【刷新】主内存,读修改后的值。

while(flag){  
	synchronized (obj){ }
}

2.3. 直接打印
因为there is a synchronized (this) in println方法

while(flag){ 
	System.out.println(flag);
}
    所有代码如下:
public class Visibility {
    // 001 volatile 关键字,让第一个线程【立刻】读到主内存中修改后的值。
    //private static volatile boolean flag=true; //001++
    private static boolean flag=true;
    //private static Object obj=new Object();//002+++
    public static void main(String[] args) throws InterruptedException {

        new Thread(()->{
            while(flag){ //下面线程的修改,上面线程没有立即得到结果
                //System.out.println("the  thread is still run");
                synchronized (obj){//002++
                }
                //002 循环中加一synchronized
                //lock原子操作,让第一个线程【刷新】主内存,读修改后的值。
                
                //System.out.println(flag);//003++
                //003 there is a synchronized (this)  in println
            }
        }).start();
        Thread.sleep(2000);//为什么加入了这句,线程就无法停止?
        //3.
        new Thread(()->{
            flag=false;
                //System.out.println();
            System.out.println("线程修改了变量的值为false");
        }).start();
    }
}

3.保证有序性
重新排序后,提高了执行效率,编译器和CPU对程序代码进行了重排序
重排序只保证了单线程情况下执行结果正确
以下依赖关系不能重排序
3.1
3.1.1写后读:

int a=1;
int b=a;
这两句顺序不能颠倒

3.1.2写后写

int a=1;
int a=2;
这两句顺序不能颠倒

3.1.3读后写

int a=1;
int b=a;
int a=2;
后2句顺序不能颠倒,颠倒后b的值就变化了。

什么时候可以重新排序?

int a=1;
int b=2;
int c=a+b;

synchronized如何保证有序性?

public class Ordering {
    private Object obj=new Object();
    int num=0;
    boolean ready=false;
    //@Actor
    public void actor1(I_Result r){
        synchronized (obj) {
            if (ready) {
                r.r1 = num + num;
            } else {
                r.r1 = 1;
            }
        }
    }

    //@Actor
    public void actor2(I_Result r){
        synchronized (obj) {
            num = 2;
            ready = true; //这两句仍然会被重新排序,但拿到锁后,必须两句都执行结束后,
            //ready= true;  再执行actor1中的num + num;
        }
    }
}

num = 2;
ready = true;
这两句仍然会被重新排序,变成下面:
ready= true;
num = 2;

但拿到锁后,必须两句都执行结束后,
所以当ready= true时, num=2;
再执行actor1中的num + num=4

小结:加了synchronized后,依然会发生重排序,只不过有了同步代码块,可以
只有一个线程执行同步代码块,保证了有序性。

synchronized可重入性
一个线程可多次执行synchronized,多次获取同一把锁。

在这里插入代码片

synchronized不可中断性

在这里插入代码片

Lock

如果一个代码块被synchronized修饰了,当一个线程获取了对应的锁,并执行该代码块时,其他线程便只能一直等待,等待获取锁的线程释放锁,而这里获取锁的线程释放锁只会有两种情况:
  1)获取锁的线程执行完了该代码块,然后线程释放对锁的占有;
  2)线程执行发生异常,此时JVM会让线程自动释放锁。

那么如果这个获取锁的线程由于要等待IO或者其他原因(比如调用sleep方法)被阻塞了,但是又没有释放锁,其他线程便只能干巴巴地等待,试想一下,这多么影响程序执行效率。
  因此就需要有一种机制可以不让等待的线程一直无期限地等待下去(比如只等待一定的时间或者能够响应中断),通过Lock就可以办到。
  注意以下几点:
  1)Lock不是Java语言内置的,synchronized是Java语言的关键字,因此是内置特性。Lock是一个类,通过这个类可以实现同步访问;
  2)Lock和synchronized有一点非常大的不同,采用synchronized不需要用户去手动释放锁,当synchronized方法或者synchronized代码块执行完之后,系统会自动让线程释放对锁的占用;而Lock则必须要用户去手动释放锁,如果没有主动释放锁,就有可能导致出现死锁现象。

synchronized与Lock的区别

性质:Lock是一个类,finally()中必须释放,等待过程可中断,状态可判断,
可公平??大量同步??
在这里插入图片描述
总结来说,Lock和synchronized有以下几点不同:
  1)Lock是一个接口,而synchronized是Java中的关键字,synchronized是内置的语言实现;
  2)synchronized在发生异常时,会自动释放线程占有的锁,因此不会导致死锁现象发生;而Lock在发生异常时,如果没有主动通过unLock()去释放锁,则很可能造成死锁现象,因此使用Lock时需要在finally块中释放锁;
  3)Lock可以让等待锁的线程响应中断,而synchronized却不行,使用synchronized时,等待的线程会一直等待下去,不能够响应中断;
  4)通过Lock可以知道有没有成功获取锁,而synchronized却无法办到。
  5)Lock可以提高多个线程进行读操作的效率。

在性能上来说,如果竞争资源不激烈,两者的性能是差不多的,而当竞争资源非常激烈时(即有大量线程同时竞争),此时Lock的性能要远远优于synchronized。
链接:https://www.jianshu.com/p/b1581c35c881

public class LockTest {
	private Lock lock = new ReentrantLock();

	//需要参与同步的方法
	private void method(Thread thread){
		lock.lock();
		try {
			System.out.println("线程名"+thread.getName() + "获得了锁");
		}catch(Exception e){
			e.printStackTrace();
		} finally {
			System.out.println("线程名"+thread.getName() + "释放了锁");
			lock.unlock();
		}
	}
	
	public static void main(String[] args) {
		LockTest lockTest = new LockTest();
		
		//线程1
		Thread t1 = new Thread(new Runnable() {
			
			@Override
			public void run() {
				lockTest.method(Thread.currentThread());
			}
		}, "t1");
		
		Thread t2 = new Thread(new Runnable() {
			
			@Override
			public void run() {
				lockTest.method(Thread.currentThread());
			}
		}, "t2");
		
		t1.start();
		t2.start();
	}
}
//执行情况:线程名t1获得了锁
//         线程名t1释放了锁
//         线程名t2获得了锁
//         线程名t2释放了锁 

锁类型

可重入锁:在执行对象中所有同步方法不用再次获得锁
可中断锁:在等待获取锁过程中可中断
公平锁: 按等待获取锁的线程的等待时间进行获取,等待时间长的具有优先获取锁权利
读写锁:对资源读取和写入的时候拆分为2部分处理,读的时候可以多线程一起读,写的时候必须同步地写

ReentrantLock

Synchronized blocks don’t offer any mechanism of a waiting queue and after the exit of one thread, any thread can take the lock. This could lead to starvation of resources for some other thread for a very long period of time.

ReentrantLock allows threads to enter into the lock on a resource more than once. When the thread first enters into the lock, a hold count is set to one. Before unlocking the thread can re-enter into lock again and every time hold count is incremented by one. For every unlocks request, hold count is decremented by one and when hold count is 0, the resource is unlocked.
进入锁,count+1,出锁count-1;

Reentrant Locks also offer a fairness parameter, by which the lock would abide by the order of the lock request 先来先得:公平
i.e. after a thread unlocks the resource, the lock would go to the thread which has been waiting for the longest time.

package lock;

import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.locks.ReentrantLock;

class worker implements Runnable {
    String name;
    ReentrantLock re;
    public worker(ReentrantLock rl, String n) {
        re = rl;
        name = n;
    }
    public void run() {
        boolean done = false;
        while (!done) {
            //Getting Outer Lock
            boolean ans = re.tryLock();

            // Returns True if lock is free
            if(ans) {
                try {
                    Date d = new Date();
                    SimpleDateFormat ft = new SimpleDateFormat("hh:mm:ss");
                    System.out.println("task name - "+ name
                            + " outer lock acquired at "
                            + ft.format(d)
                            + " Doing outer work");
                    Thread.sleep(1500);

                    // Getting Inner Lock
                    re.lock();
                    try {
                        d = new Date();
                        ft = new SimpleDateFormat("hh:mm:ss");
                        System.out.println("task name - "+ name
                                + " inner lock acquired at "
                                + ft.format(d)
                                + " Doing inner work");
                        System.out.println("Lock Hold Count - "+ re.getHoldCount());
                        Thread.sleep(1500);
                    }
                    catch(InterruptedException e) {
                        e.printStackTrace();
                    }
                    finally {
                        //Inner lock release
                        System.out.println("task name - " + name +
                                " releasing inner lock");

                        re.unlock();
                    }
                    System.out.println("Lock Hold Count - " + re.getHoldCount());
                    System.out.println("task name - " + name + " work done");

                    done = true;
                } catch(InterruptedException e) {
                    e.printStackTrace();
                } finally {
                    //Outer lock release
                    System.out.println("task name - " + name +
                            " releasing outer lock");

                    re.unlock();
                    System.out.println("Lock Hold Count - " +
                            re.getHoldCount());
                }
            } else {
                System.out.println("task name - " + name +
                        " waiting for lock");
                try {
                    Thread.sleep(1000);
                } catch(InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

public class Test {
    static final int MAX_T = 2;
    public static void main(String[] args) {
        ReentrantLock rel = new ReentrantLock();
        ExecutorService pool = Executors.newFixedThreadPool(MAX_T);
        Runnable w1 = new worker(rel, "Job1");
        Runnable w2 = new worker(rel, "Job2");
        Runnable w3 = new worker(rel, "Job3");
        Runnable w4 = new worker(rel, "Job4");
        pool.execute(w1);
        pool.execute(w2);
        pool.execute(w3);
        pool.execute(w4);
        pool.shutdown();
    }
}

task name - Job2 waiting for lock
task name - Job1 outer lock acquired at 11:22:40 Doing outer work
task name - Job2 waiting for lock
task name - Job1 inner lock acquired at 11:22:42 Doing inner work
Lock Hold Count - 2
task name - Job2 waiting for lock
task name - Job2 waiting for lock
task name - Job1 releasing inner lock
Lock Hold Count - 1
task name - Job1 work done
task name - Job1 releasing outer lock
Lock Hold Count - 0
task name - Job3 outer lock acquired at 11:22:43 Doing outer work
task name - Job2 waiting for lock
task name - Job3 inner lock acquired at 11:22:45 Doing inner work
Lock Hold Count - 2
task name - Job2 waiting for lock
task name - Job2 waiting for lock
task name - Job3 releasing inner lock
Lock Hold Count - 1
task name - Job3 work done
task name - Job3 releasing outer lock
Lock Hold Count - 0
task name - Job4 outer lock acquired at 11:22:46 Doing outer work
task name - Job2 waiting for lock
task name - Job4 inner lock acquired at 11:22:48 Doing inner work
Lock Hold Count - 2
task name - Job2 waiting for lock
task name - Job2 waiting for lock
task name - Job4 releasing inner lock
Lock Hold Count - 1
task name - Job4 work done
task name - Job4 releasing outer lock
Lock Hold Count - 0
task name - Job2 outer lock acquired at 11:22:50 Doing outer work
task name - Job2 inner lock acquired at 11:22:52 Doing inner work
Lock Hold Count - 2
task name - Job2 releasing inner lock
Lock Hold Count - 1
task name - Job2 work done
task name - Job2 releasing outer lock
Lock Hold Count - 0

Process finished with exit code 0

ReentrantReadWriteLock

package lock;
import java.util.concurrent.locks.ReentrantReadWriteLock;

public class ReentrantReadWriteLockExample {
    private static final ReentrantReadWriteLock lock = new ReentrantReadWriteLock(true);

    private static String number = "0";

    public static void main(String[] args) throws InterruptedException{
        Thread t1 = new Thread(new Reader(),"Read 1");
        Thread t2 = new Thread(new Reader(),"Read 2");
        Thread t3 = new Thread(new WriterOdd(),"Write odd");
        Thread t4 = new Thread(new WriterEven(),"Write even");
        t1.start();
        t2.start();
        t3.start();
        t4.start();
        t1.join();
        t2.join();
        t3.join();
        t4.join();
    }

    static class Reader implements Runnable {
        public void run() {
            for(int i = 0; i<= 10; i ++) {
                lock.readLock().lock();
                System.out.println(Thread.currentThread().getName() + " ---> Number is " + number );
                lock.readLock().unlock();
            }
        }
    }

    static class WriterOdd implements Runnable {
        public void run() {
            for(int i = 1; i<= 7; i+=2) {
                try {
                    lock.writeLock().lock();
                    System.out.println("Writer odd is writing");
                    number = number.concat(" "+i);
                } finally {
                    lock.writeLock().unlock();
                }
            }
        }
    }

    static class WriterEven implements Runnable {
        public void run() {
            for(int i = 2; i<= 8; i +=2) {
                try {
                    lock.writeLock().lock();
                    System.out.println("Writer Even is writing");
                    number = number.concat(" "+i);
                } finally {
                    lock.writeLock().unlock();
                }
            }
        }
    }
}

Read 1 —> Number is 0
Read 2 —> Number is 0
Read 2 —> Number is 0
Read 2 —> Number is 0
Read 2 —> Number is 0
Read 2 —> Number is 0
Read 1 —> Number is 0
Writer odd is writing
Read 2 —> Number is 0 1
Writer Even is writing
Read 1 —> Number is 0 1 2
Writer odd is writing
Read 2 —> Number is 0 1 2 3
Writer Even is writing
Read 1 —> Number is 0 1 2 3 4
Writer odd is writing
Read 2 —> Number is 0 1 2 3 4 5
Writer Even is writing
Read 1 —> Number is 0 1 2 3 4 5 6
Writer odd is writing
Read 2 —> Number is 0 1 2 3 4 5 6 7
Writer Even is writing
Read 1 —> Number is 0 1 2 3 4 5 6 7 8
Read 2 —> Number is 0 1 2 3 4 5 6 7 8
Read 1 —> Number is 0 1 2 3 4 5 6 7 8
Read 2 —> Number is 0 1 2 3 4 5 6 7 8
Read 1 —> Number is 0 1 2 3 4 5 6 7 8
Read 1 —> Number is 0 1 2 3 4 5 6 7 8
Read 1 —> Number is 0 1 2 3 4 5 6 7 8
Read 1 —> Number is 0 1 2 3 4 5 6 7 8

Process finished with exit code 0
链接:https://www.jianshu.com/p/b1581c35c881
???
悲观锁
乐观锁

没有做好并发控制,就可能导致脏读、幻读和不可重复读等问题。

悲观锁
Pessimistic Concurrency Control,缩写“PCC”,又名“悲观锁”】。
悲观锁,具有强烈的独占排他特性。它指的是对数据被外界修改持保守态度(包括本系统当前的其他事务,以及来自外部系统的事务处理)。
因此,在整个数据处理过程中,将数据处于锁定状态。悲观锁的实现,往往依靠数据库提供的锁机制(也只有数据库层提供的锁机制才能真正保证数据访问的排他性,否则,即使在本系统中实现了加锁机制,也无法保证外部系统不会修改数据)。

悲观锁的流程如下:

1.在对记录进行修改前,先尝试为该记录加上排他锁(exclusive locks)。
2.如果加锁失败,说明该记录正在被修改,那么当前查询可能要等待或者抛出异常。具体响应方式由开发者根据实际需要决定。
3.如果成功加锁,那么就可以对记录做修改,事务完成后就会解锁了。
4.期间如果有其他对该记录做修改或加排他锁的操作,都会等待解锁或直接抛出异常。

//0.开始事务
begin;
//1.查询商品库存信息
select quantity from items where id=1 for update;
//2.修改商品库存为2
update items set quantity=2 where id=1;
//3.提交事务
commit;

在对id = 1的记录修改前,先通过 for update 的方式进行加锁,然后再进行修改。这就是比较典型的悲观锁策略。
如果以上修改库存的代码发生并发,同一时间只有一个线程可以开启事务并获得id=1的锁,其它的事务必须等本次事务提交之后才能执行。这样可以保证当前的数据不会被其它事务修改。

乐观锁
乐观锁假设数据一般情况下不会造成冲突,所以在数据进行提交更新的时候,才会正式对数据的冲突与否进行检测,如果发现冲突了,则返回给用户错误的信息,让用户决定如何去做。乐观锁适用于读操作多的场景,这样可以提高程序的吞吐量。
(乐观锁心比较大,宽容度比较高,所以说对外界的修改需求,抱有乐观的态度。)

乐观锁的实现:

CAS 实现:Java 中java.util.concurrent.atomic包下面的原子变量使用了乐观锁的一种 CAS 实现方式。
版本号控制:一般是在数据表中加上一个数据版本号 version 字段,表示数据被修改的次数。当数据被修改时,version 值会+1。当线程A要更新数据值时,在读取数据的同时也会读取 version 值,在提交更新时,若刚才读取到的 version 值与当前数据库中的 version 值相等时才更新,否则重试更新操作,直到更新成功。

链接:https://www.jianshu.com/p/d2ac26ca6525

锁的升级:
无锁—>偏向锁—>轻量锁—>重量锁

代码中如何优化synchronized?

1.减少synchronized的范围
同步代码块中尽量短,减少执行时间,减少锁的竞争:

synchronized(Demo.class){
	System.out.println("aaa");
}

2.降低synchronized锁的力度
将一个锁拆分成多个锁,提高高并发
例子1:比如HashTable中对put,get,remove全部加锁,用的都是同一把锁。
ConcurrentHashMap优化:
对第一次加入的元素使用CAS, segment对桶里的第一个元素加锁。这样锁住一个桶,另外一个桶也可以同时加元素。

例子2:

public void test01(){
	synchronized(Demo.class){}
}
public void test02(){
	synchronized(Demo.class){}
}

使用类实例,两个线程都使用同一把锁,性能差。 尽量不用class实例,去降低锁的粒度。

3.读写分离
在队列中,为了保证队列安全,加锁。如果获取和添加都是同一把锁,性能就慢。

LinkedBlockingQueue实现: 从一头take元素使用一把锁,从另一头put元素使用另一把锁。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值