目录
Synchronized
称之为”同步锁
作用
保证在同一时刻, 被修饰的代码块或方法只会有一个线程执行,以达到保证并发安全的效果
用法
1.修饰方法:方法锁,锁的对象是当前对象
2.修饰静态方法:类锁,锁的对象是当前的类,实际是这个类的.class对象
3.修饰代码块:对象锁,锁的对象是synchronized修饰的对象
案例
修饰方法
public class TestThread {
public static void main(String[] arge){
Ticket ticket = new Ticket();
new Thread(()->{
for(int i=1;i<40;i++){
ticket.ticket();
}
},"A").start();
new Thread(()->{
for(int i=1;i<40;i++){
ticket.ticket();
}
},"B").start();
new Thread(()->{
for(int i=1;i<40;i++){
ticket.ticket();
}
},"C").start();
}
}
class Ticket{
private int num = 20;
synchronized void ticket(){
if(num > 0){
System.out.println(Thread.currentThread().getName()+"剩余票数:"+num--);
}
}
}
修饰代码块
class SyncThread implements Runnable {
private static int count;
public SyncThread() {
count = 0;
}
public void run() {
synchronized(this) {
for (int i = 0; i < 5; i++) {
try {
System.out.println(Thread.currentThread().getName() + ":" + (count++));
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
public int getCount() {
return count;
}
}
public class Demo00 {
public static void main(String args[]){
SyncThread s = new SyncThread();
Thread t1 = new Thread(s);
Thread t2 = new Thread(s);
t1.start();
t2.start();
}
}
修饰类
给class加锁和上例的给静态方法加锁是一样的,所有对象公用一把锁
class ClassName {
public void method() {
synchronized(ClassName.class) {
}
}
}
注意
1.在定义接口方法时不能使用synchronized关键字;构造方法不能使用synchronized关键字
2.实现同步是要很大的系统开销作为代价的,甚至可能造成死锁,所以尽量避免无谓的同步控制
3.每个对象只有一个锁与之相关联,谁拿到这个锁谁就可以运行它所控制的那段代码
Lock
Lock是一个同步线程机制;
主要方法
lock():获取锁,加锁
tryLock():判断锁是否可用
unlock():释放锁
使用
public class TestThread2 {
public static void main(String[] arge){
Ticket2 ticket = new Ticket2();
new Thread(()->{
for(int i=1;i<40;i++){
ticket.ticket();
}
},"A").start();
new Thread(()->{
for(int i=1;i<40;i++){
ticket.ticket();
}
},"B").start();
new Thread(()->{
for(int i=1;i<40;i++){
ticket.ticket();
}
},"C").start();
}
}
class Ticket2{
private int num = 20;
void ticket(){
Lock lock = new ReentrantLock();
lock.lock();
try{
if(num > 0){
System.out.println(Thread.currentThread().getName()+"剩余票数:"+num--);
}
}catch (Exception e){
e.printStackTrace();
}finally {
lock.unlock();
}
}
}
ReentrantLock
重入锁也叫做递归锁,指的是同一线程 外层函数获得锁之后 ,内层递归函数仍然有获取该锁的代码,但不受影响。避免死锁问题
public class ReentrantDemo implements Runnable {
Lock lock = new ReentrantLock();
@Override
public void run() {
set();
}
public void set() {
try {
lock.lock();
System.out.println("set 方法");
get();
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();// 必须在finally中释放
}
}
public void get() {
try {
lock.lock();
System.out.println("get 方法");
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
public static void main(String[] args) {
ReentrantDemo reentrantDemo = new ReentrantDemo();
new Thread(reentrantDemo).start();
}
}
同一个线程,首先在set方法中获取锁,然后调用get方法,get方法中重复获取同一个锁。两个方法都执行成功
ReentrantReadWriteLock
读写锁,可以分别获取读锁或写锁。
特点
读锁使用共享模式;写锁使用独占模式;读锁可以在没有写锁的时候被多个线程同时持有,写锁是独占的。当有读锁时,写锁就不能获得;而当有写锁时,除了获得写锁的这个线程可以获得读锁外,其他线程不能获得读锁
常用方法
writeLock():获取写锁
readLock():获取读锁
区别
1.synchronized内置关键字;lock是一个Java类
2.synchronized无法判断锁状态;lock可以判断是否获取到锁
3.synchronized会自动释放锁;lock必须手动释放锁,不释放会造成死锁
4.synchronized可重入锁,不可以中断,非公平;lock可重入锁,可以判断锁,非公平(可以设置)
5.synchronized适合锁少量的代码同步问题;lock适合锁大量的同步代码