JAVA 线程同步

多线程在操作同一个资源时,同一时刻只能有一个线程操作,其他线程等待这个线程操作结束后抢占操作这个资源,就是线程同步。

优点:线程同步可以保证多线程在操作同一个资源时,结果的正确性。

缺点:抢占式占用cpu处理器,只能保持一个线程执行,性能下降。

  • 线程同步的实现 加锁

方式一:synchronized锁代码块。

public class TestSyn {
    public static void main(String[] args) {
        Thread thread1 = new Thread(new MyTicket(),"黄牛1");
        Thread thread2 = new Thread(new MyTicket(),"黄牛2");
        Thread thread3 = new Thread(new MyTicket(),"观众");

        thread1.start();
        thread2.start();
        thread3.start();

    }
}

/*
*synchronized ("锁"){
}
//锁是一个Object对象类型 
()里面的值:
    1."锁"--固定值
    2.this--对象锁 只能是同一个对象   
    这里是三个对象: 使用了三次 new MyTicket();
        Thread thread1 = new Thread(new MyTicket(),"黄牛1");
        Thread thread2 = new Thread(new MyTicket(),"黄牛2");
        Thread thread3 = new Thread(new MyTicket(),"观众");
     这里是一个对象:使用了一次 new MyTicket(); 注意两次 new Thread()只是代理了一个MyTicket对象申请                     了两个线程 比喻为一个人有两个账号买票
        MyTicket myTicket = new MyTicket();
        Thread thread1 = new Thread(myTicket,"观众1");
        Thread thread2 = new Thread(myTicket,"观众2");
        Thread thread3 = new Thread(new MyTicket(),"观众");  
*/

class MyTicket implements  Runnable{

    static int num = 1;
    @Override
    public void run() {
        while(num<=1000){
            synchronized ("锁"){//锁是一个Object对象类型 ()里面的值:"锁"--固定值、this--对象锁    
                if (num<=1000){
                    System.out.println(Thread.currentThread().getName()+"买了第"+num+"张票");
                    num++;
                }else{
                    System.out.println("票售完了");
                }

            }
        }
    }
}

方式二:synchronized锁方法 [1]不建议结合run()修饰使用 [2]锁就是this

public class TestSyn2 {
    public static void main(String[] args) {
        MyTicket2 myTicket2 = new MyTicket2();
        Thread thread1 = new Thread(myTicket2,"黄牛");
        Thread thread2 = new Thread(myTicket2,"观众");
        thread1.start();
        thread2.start();

//        //下面代码配合demo()01
//        MyTicket2 myTicket3 = new MyTicket2();
//        MyTicket2 myTicket4 = new MyTicket2();
//        Thread thread3 = new Thread(myTicket3,"黄牛");
//        Thread thread4 = new Thread(myTicket4,"观众");
//        thread3.start();
//        thread4.start();
    }
}
class MyTicket2 implements Runnable{
    //代表的卖出的票数
    static int num = 1;

    public void run(){
        while(num<=500){
            //demo01();
            demo02();
        }
    }


    /*
       synchronized 修饰成员方法 锁的是this 要锁的对象必须是同一个对象
       synchronized 修饰静态方法 当前锁的类锁 传递对象的字节码.class 同一个类创建出不同的对象
     */

    public  synchronized  static void demo01() {


        if (num<=500){
            System.out.println(Thread.currentThread().getName()+"买了第"+num+"张票");
            num++;
        }else{
            System.out.println("票售完了");


        }
    }

    public  synchronized  void demo02() {


                if (num<=500){
                    System.out.println(Thread.currentThread().getName()+"买了第"+num+"张票");
                    num++;
                }else{
                    System.out.println("票售完了");


        }
    }


}

  • 死锁

死锁产生的原因很多:1多个线程共享多个资源 2:多个线程都需要其他线程的资源,每个线程又不愿意或者无法放弃自己的资源

案例:

