java并发编程基础——线程同步

线程同步

一、线程安全问题

如果你的代码所在的进程中有多个线程在同时运行,而这些线程可能会同时运行这段代码。如果每次运行结果和单线程运行的结果是一样的,而且其他的变量的值也和预期的是一样的,就是线程安全的。

线程安全问题往往发生在多个线程调用同一方法或者操作同一变量,但是我们要知道其本质就是CPU对线程的随机调度,CPU无法保证一个线程执行完其逻辑才去调用另一个线程执行。

package threadtest;
 
public class ThreadTest  implements Runnable{
 
    static int i = 0;
    public  void incre() {
        i++;
    }
     
    @Override
    public  void run() {
        for(int j=0;j<1000000;j++) {
            incre();
        }
    }
 
    public static void main(String[] args) throws InterruptedException {
        ThreadTest tt = new ThreadTest();
        Thread t1 = new Thread(tt);
        Thread t2 = new Thread(tt);
        t1.start();
        t2.start();
        t1.join();
        t2.join();
        System.out.println(i);
    }
         
}

由于 i++不是原子操作,先读取值,再加1 赋值,所以当在读取i值的时候线程切换了,导致两个线程读取的i相同,导致线程安全问题。

1363390  //结果小于2000000

二、线程同步

java多线程支持引入了同步监视器来解决线程同步问题,通过synchronized关键字,主要有同步方法和同步代码块

执行同步代码前必须先获得对同步监视器的锁定(任何时刻都只有一个线程可以获得同步监视器的锁定)

java5开始提供了更强大的同步机制,同步锁Lock

1、同步方法: synchronized修饰方法

package threadtest;
 
public class ThreadTest  implements Runnable{
 
    static int i = 0;
    public synchronized void incre() {
        i++;
    }
     
    @Override
    public   void run() {
        for(int j=0;j<1000000;j++) {
            incre();
        }
    }
 
    public static void main(String[] args) throws InterruptedException {
        ThreadTest tt = new ThreadTest();
        Thread t1 = new Thread(tt);
        Thread t2 = new Thread(tt);
        t1.start();
        t2.start();
        t1.join();
        t2.join();
        System.out.println(i);
    }
         
}

同步方法的同步监视器就是方法所属的对象本身

2000000 //结果正常

synchronized修饰静态方法

package threadtest;
 
public class ThreadTest  implements Runnable{
 
    static int i = 0;
    /**
     * 同步静态方法的同步监视器是该类对应的class对象
     */
    public static synchronized  void incre() {
        i++;
    }
     
    @Override
    public   void run() {
        for(int j=0;j<1000000;j++) {
            incre();
        }
    }
 
    public static void main(String[] args) throws InterruptedException {
        Thread t1 = new Thread(new ThreadTest());
        Thread t2 = new Thread(new ThreadTest());
        t1.start();
        t2.start();
        t1.join();
        t2.join();
        System.out.println(i);
    }
         
}

上面类中synchronized修饰的静态方法,同步监视器是该类对应的class对象,i是类属性,多个线程调用不同实例,i也是线程安全的。

2000000

2、同步代码块

除了使用关键字修饰实例方法和静态方法外,还可以使用同步代码块,在某些情况下,我们编写的方法体可能比较大,同时存在一些比较耗时的操作,而需要同步的代码又只有一小部分,如果直接对整个方法进行同步操作,可能会得不偿失,此时我们可以使用同步代码块的方式对需要同步的代码进行包裹,这样就无需对整个方法进行同步操作了

package threadtest;
 
public class ThreadTest  implements Runnable{
 
    static int i = 0;
    /**
     * 同步代码块,synchronized(obj),obj就是同步监视器
     */
    public void incre() {
        synchronized(this) {
            i++;
        }
    }
     
    @Override
    public   void run() {
        for(int j=0;j<1000000;j++) {
            incre();
        }
    }
 
    public static void main(String[] args) throws InterruptedException {
        ThreadTest tt = new ThreadTest();
        Thread t1 = new Thread(tt);
        Thread t2 = new Thread(tt);
        t1.start();
        t2.start();
        t1.join();
        t2.join();
        System.out.println(i);
    }
         
}

同步代码块修饰静态方法

package threadtest;
 
public class ThreadTest  implements Runnable{
 
    static int i = 0;
    /**
     * 同步代码块,synchronized(obj),obj就是同步监视器
     */
    public static void incre() {
        synchronized(ThreadTest.class) {
            i++;
        }
    }
     
    @Override
    public   void run() {
        for(int j=0;j<1000000;j++) {
            incre();
        }
    }
 
    public static void main(String[] args) throws InterruptedException {
        //ThreadTest tt = new ThreadTest();
        Thread t1 = new Thread(new ThreadTest());
        Thread t2 = new Thread(new ThreadTest());
        t1.start();
        t2.start();
        t1.join();
        t2.join();
        System.out.println(i);
    }
         
}

3、同步锁Lock

java5开始提供了通过显示定义同步锁对象来实现同步。

在实现线程安全中,比较常用的是ReentrantLock(可重入锁),它是Lock接口的实现类。

package threadtest;
 
import java.util.concurrent.locks.ReentrantLock;
 
public class ThreadTest  implements Runnable{
 
    private final ReentrantLock lock = new ReentrantLock();
    static int i = 0;
    public void incre() {
        lock.lock();//加锁
        try {
            i++;
        } finally {
            lock.unlock();
        }
             
    }
     
    @Override
    public   void run() {
        for(int j=0;j<1000000;j++) {
            incre();
        }
    }
 
    public static void main(String[] args) throws InterruptedException {
        ThreadTest tt = new ThreadTest();
        Thread t1 = new Thread(tt);
        Thread t2 = new Thread(tt);
        t1.start();
        t2.start();
        t1.join();
        t2.join();
        System.out.println(i);
    }
         
}
2000000//线程安全

4、死锁

当两个线程同时等待对方释放同步监视器就会发生死锁,java虚拟机没有检测,也没有采取措施来处理死锁情况,所以多线程编程时应该采取措施避免死锁出现。

一旦出现死锁,程序不会发生任何异常情况,也没有任何提示,只是所有线程处于阻塞状态,无法继续

下面程序就是发生死锁

package threadtest;
/**
 * 一个简单的死锁例子,大概的思路:两个线程A和B,两把锁X和Y,现在A先拿到锁X,然后sleep()一段时间,我们知道sleep()是不会释放锁资源的。然后如果这段时间线程B拿到锁Y,也sleep()一段时间的话,那么等到两个线程都醒过来的话,那么将互相等待对方释放锁资源而僵持下去,陷入死锁。flag的作用就是让A和B获得不同的锁。
 * @author rdb
 *
 */
public class ThreadTest  implements Runnable{
 
    Object o1 = new Object();
    Object o2 = new Object();
    private boolean flag = true ;
     
    @Override
    public   void run() {
        if(flag) {
            flag = false;
            synchronized (o1) {
                System.out.println(Thread.currentThread().getName());
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                synchronized (o2) {
                    System.out.println("**************");
                }
            }
        }else {
            flag = true;
            synchronized (o2) {
                System.out.println(Thread.currentThread().getName());
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                synchronized (o1) {
                    System.out.println("**************");
                }
            }
        }
    }
 
    public static void main(String[] args) throws InterruptedException {
        ThreadTest tt = new ThreadTest();
        Thread t1 = new Thread(tt);
        Thread t2 = new Thread(tt);
        t1.start();
        t2.start();
         
         
    }
         
}

猜你喜欢

转载自www.cnblogs.com/jnba/p/10592970.html