java 多线程: Thread 并发访问-代码块同步synchronized {};String作为被锁的对象

方法同步的弊端

方法同步的时候,如果一个方法需要线程安全控制的代码速度其实很快,但是有其他的业务逻辑代码耗时非常长(比如网络请求),这样所有的线程就在这一块就等待着了,这样造成了极大的资源浪费如果并发量很大,可能会造成系统崩溃。(并发的线程遇到synchronized同步的方法,变成串行....

并发访问-代码块同步

语法:

synchronized (要锁住的对象) {
  并发执行且不耗时的业务计算代码;
}
 
代码示例: 方法同步比代码块同步耗时天壤地别
import java.util.HashSet;
import java.util.Set;

/**
 * @ClassName ThreadSyncCode
 * @projectName: object1
 * @author: Zhangmingda
 * @description: XXX
 * date: 2021/4/22.
 */
public class ThreadSyncCode {
    //子线程类外公共用来计算的变量
    private static int num = 0;
    //子线程对象
    private static class MyRannable implements Runnable {
        @Override
        public void run() {  //这里synchronized 导致耗时的IO操作也变为串行
            /**
             * 模拟网络IO,磁盘IO耗时等待操作
             */
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            /**
             * 避免多线程同时计算的代码块改为同步代码块。变为多线程到此串行执行;上面的IO耗时操作仍然并行
             */
            synchronized (getClass()){
                for (int i=0; i<1000; i++){
                    num++;
                }
            }
            System.out.println(Thread.currentThread().getName() + " num result:" + num);
        }
    }
    public static void main(String[] args) {
        Set<Thread> threads = new HashSet<>();
        MyRannable myRannable = new MyRannable();
        for (int i=0; i<5; i++){
            threads.add(new Thread(myRannable,"线程"+i));
        }
        threads.forEach(thread -> thread.start());

        try {
            Thread.sleep(6000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("main num result:" + num);
    }

}

方法同步IO耗时串行导致长达5S出结果。而代码块同步耗时1S多结束。

String作为被锁的对象

import java.util.HashSet;
import java.util.Set;

/**
 * @ClassName ThreadMultiObjectSyncStringLock
 * @projectName: object1
 * @author: Zhangmingda
 * @description: XXX
 * date: 2021/4/22.
 */
public class ThreadMultiObjectSyncStringLock {
    private static int num = 0;
    private static class MyThread extends Thread{
        private String lock;

        public MyThread(String name, String lock) {
            super(name);
            this.lock = lock;
        }

        @Override
        public void run() {
            synchronized (lock){
                for (int i=0; i<1000; i++){
                    num++;
                }
                System.out.println(Thread.currentThread().getName() + "   num:" + num);
            }
        }
    }
    public static void main(String[] args) {
        //String锁对象
        String lock = "LOCK";
        //存放线程的集合
        Set<MyThread> myThreads = new HashSet<>();
        //向集合添加线程实例
        for (int i=0; i<5; i++){
            myThreads.add(new MyThread("T" +i, lock));
        }
        //启动所有线程
        myThreads.forEach(myThread -> myThread.start());
        //稍等时间主线程查看结果
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(Thread.currentThread().getName() + "num Result:" + num); //5000完全正确
    }
}
结果:最终打印出来的num的值就是5000,结果是正确的。但是我们发现我们在for循环创建我们的线程的时候,我们定义了5次lock,但是实际上这些lock是同一个对象(指向的是同一个内存地址),因为在JAVA中,字符串具有常量缓存的功能。所以会被锁起来。
原文地址:https://www.cnblogs.com/zhangmingda/p/14691099.html