/**
 * 死锁案例:小明和小红都要看电视  小明要看铠甲勇士 小红要看巴啦啦小魔仙 但是只有一个遥控器 小明拿到了遥控器壳 小红拿到了电池
 * 小红和小明都不愿做出让步 然后两个人都看不到电视 电视就相当整个计算机 遥控器相当于CPU 小明和小红看电视的要求 相当于两个线程
 * 两个线程都需要对方的资源才能上cpu运行 但是两个线程都不可释放需要的资源 cpu停滞 就是死锁
 */
public class TestSyn3 {
    public static void main(String[] args) {
        Thread thread1 = new Thread(new XiaoMing());
        Thread thread2 = new Thread(new XiaoHong());
        thread1.start();
        thread2.start();
    }
}

class XiaoMing implements Runnable{

    @Override
    public void run() {
        synchronized ("遥控器壳"){
            System.out.println("小明抢到了遥控器壳");
            synchronized ("电池"){
                System.out.println("我要看铠甲勇士");
            }
        }
    }
}

class XiaoHong implements Runnable{

    @Override
    public void run() {
        synchronized ("电池"){
            System.out.println("小红抢到了电池");
            synchronized ("遥控器壳"){
                System.out.println("我要看巴啦啦小魔仙");
            }
        }
    }
}

synchronize缺点:产生死锁的情况是没有办法处理,主要的原因是加锁和解锁的操作我们是不可以自己操作的,都是JVM完成。

方式三:Lock 可是首先手动加锁、解锁。 多线程使用的时候必须是同一个锁对象 如果代码出现了异常程序不会自动解锁,最好捕获异常。

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;


public class TestD {

    public static void main(String[] args) {
        new Thread(new TicketRunnable(),"1号窗口").start();
        new Thread(new TicketRunnable(),"2号窗口").start();

    }
}
class TicketRunnable implements Runnable {

    static int num = 1;
    //创建Lock锁
    static Lock lock = new ReentrantLock();
    @Override
    public void run() {
        while (num<=1000){
            lock.lock(); //给共享资源上锁
            try{
                if (num<=1000){
                    System.out.println(Thread.currentThread().getName()+"售出了第"+num+"张票");
                    num++;
                }else {
                    System.out.println("票售完了");
                }
            }catch (Exception e){
                e.printStackTrace();
            }finally {
                lock.unlock();//解锁
            }
        }
    }
}

Lock锁解析:

  1. lock()使用最多的一个方法,就是用来获取锁,如果锁已经被其他线程获取,则进行等待。

如果采用lock锁,必须主动去释放锁,并且发生异常时,不会自动释放锁,防止死锁的发生。通常配合异常使用:

            lock.lock(); //给共享资源上锁
            try{

            }catch (){
             
            }finally {
                lock.unlock();//解锁
            }
  1. tryLock()

tryLock()方法是有返回值的,它表示用来尝试获取锁,如果获取成功,则返回true,如果获取失败(即锁已被其他线程获取),返回false。也就是说整个方法无论如何都会立即返回,拿不到锁时不会一直在那等待。

  1. tryLock(long time,TimeUnit unit)

获取锁的方法和tryLock()类似,区别在于不会一直等待,参数有时间,如果一开始就拿到锁或者在等待区间拿不到锁就返回false,如果一开始就拿到锁或者在等待区间拿到锁就返回true。

  1. lockInterruptibly()

lockInterruptibly()方法比较特殊,可以中断线程的等待状态。

ReentrantLock()可重入锁。

ReentrantLock(),意思是"可重入锁",可以被单个线程多次获取。

ReadWriteLock()

两个主要方法ReadLock()读锁 WriteLock()写锁;

获取读锁和写锁代码

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;

public class TestReadWriteLock {
    public static void main(String[] args) {
        //获取读和写的锁
        ReentrantReadWriteLock reentrantReadWriteLock = new ReentrantReadWriteLock();
        //获取读锁
        ReentrantReadWriteLock.ReadLock readLock = reentrantReadWriteLock.readLock();
        //获得写锁
        ReentrantReadWriteLock.WriteLock writeLock = reentrantReadWriteLock.writeLock();

        System.out.println("-----------使用接口形式进行表示--------------");
        //使用多态 父类引用子类对象
        ReadWriteLock readWriteLock = new ReentrantReadWriteLock();
        //获取读锁
        Lock readLock2 = readWriteLock.readLock();
        //获取写锁
        Lock writeLock2 = readWriteLock.writeLock();
        
    }


}

