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中,字符串具有常量缓存的功能。所以会被锁起来。
posted on 2021-04-22 19:15 zhangmingda 阅读(188) 评论(0) 编辑 收藏 举报