>  기사  >  Java  >  잠금 사용량 요약 공유

잠금 사용량 요약 공유

巴扎黑
巴扎黑원래의
2017-06-23 10:50:452087검색

Lock

이전 기사에서 동기 액세스를 달성하기 위해 동기화 키워드를 사용하는 방법에 대해 이야기했습니다. 이 기사에서는 이 문제에 대해 계속 논의합니다. Java 5부터 동기 액세스를 달성하는 또 다른 방법이 java.util.concurrent.locks 패키지에 제공되는데, 이것이 바로 Lock입니다.

동기화된 액세스는 동기화를 통해 달성할 수 있는데 왜 잠금을 제공해야 합니까? 이 문제는 아래에서 다루겠습니다. 이 기사는 동기화의 결함으로 시작하여 java.util.concurrent.locks 패키지에서 일반적으로 사용되는 클래스와 인터페이스에 대해 이야기하고 마지막으로 잠금 개념에 대해 다음 사항을 논의합니다

동기화 결함

앞서 언급한 내용은 동기화된 스레드가 잠금을 해제하는 경우 두 가지 상황이 있다고 합니다.

  1. 코드 블록 또는 동기화된 메서드가 실행됩니다.

  2. 코드 블록 또는 동기화된 메서드와 jvm에서 예외가 발생합니다. 자동으로 잠금을 해제합니다

위에서 보면 동기화 코드 블록이 실행되거나 예외가 발생한 경우에만 해제된다는 것을 동기화 해제 잠금에서 알 수 있습니다. IO 이유로 인해 스레드는 잠금을 해제하지 않지만 이때 다른 스레드는 계속 실행됩니다. 다른 프로그램은 프로그램의 실행 효율성에 큰 영향을 미쳤습니다. 이제 스레드가 무한정 대기하고 응답하는 것을 방지할 수 있는 메커니즘이 필요합니다. 이는 lock

을 통해 수행할 수 있습니다. 또한 프로그램에 여러 개의 읽기 스레드와 하나의 쓰기 스레드가 포함되어 있으면 동기화는 하나의 스레드에서만 실행할 수 있지만 여러 개의 읽기 스레드가 필요하다는 것을 알 수 있습니다. 동시에 읽기 때문에 동기화를 사용하는 것은 절대 불가능하지만 잠금을 사용할 수도 있습니다.

Lock

에 가서 API를 확인하면 잠금이 인터페이스이므로 객체를 생성할 수 없음을 알 수 있습니다. 그러나 먼저 Lock 클래스를 살펴보도록 하겠습니다. 클래스를 소개할 때 구체적인 구현에 대해 자세히 설명하겠습니다. Implements

method

  • lock() 잠금을 획득하고 획득하지 못한 경우 계속 대기합니다lock() 获取锁,如果没有获得就会一直等待

  • unlock() 释放锁

  • tryLock() 尝试获得锁,如果成功获得锁就执行,如果没有成功获得锁,那么就不会等待了

  • lockInterruptibly() 如果当前线程未被中断,则获取锁。

ReentrantLock

ReentrantLock是可重入锁,是实现Lock接口的一个类,可重入是一种线程的分配机制,可重入的意思就是总是分配给最近获得锁的线程,这是一种不公平的分配机制,将会出现饥饿现象,当然为了解决这种现象,ReentrantLock的构造方法还提供了一个fair参数,如果fair为true表示使用公平分配机制,将会有等待时间最长的线程获得锁

构造方法

  • ReentrantLock() 创建一个对象,默认使用的时可重入的机制

  • ReentrantLock(boolean fair) 如果fair为true那么使用的是公平分配机制

常用方法

  • lock() 获取锁,如果没有获取到将会一直阻塞

下面使用一段程序演示以下lock方法的使用,代码如下:

