记一次多线程实战收获

需求:一台机器有3类数据,总共有多台机器,开启多个线程分别去不同机器获取数据。要求即快速又准确。
心得:

  1. 使用synchronized包装出现资源同步问题的临界区
  2. 在java中使用Map要使用线程安全的ConcurrentHashMap
  3. 在java中使用List要使用List<T> sysList = Collections.synchronizedList(new ArrayList<>());

使用说明:
有关操作系统中线程、进程、死锁、同步、互斥的概念,请看
操作系统之进程管理

关于synchronized的使用

Synchronized的作用

  • 确保线程互斥的访问同步代码
  • 保证共享变量的修改能够及时可见
  • 有效解决重排序问题

1.普通同步方法,锁的是当前实例对象

与2区别在使用声明那里

package ink.poesy.service;

/**
 * @author: WenLeiWang
 * Created in 2019/11/20 11:39
 */
public class SynchronizedUse  implements Runnable{
    //共享资源(临界资源)
    static int i=0;

    /**
     * synchronized 修饰实例方法
     */
    public synchronized void increase(){
        i++;
    }
    @Override
    public void run() {
        for(int j=0;j<1000000;j++){
            increase();
        }
    }
}

创建main调用创建实例调用上面编写的方法

public static void main(String[] args) throws InterruptedException {
        SynchronizedUse instance=new SynchronizedUse();
        Thread t1=new Thread(instance);
        Thread t2=new Thread(instance);
        t1.start();
        t2.start();
        t1.join();
        t2.join();
        System.out.println(i);
    }
    /**
     * 输出结果:
     * 2000000
     */

但是如果这么编写代码的话

public static void main(String[] args) throws InterruptedException {
        SynchronizedUse instance=new SynchronizedUse();
        SynchronizedUse instance2=new SynchronizedUse();
        Thread t1=new Thread(instance);
        Thread t2=new Thread(instance2);
        t1.start();
        t2.start();
        t1.join();
        t2.join();
        System.out.println(i);
    }
    //多次执行
    /**
     * 输出结果:
     * 1994457
     */
     /**
     * 输出结果:
     * 1959761
     */

出现了同步问题,t1和t2都会进入各自的对象锁,也就是说t1和t2线程使用的是不同的锁,因此线程安全是无法保证的。因此要注意上面的方法不能创建多个对象来访问对象里的公共资源,不然锁失效。也同样验证了这样锁的只是当前实例的对象。

2.静态同步方法,锁的是当前类的class对象

同样是锁在了方法上,方法是静态的。

package ink.poesy.service;

/**
 * @author: WenLeiWang
 * Created in 2019/11/20 11:39
 */
public class SynchronizedUse  implements Runnable{
    static int i=0;
    static int j=0;

    /**
     * 作用于静态方法,锁是当前class对象,也就是
     * AccountingSyncClass类对应的class对象
     */
    public static synchronized void increase(){
        i++;
    }

    /**
     * 非静态,访问时锁不一样不会发生互斥
     */
    public synchronized void increase4Obj(){
        j++;
    }

    @Override
    public void run() {
        for(int j=0;j<1000000;j++){
            increase();
            increase4Obj();
        }
    }
    public static void main(String[] args) throws InterruptedException {
        //new新实例
        Thread t1=new Thread(new SynchronizedUse());
        //new心事了
        Thread t2=new Thread(new SynchronizedUse());
        //启动线程
        t1.start();t2.start();

        t1.join();t2.join();
        System.out.println("操作i:"+i);
        System.out.println("操作j:"+j);
    }
}

执行结果:
做100000次循环,正常来说结果是200000。
执行结果
由于synchronized关键字修饰的是静态increase方法,与修饰实例方法不同的是,其锁对象是当前类的class对象。注意代码中的increase4Obj方法是实例方法,其对象锁是当前实例对象,如果别的线程调用该方法,将不会产生互斥现象,毕竟锁对象不同,但我们应该意识到这种情况下可能会发现线程安全问题(操作了共享静态变量i)。

3.同步方法块,锁是括号里面的对象

同步方法块
注释位置表示同步方法块,代码如下

package ink.poesy.service;

/**
 * @author: WenLeiWang
 * Created in 2019/11/20 11:39
 */
public class SynchronizedUse  implements Runnable{
    static SynchronizedUse inc = new SynchronizedUse();
    static int i=0;
    static int j=0;
    static int k=0;

    /**
     * 作用于静态方法,锁是当前class对象,也就是
     * AccountingSyncClass类对应的class对象
     */
    public static synchronized void increase(){
        i++;
    }

    /**
     * 非静态,访问时锁不一样不会发生互斥
     */
    public synchronized void increase4Obj(){
        j++;
    }
    public void increase2Obj(){
        //synchronized (inc){
            k++;
        //}
    }

    @Override
    public void run() {
        for(int j=0;j<1000000;j++){
            increase();
            increase4Obj();
            increase2Obj();
        }
    }
    public static void main(String[] args) throws InterruptedException {
        //new新实例
        Thread t1=new Thread(new SynchronizedUse());
        //new心事了
        Thread t2=new Thread(new SynchronizedUse());
        Thread t3=new Thread(inc);
        //启动线程
        t1.start();t2.start();t3.start();

        t1.join();t2.join();t3.join();
        System.out.println("操作i:"+i);
        System.out.println("操作j:"+j);
        System.out.println("操作k:"+k);
    }
}

有注释结果:k是此次测试的目标结果
在这里插入图片描述
放开注释的结果,即加上块锁:
块锁

发布了67 篇原创文章 · 获赞 22 · 访问量 3万+

猜你喜欢

转载自blog.csdn.net/weixin_42119415/article/details/103157296