java中字符串作爲多線程鎖對象的示例與注意事項

開發過程當中,爲了提交系統效率,會使用一些多線程技術,好比消息隊列、異步service等,雖然提升了系統效率,同時也大大增長了併發機率。
加鎖是最快的解決辦法,加鎖的方式有不少種,本文不作過多討論。
本文主要記錄以字符串爲鎖對象的一個示例和一些注意事項。
話很少說,示例以下:java

package com.xiang.test;

import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier;

import org.apache.commons.lang3.RandomUtils;

public class MainTest {

    public static void main(String[] args) {
        MainTest test = new MainTest();
        test.syncTest();
    }

    public void syncTest() {
        String[] keys = { "1", "2", "3", "4", "5" };
        int oneThreadCnt = 10;
        CyclicBarrier cyclicBarrier = new CyclicBarrier(14);
        for (int i = 0; i < oneThreadCnt; i++) {
            String key = (i >= keys.length) ? keys[i - keys.length] : keys[i];
            key = key + "xxx";
            Thread tempThread = new Thread(new syncTest(cyclicBarrier, key));
            tempThread.setName("Td-" + i);
            tempThread.start();
        }
        new Thread(new syncTest(cyclicBarrier, "3")).start();// 有鎖的效果
        new Thread(new syncTest(cyclicBarrier, "2")).start();// 有所的效果
        new Thread(new syncTest(cyclicBarrier, new String("1"))).start();// 沒有鎖的效果
        new Thread(new syncTest(cyclicBarrier, new String("5"))).start();// 沒有鎖的效果
    }

    class syncTest implements Runnable {

        private CyclicBarrier cyclicBarrier;
        private String key;
        private String x = "ccc";

        public syncTest(CyclicBarrier cyclicBarrier, String key) {
            this.cyclicBarrier = cyclicBarrier;
            this.key = key;
        }

        @Override
        public void run() {
            try {
                cyclicBarrier.await();
                // String syncKey = key + x;// 不能達到鎖的效果
                String syncKey = buildSyncKey(key, x);// 能夠鎖
                // synchronized (key) {//能夠鎖
                synchronized (syncKey) {
                    long sleepMillis = RandomUtils.nextLong(3000, 10000);
                    System.out.println(Thread.currentThread().getName() + "啓動,當前時間:" + System.currentTimeMillis()
                            + "當前是" + key + ",預計休眠時間:" + sleepMillis + ",key.hash:" + key.hashCode());
                    Thread.sleep(sleepMillis);
                }
            } catch (InterruptedException | BrokenBarrierException e1) {
                e1.printStackTrace();
            }
        }

        private String buildSyncKey(String... keys) {
            StringBuilder sb = new StringBuilder();
            for (String string : keys) {
                sb.append(string);
            }
            return sb.toString().intern();
        }
    }

}

注意事項:
如下這種寫法是達不到鎖的效果的,由於 syncKey每次產生的是一個新的對象。
String syncKey = key + x;
synchronized (syncKey) {}apache

只有保證兩個字符串變量最終結果是同一個對象時鎖才能生效
最方便的解決辦法是使用字符串的intern()方法。多線程

intern() 返回常量池中的字符串對象,相同兩個字符串的值相同,則返回的是同一個對象併發

此問題的示例能夠參見轉載資料:
java+內存分配及變量存儲位置的區別: https://blog.csdn.net/rj042/a...
String常量池問題的幾個例子:https://blog.csdn.net/gaopeng...app

相關文章
相關標籤/搜索