线程的同步

来源:互联网 发布:json数据格式 编辑:程序博客网 时间:2024/06/02 03:24

线程的不确定性

由于cpu执行线程采取时间片轮询的方式,当某个操作还没执行完就调度到另一个线程。这就导致下次再调度到这个线程的时候。数据就会发生错乱。
举个例子:
我们创建两个相同线程,让他们对同一个数加1,每个加5次:

public class Main {    private static int currentCount = 0;    private static int NUM = 5;    public static void main(String[] args) {        new Thread(new MRunnable()).start();        new Thread(new MRunnable()).start();    }    static class MRunnable implements Runnable {        @Override        public void run() {            for (int i = 0; i < NUM; i++) {                currentCount++;                try {                    Thread.sleep(10);                } catch (InterruptedException e) {                    e.printStackTrace();                }            }            System.out.println("最后结果:"+currentCount);        }    }}

当NUM为5的时候正常输出:

最后结果:10最后结果:10

当NUM为10的时候结果是这样的:

最后结果:18最后结果:18

运行多几次每次的结果都有可能不一样。
因为这种操作不是原子性的,在操作的过程中有可能没有存在内存里面,所以就导致的线程的不确定性

多线程同步

由于同时运行的线程需要共享数据,就要考虑其他的线程的行为与状态,所以同步就产生了。
java利用对象的互斥性,来保证数据的完整。

  1. 每个对象对应一个互斥锁的标记,来保证线程访问对象的唯一性。
  2. 关键字synchronized

synchronized用法:

  1. 对代码块加锁
  2. 对某个方法加锁
    用刚刚的例子,在直接对 for循环 上锁:
public class Main {    private static int currentCount = 0;    private static int NUM = 10;    public static void main(String[] args) {        new Thread(new MRunnable()).start();        new Thread(new MRunnable()).start();    }    static class MRunnable implements Runnable {        @Override        public void run() {            synchronized (this) {                for (int i = 0; i < NUM; i++) {                    currentCount++;                    try {                        Thread.sleep(10);                    } catch (InterruptedException e) {                        e.printStackTrace();                    }                }            }            System.out.println("最后结果:" + currentCount);        }    }}

经过运行了多次 结果都是:

最后结果:20最后结果:20

当for 循环拿到锁后,其他操作只能等待,当for循环执行完,释放锁后另一个线程才可以进去,这就保证了线程的安全性。

线程的同步控制

wait():当上锁的对象或方法需要等待其他操作的时候,就需要释放当前的锁,也就是等待其他的操作完成再来执行。
notify()或者notifyAll:让等待的方法进入就绪状态。
例子:
在一个数组中的get与add方法

static class MyArray {        private int index = 0;        private int[] array = new int[10];        public synchronized void put(int value) {            //当数组元素为最大的时候,就需要等待get数据后再进行操作            while (array.length == index) {                try {                    this.wait();                } catch (InterruptedException e) {                    e.printStackTrace();                }            }            array[index] = value;            index++;            this.notify();        }        public synchronized int get() {            //当数组元为0的时候,就需要等待add数据后再进行操作            while (index<= 0) {                try {                    this.wait();                } catch (InterruptedException e) {                    e.printStackTrace();                }            }            index--;            int val =array[index];            this.notify();            return val;        }    }

可以看出,当length=0的时候wait()等待释放锁,然后让put执行,通里put也一样。

死锁

死锁的情况有时很难查找出来,但有很重要,因为一旦死锁,程序就无法进行,一般死锁是两个锁之间互相等待对方执行导致的。网上也会有相应的例子
JDK从1.5以后就增加了更多的类,让使用锁机制更加灵活
比如:

  1. java.util.concurrent.locks包
  2. lock() 、tryLock()、unLock()
  3. ReadWriteLock等接口

    lock() 、tryLock()、unLock() 后面有时间在慢慢研究。

原创粉丝点击