//实现接口的线程类public class MyThread implements Runnable {public ReentrantLock rLock = null;  //注意这里的锁一定要是全局变量,否则每一个线程都创建一把锁,那么将会毫无意义
 public MyThread() {this.rLock = new ReentrantLock(); // 创建默认的可重入锁}// 将unlock方法放在finally中确保执行中代码出现异常仍然能够释放锁,否则将会造成其它的线程阻塞public void display() {this.rLock.lock(); // 获取锁try {for (int i = 0; i < 10; i++) {
                System.out.println(Thread.currentThread().getName() + "正在输出"+ i);
            }
        } finally {this.rLock.unlock(); // 释放锁,注意这步是一定需要的}

    }@Overridepublic void run() {this.display(); // 调用display方法}

}//线程的测试类,主要是创建对象启动线程public class Test {public static void main(String[] args) {final MyThread thread = new MyThread(); // 创建对象// 下面创建两个线程,并且直接启动,new Thread(thread).start();new Thread(thread).start();

    }
}

执行上面的代码得到下图的잠금 사용량 요약 공유:

잠금 사용량 요약 공유

从上面的잠금 사용량 요약 공유看出,线程是一个一个输出的,并且只有等待一个线程输出完毕才能执行下一个线程,这里的仅仅是针对lock和unlock之间的代码,之外的代码并不是受到控制

注意: 这里的创建的可重入锁的对象必须对于每一个线程来说是全局的变量,是可以共享的一个对象,如果你在display方法中创建这个对象,那么是毫无意义的,因为每一个线程用的根本不是同一把锁

  • boolean tryLock()

unlock() 잠금 해제 lock

tryLock() 잠금 획득을 시도하고, 잠금 획득에 성공하면 실행하고, 잠금 획득에 실패하면 대기 시간이 없습니다
🎜lockInterruptible() 현재 스레드가 중단되지 않으면 잠금을 획득합니다. 🎜🎜🎜🎜🎜ReentrantLock🎜🎜🎜ReentrantLock은 Lock 인터페이스를 구현하는 클래스입니다. Reentrant는 최근에 잠금을 획득한 스레드에 항상 할당된다는 의미입니다. 물론 이 현상을 해결하기 위해 ReentrantLock의 생성자도 공정한 매개변수를 제공한다는 것은 공정한 할당 메커니즘을 사용한다는 의미입니다. 가장 긴 스레드가 잠금을 획득합니다🎜🎜🎜Construction method🎜🎜🎜🎜🎜ReentrantLock() 기본적으로 재진입 메커니즘을 사용하여 객체를 생성합니다🎜🎜🎜🎜ReentrantLock(boolean fair ) fair가 true이면 공정 할당 메커니즘이 사용됩니다🎜🎜🎜🎜🎜Common 메서드🎜🎜🎜🎜🎜lock() 잠금을 획득하지 못한 경우, 그것은 항상 차단됩니다🎜🎜🎜🎜🎜다음은 프로그램을 사용하여 다음 잠금 방법의 사용을 보여줍니다.🎜🎜🎜
//实现接口的线程类public class MyThread implements Runnable {public ReentrantLock rLock = null; // 注意这里的锁一定要是全局变量,否则每一个线程都创建一把锁,那么将会毫无意义public MyThread() {this.rLock = new ReentrantLock(); // 创建默认的可重入锁}// 将unlock方法放在finally中确保执行中代码出现异常仍然能够释放锁,否则将会造成其它的线程阻塞public void display() {if (this.rLock.tryLock()) // 如果获取了锁{try {for (int i = 0; i < 10; i++) {
                    System.out.println(Thread.currentThread().getName()
                            + "正在输出" + i);
                }
            } finally {this.rLock.unlock(); // 释放锁,注意这步是一定需要的}

        } else {
            System.out.println(Thread.currentThread().getName()
                    + "获取锁失败,我将不会一直等待........");
        }

    }@Overridepublic void run() {this.display(); // 调用display方法}

}//线程的测试类,主要是创建对象启动线程public class Test {public static void main(String[] args) {final MyThread thread = new MyThread(); // 创建对象// 下面创建两个线程,并且直接启动,new Thread(thread).start();new Thread(thread).start();

    }
}
🎜🎜🎜위 코드를 실행하면 아래와 같은 결과를 얻을 수 있습니다. 🎜🎜🎜🎜잠금 사용량 요약 공유🎜🎜🎜🎜보실 수 있습니다 위의 결과에서 스레드는 하나씩 출력되고 하나만 대기합니다. 다음 스레드는 스레드 출력이 완료된 후에만 실행될 수 있습니다. 여기의 코드는 잠금과 잠금 해제 사이의 코드에만 적용됩니다. 🎜🎜🎜🎜참고: 여기서 생성은 재진입입니다. 잠금 개체는 각 스레드에 대한 전역 변수여야 하며, 표시 메서드에서 이 개체를 생성하면 의미가 없습니다. 각 스레드는 동일한 잠금을 전혀 사용하지 않기 때문입니다. 🎜🎜🎜🎜🎜🎜boolean tryLock() 먼저 잠금을 획득하려고 시도하고 그렇지 않으면 기다리지 않습니다. 영원히🎜🎜🎜🎜🎜🎜 코드를 사용하여 다음 방법을 시도해 보겠습니다. 코드는 다음과 같습니다.🎜🎜🎜
//实现接口的线程类public class MyThread implements Runnable {public ReentrantReadWriteLock rwlock = null;public Lock rLock = null;public MyThread() {this.rwlock = new ReentrantReadWriteLock(); // 创建对象,使用的是非公平的this.rLock = this.rwlock.readLock(); // 获取读取锁对象}// 将unlock方法放在finally中确保执行中代码出现异常仍然能够释放锁,否则将会造成其它的线程阻塞public void display() {this.rLock.lock(); // 获取读取锁try {for (int i = 0; i < 10; i++) {
                System.out.println(Thread.currentThread().getName() + "正在输出"+ i);
            }
        } finally {this.rLock.unlock(); // 释放锁,注意这步是一定需要的}

    }@Overridepublic void run() {this.display(); // 调用display方法}

}//线程的测试类,主要是创建对象启动线程public class Test {public static void main(String[] args) {final MyThread thread = new MyThread(); // 创建对象// 下面创建两个线程,并且直接启动,for(int i=0;i<5;i++)
        {new Thread(thread).start();
        }
        


    }
}
🎜

