java并发编程之synchronized

1.多个线程访问同一资源时会产生线程安全问题,因此要实现同步互斥访问资源。

举例:在购票时,总票数10,售票员1卖一张票票数为9,但在售票员1卖这张票的过程中,售票员2也在卖票其开始读到总票数也为10,卖一张9,然后两个售票员都读到了余票为9的错误的信息。

2.解决线程安全问题方式:

(1)synchronized同步方法或同步块(2)lock锁

 在java中,每个对象都有一个锁标记,当线程要访问被synchronized关键字修饰的代码片段时,必须检查该对象的锁是否可用,可用,获得锁,执行代码,释放锁。其他想访问该代码片段没获得该锁的线程就处于自我阻塞状态。

<1>synchronized 同步方法
未使用synchronized修饰:

package day03;

import java.util.ArrayList;

/**
 * @author wangpei
 * @version 创建时间:2017年2月10日 上午10:38:22 类说明
 */
public class Test implements Runnable {
    final InsertData insertData = new InsertData();

    public void run() {
        insertData.insert(Thread.currentThread());

    }

    public static void main(String[] args) {

        Test t = new Test();
        new Thread(t, "Thread-0").start();
        new Thread(t, "Thread-1").start();
    }
}

class InsertData {
    private ArrayList<Integer> arrayList = new ArrayList<Integer>();

    public  void insert(Thread thread) {
        for (int i = 0; i < 5; i++) {
            System.out.println(thread.getName() + "在插入数据" + i);
            arrayList.add(i);
        }
    }
}

执行结果:

Thread-0在插入数据0
Thread-1在插入数据0
Thread-1在插入数据1
Thread-0在插入数据1
Thread-1在插入数据2
Thread-0在插入数据2
Thread-1在插入数据3
Thread-0在插入数据3
Thread-1在插入数据4
Thread-0在插入数据4
线程0和线程1的执行是无序的

使用synchronized:

class InsertData {
    private ArrayList<Integer> arrayList = new ArrayList<Integer>();

    public synchronized void insert(Thread thread) {
        for (int i = 0; i < 5; i++) {
            System.out.println(thread.getName() + "在插入数据" + i);
            arrayList.add(i);
        }
    }
}

执行结果:

Thread-0在插入数据0
Thread-0在插入数据1
Thread-0在插入数据2
Thread-0在插入数据3
Thread-0在插入数据4
Thread-1在插入数据0
Thread-1在插入数据1
Thread-1在插入数据2
Thread-1在插入数据3
Thread-1在插入数据4
Thread-0先获得对象的锁然后,执行代码插入数据,然后释放锁,接着Thread-1执行。0,1线程是顺序执行的。

注意:
(1)在并发访问时,要将资源域设置为private,防止其它线程直接操作该资源。
(2)当一个线程在访问该对象的synchronized修饰的方法时,其它线程可以访问该对象的非synchronized修饰的方法,但不可以访问该对象的其他被synchronized修饰的方法。(对象锁只有一个,已经在使用中,必须获得锁在可以访问synchronized修饰的方法)
例子:
对于对象object:有方法
synchronized void s1(){},
synchronized void s2(){},
void s3(){},
当线程Thread-0获得锁而操作s1方法时,线程Thread-1可以访问s3方法,不可以访问s2方法。
<2>synchronized 同步代码块

形式:
synchronized(对象this(当前对象)/属性){}

class InsertData {
    private ArrayList<Integer> arrayList = new ArrayList<Integer>();

    public  void insert(Thread thread) {
        synchronized(arrayList){
        for (int i = 0; i < 5; i++) {
            System.out.println(thread.getName() + "在插入数据" + i);

                arrayList.add(i);
        }
    }
    }
}

同步代码块,比同步方法范围小,在执行时访问线程Thread-0访问a方法中同步代码块中的内容,而没获得锁的Thread-1仍旧可访问a方法中的非synchronized代码快。

<3>类锁
针对每个类,也有一个锁(作为class对象的一部分)。synchronized static修饰的方法可以在类范围内防止对static数据的并发访问。

原文地址:https://www.cnblogs.com/wangxiaopei/p/8551256.html