读写案例: 实现读写可以互斥进行,可以多个线程同时读,不能同时写,读写操作不能同时进行。

public class TestReadWriteLock2 {
    public static void main(String[] args) {
        new Thread(()->{
            new Operator().read();
        },"A").start();

        new Thread(()->{
            new Operator().read();
        },"B").start();

        new Thread(()->{
            new Operator().write();
        },"C").start();

        new Thread(()->{
            new Operator().write();
        },"D").start();
    }
}

class Operator{
    //这个方法就是一个读方法
    public void read(){
        System.out.println(Thread.currentThread().getName()+"开始读操作");
        try{
            Thread.sleep(1000); //模拟读消耗的时间
        }catch (Exception e){
            e.printStackTrace();
        }
        System.out.println(Thread.currentThread().getName()+"结束读操作");
    }
    public void write(){
        System.out.println(Thread.currentThread().getName()+"开始写操作");
        try{
            Thread.sleep(1000); //模拟读消耗的时间
        }catch (Exception e){
            e.printStackTrace();
        }
        System.out.println(Thread.currentThread().getName()+"结束写操作");
    }
}
JAVA 线程同步_第1张图片

没有加锁可以看到运行结果读写顺序不一致。

加lock锁测试

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class TestReadWriteLock2 {
    public static void main(String[] args) {
        new Thread(()->{
            new Operator().read();
        },"A").start();

        new Thread(()->{
            new Operator().read();
        },"B").start();

        new Thread(()->{
            new Operator().write();
        },"C").start();

        new Thread(()->{
            new Operator().write();
        },"D").start();
    }
}

class Operator{
    //这个方法就是一个读方法
    static Lock lock = new ReentrantLock();
    public void read(){
        lock.lock();
        System.out.println(Thread.currentThread().getName()+"开始读操作");
        try{
            Thread.sleep(1000); //模拟读消耗的时间
        }catch (Exception e){
            e.printStackTrace();
        }
        System.out.println(Thread.currentThread().getName()+"结束读操作");
        lock.unlock();
    }
    public void write(){
        lock.lock();
        System.out.println(Thread.currentThread().getName()+"开始写操作");
        try{
            Thread.sleep(1000); //模拟读消耗的时间
        }catch (Exception e){
            e.printStackTrace();
        }
        System.out.println(Thread.currentThread().getName()+"结束写操作");
        lock.unlock();
    }
}
JAVA 线程同步_第2张图片

只能保证单个线程的读写互斥。

加读写锁

import java.util.concurrent.locks.ReentrantReadWriteLock;

public class TestReadWriteLock2 {
    public static void main(String[] args) {
        new Thread(()->{
            new Operator().read();
        },"A").start();

        new Thread(()->{
            new Operator().read();
        },"B").start();

        new Thread(()->{
            new Operator().write();
        },"C").start();

        new Thread(()->{
            new Operator().write();
        },"D").start();
    }
}

class Operator{
    //这个方法就是一个读方法
    static ReentrantReadWriteLock readWriteLock = new ReentrantReadWriteLock();
    public void read(){
        readWriteLock.readLock().lock();//读锁上锁
        System.out.println(Thread.currentThread().getName()+"开始读操作");
        try{
            Thread.sleep(1000); //模拟读消耗的时间
        }catch (Exception e){
            e.printStackTrace();
        }
        System.out.println(Thread.currentThread().getName()+"结束读操作");
        readWriteLock.readLock().unlock();//读锁解锁
    }
    public void write(){
        readWriteLock.writeLock().lock();//写锁上锁
        System.out.println(Thread.currentThread().getName()+"开始写操作");
        try{
            Thread.sleep(1000); //模拟读消耗的时间
        }catch (Exception e){
            e.printStackTrace();
        }
        System.out.println(Thread.currentThread().getName()+"结束写操作");
        readWriteLock.writeLock().unlock();//写锁解锁
    }
}
JAVA 线程同步_第3张图片

你可能感兴趣的:(JAVA多线程,java,开发语言,面试)