执行后的잠금 사용량 요약 공유如下图:

잠금 사용량 요약 공유

从上面的잠금 사용량 요약 공유我们知道线程0获取了锁开始执行,但是线程1并没有获取锁,但是使用的是tryLock并不是lock,因此不会一直等待下去,所以直接程序向下运行,直接跳过上锁的代码段,因此就输出了上面的那句话后直接结

ReadWriteLock

从API中可以知道,这个也是一个接口,用于实现读写线程,他有两个方法:Lock readLock(),Lock writeLock() 分别用于获得读锁和写锁,指定特定的锁可以实现特定的功能,比如读锁可以在写线程在执行的情况下可以实现多个读线程进行操作,下面我们来介绍它的具体的实现的类ReentrantReadWriteLock

ReentrantReadWriteLock

这个类也是一个可重入分配的类,当然前面已经说过了什么是可重入,现在我们来说说说这个类的详细的用法

构造方法

  • ReentrantReadWriteLock() 使用默认(非公平)的排序属性创建一个新的 ReentrantReadWriteLock。

  • ReentrantReadWriteLock(boolean fair) 使用给定的公平策略创建一个新的ReentrantReadWriteLock。

常用的方法

  • ReentrantReadWriteLock.ReadLock readLock() 用于返回读取操作的锁

前面已经说过读取操作的锁是用来实现多个线程共同执行的,代码如下:

//实现接口的线程类public class MyThread implements Runnable {public ReentrantReadWriteLock rwlock = null;public Lock rLock = null;public MyThread() {this.rwlock = new ReentrantReadWriteLock(); // 创建对象,使用的是非公平的this.rLock = this.rwlock.readLock(); // 获取读取锁对象}// 将unlock方法放在finally中确保执行中代码出现异常仍然能够释放锁,否则将会造成其它的线程阻塞public void display() {this.rLock.lock(); // 获取读取锁try {for (int i = 0; i < 10; i++) {
                System.out.println(Thread.currentThread().getName() + "正在输出"+ i);
            }
        } finally {this.rLock.unlock(); // 释放锁,注意这步是一定需要的}

    }@Overridepublic void run() {this.display(); // 调用display方法}

}//线程的测试类,主要是创建对象启动线程public class Test {public static void main(String[] args) {final MyThread thread = new MyThread(); // 创建对象// 下面创建两个线程,并且直接启动,for(int i=0;i<5;i++)
        {new Thread(thread).start();
        }
        


    }
}

执行上面的程序잠금 사용량 요약 공유如下:

잠금 사용량 요약 공유

从上面的잠금 사용량 요약 공유可以知道,其实使用读取操作是多个线程同时进行读取的操作,因此一定要小心谨慎的使用,根据自己的需求,一般不能在里面进行修改了,因为出现잠금 사용량 요약 공유不准确的잠금 사용량 요약 공유,这个就不多说了,相信大家都明白,总之要小心使用

  • ReentrantReadWriteLock.WriteLock writeLock() 返回用于写入操作的锁

写入操作的锁和读取操作的锁不一样了,因为一次只能允许一个线程执行写入操作。

并且如果一个线程已经占用了读锁,另外一个线程申请写锁将会一直等待线程释放读锁。

如果一个线程已经占用了写锁,另外一个线程申请读锁,那么这个线程将会一直等待线程释放写锁才能执行。

总之意思就是写线程和读线程不能同时执行,但是多个读线程可以同时执行

下面将使用一个程序详细的体会以下读写锁的综合使用,代码如下:

//实现接口的线程类public class MyThread {public ReentrantReadWriteLock rwlock = null;public Lock rLock = null;public Lock wLock = null;public ArrayList<Integer> arrayList = null;public MyThread() {this.rwlock = new ReentrantReadWriteLock(); // 创建对象,使用的是非公平的this.rLock = this.rwlock.readLock(); // 获取读取锁对象arrayList = new ArrayList<>(); // 实例化this.wLock = this.rwlock.writeLock(); // 获取写入锁对象}// 将unlock方法放在finally中确保执行中代码出现异常仍然能够释放锁,否则将会造成其它的线程阻塞// //向arraylist中写入数据public void put() {this.wLock.lock(); // 获取写入锁try {for (int i = 0; i < 10; i++) {
                System.out.println(Thread.currentThread().getName()
                        + "正在执行写入操作,写入" + i);this.arrayList.add(i);
            }
        } finally {this.wLock.unlock();
        }

    }// 从arraylist中读取数据,这里只是随机读取使用的是get,并没有做什么修改,因为这仅仅是读取操作,如果进行了修改必须实现同步public void get() {this.rLock.lock(); // 获取读取操作的锁Random random = new Random();if (!arrayList.isEmpty()) {try {for (int i = 0; i < 10; i++) {int index = random.nextInt(this.arrayList.size() - 1);int data = this.arrayList.get(index);
                    System.out.println(Thread.currentThread().getName()
                            + "正在读取数据     " + data);
                }
            } finally {this.rLock.unlock();

            }
        } else {
            System.out.println("ArrayList为空");
        }

    }

}//线程的测试类,主要是创建对象启动线程public class Test {public static void main(String[] args) {final MyThread thread = new MyThread(); // 创建对象ArrayList<Thread> arrayList = new ArrayList<>();/*         * 创建8个读线程,2个写线程         */for (int i = 0; i < 2; i++) {
            arrayList.add(new Thread() {@Overridepublic void run() {
                    thread.put();
                }
            });

        }        for(int i=0;i<8;i++)
        {
            arrayList.add(new Thread(){@Overridepublic void run() {
                    thread.get();
                }
            });
        }        
        
        
        for (Thread t : arrayList) {
            t.start();
        }

    }
}

잠금 사용량 요약 공유如下图:

잠금 사용량 요약 공유

从上面可以看出写入线程都是一个一个执行的,读取线程是一起执行的

注意: 所有的锁对象对于线程来说必须是全局变量,否则毫无意义。读线程只能进行不影响线程安全性的操作,比如不能进行对数据的修改插入,如果想要进行修改的话必须还要使用锁对必要的代码实现同步操作

参考文章


위 내용은 잠금 사용량 요약 공유의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

성명:
본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.