并发编程[2]—各种并发类,CAS,各种锁

一、常用的并发工具类

1.1 CountDownLatch

作用:是一组线程等待其他的线程完成工作以后在执行,加强版join
await用来等待,countDown负责计数器的减一

CountDownLatch代码演示:

package com.thread;

import java.util.concurrent.CountDownLatch;

/**
 * 类说明:演示CountDownLatch,有5个初始化的线程,6个扣除点,
 * 扣除完毕以后,主线程和业务线程才能继续自己的工作
 */
public class UseCountDownLatch {
    static CountDownLatch latch = new CountDownLatch(6);

    //初始化线程(只有一步,有4个)
    private static class InitThread implements Runnable {
        @Override
        public void run() {
            System.out.println("InitThread_" + Thread.currentThread().getId());
            latch.countDown(); //初始化线程完成工作了,countDown方法只扣减一次;
            System.out.println("InitThread_" + Thread.currentThread().getId() + " countDown");
        }
    }

    //业务线程
    private static class BusiThread implements Runnable {
        @Override
        public void run() {
            try {
                latch.await();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            for (int i = 0; i < 3; i++) {
                System.out.println("BusiThread_" + Thread.currentThread().getId()
                        + " do business-----");
            }
        }
    }

    public static void main(String[] args) throws InterruptedException {
        //单独的初始化线程,初始化分为2步,需要扣减两次
        new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    Thread.sleep(1000);
                    System.out.println("begin step 1nd.......");
                    System.out.println("Thread_" + Thread.currentThread().getId());
                    latch.countDown();//每完成一步初始化工作,扣减一次
                    System.out.println("Thread_" + Thread.currentThread().getId()
                            + " countDown...1");
                    System.out.println("begin step 2nd.......");
                    Thread.sleep(1000);
                    latch.countDown();//每完成一步初始化工作,扣减一次
                    System.out.println("Thread_" + Thread.currentThread().getId()
                            + " countDown...2");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }).start();
        new Thread(new BusiThread()).start();
        for (int i = 0; i <= 3; i++) {
            Thread thread = new Thread(new InitThread());
            thread.start();
        }
        latch.await();
        System.out.println("Main do ites work........");
    }
}

输出结果如下:

"C:\Program Files\Java\jdk1.8.0_131\bin\java" "-javaagent:D:\IntelliJ IDEA 2017.2.6\lib\idea_rt.jar=2491:D:\IntelliJ IDEA 2017.2.6\bin" -Dfile.encoding=UTF-8 -classpath "C:\Program Files\Java\jdk1.8.0_131\jre\lib\charsets.jar;C:\Program Files\Java\jdk1.8.0_131\jre\lib\deploy.jar;C:\Program Files\Java\jdk1.8.0_131\jre\lib\ext\access-bridge-64.jar;C:\Program Files\Java\jdk1.8.0_131\jre\lib\ext\cldrdata.jar;C:\Program Files\Java\jdk1.8.0_131\jre\lib\ext\dnsns.jar;C:\Program Files\Java\jdk1.8.0_131\jre\lib\ext\jaccess.jar;C:\Program Files\Java\jdk1.8.0_131\jre\lib\ext\jfxrt.jar;C:\Program Files\Java\jdk1.8.0_131\jre\lib\ext\localedata.jar;C:\Program Files\Java\jdk1.8.0_131\jre\lib\ext\nashorn.jar;C:\Program Files\Java\jdk1.8.0_131\jre\lib\ext\sunec.jar;C:\Program Files\Java\jdk1.8.0_131\jre\lib\ext\sunjce_provider.jar;C:\Program Files\Java\jdk1.8.0_131\jre\lib\ext\sunmscapi.jar;C:\Program Files\Java\jdk1.8.0_131\jre\lib\ext\sunpkcs11.jar;C:\Program Files\Java\jdk1.8.0_131\jre\lib\ext\zipfs.jar;C:\Program Files\Java\jdk1.8.0_131\jre\lib\javaws.jar;C:\Program Files\Java\jdk1.8.0_131\jre\lib\jce.jar;C:\Program Files\Java\jdk1.8.0_131\jre\lib\jfr.jar;C:\Program Files\Java\jdk1.8.0_131\jre\lib\jfxswt.jar;C:\Program Files\Java\jdk1.8.0_131\jre\lib\jsse.jar;C:\Program Files\Java\jdk1.8.0_131\jre\lib\management-agent.jar;C:\Program Files\Java\jdk1.8.0_131\jre\lib\plugin.jar;C:\Program Files\Java\jdk1.8.0_131\jre\lib\resources.jar;C:\Program Files\Java\jdk1.8.0_131\jre\lib\rt.jar;E:\projectCode\springBoot\target\classes" com.thread.UseCountDownLatch
InitThread_14
InitThread_16
InitThread_15
InitThread_16 countDown
InitThread_14 countDown
InitThread_17
InitThread_15 countDown
InitThread_17 countDown
begin step 1nd.......
Thread_12
Thread_12 countDown...1
begin step 2nd.......
Thread_12 countDown...2
Main do ites work........
BusiThread_13 do business-----
BusiThread_13 do business-----
BusiThread_13 do business-----

Process finished with exit code 0

latch.await();类似于是一把自动锁,只有CountDownLatch的扣除数被扣除完毕后才能继续执行await()方法后面的代码。

1.2 CyclicBarrier

让一组线程达到某个屏障,被阻塞,一直到组内最后一个线程达到屏障时,屏障开放,所有被阻塞的线程会继续运行CyclicBarrier(int parties)
CyclicBarrier(int parties, Runnable barrierAction),屏障开放,barrierAction定义的任务会执行
CountDownLatch和CyclicBarrier辨析
1、countdownlatch放行由第三者控制,CyclicBarrier放行由一组线程本身控制
2、countdownlatch放行条件》=线程数,CyclicBarrier放行条件=线程数

package com.thread;

import java.util.Map;
import java.util.Random;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CyclicBarrier;

public class UseCyclicBarrier {
    private static CyclicBarrier barrier
            = new CyclicBarrier(5, new CollectThread());

    private static ConcurrentHashMap<String, Long> resultMap
            = new ConcurrentHashMap<>();//存放子线程工作结果的容器

    public static void main(String[] args) {
        for (int i = 0; i <= 4; i++) {
            Thread thread = new Thread(new SubThread());
            thread.start();
        }

    }

    //负责屏障开放以后的工作
    private static class CollectThread implements Runnable {
        @Override
        public void run() {
            StringBuilder result = new StringBuilder();
            for (Map.Entry<String, Long> workResult : resultMap.entrySet()) {
                result.append("[" + workResult.getValue() + "]");
            }
            System.out.println(" the result = " + result);
            System.out.println("do other business........");
        }
    }

    //工作线程
    private static class SubThread implements Runnable {

        @Override
        public void run() {
            long id = Thread.currentThread().getId();//线程本身的处理结果
            resultMap.put(Thread.currentThread().getId() + "", id);
            Random r = new Random();//随机决定工作线程的是否睡眠
            try {
                if (r.nextBoolean()) {
                    Thread.sleep(2000 + id);
                    System.out.println("Thread_" + id + " ....do something ");
                }
                System.out.println(id + "....is await");
                barrier.await();
                Thread.sleep(2000 + id);
                System.out.println("Thread_" + id + " ....do its business ");
            } catch (Exception e) {
                e.printStackTrace();
            }

        }
    }
}

"C:\Program Files\Java\jdk1.8.0_131\bin\java" "-javaagent:D:\IntelliJ IDEA 2017.2.6\lib\idea_rt.jar=3110:D:\IntelliJ IDEA 2017.2.6\bin" -Dfile.encoding=UTF-8 -classpath "C:\Program Files\Java\jdk1.8.0_131\jre\lib\charsets.jar;C:\Program Files\Java\jdk1.8.0_131\jre\lib\deploy.jar;C:\Program Files\Java\jdk1.8.0_131\jre\lib\ext\access-bridge-64.jar;C:\Program Files\Java\jdk1.8.0_131\jre\lib\ext\cldrdata.jar;C:\Program Files\Java\jdk1.8.0_131\jre\lib\ext\dnsns.jar;C:\Program Files\Java\jdk1.8.0_131\jre\lib\ext\jaccess.jar;C:\Program Files\Java\jdk1.8.0_131\jre\lib\ext\jfxrt.jar;C:\Program Files\Java\jdk1.8.0_131\jre\lib\ext\localedata.jar;C:\Program Files\Java\jdk1.8.0_131\jre\lib\ext\nashorn.jar;C:\Program Files\Java\jdk1.8.0_131\jre\lib\ext\sunec.jar;C:\Program Files\Java\jdk1.8.0_131\jre\lib\ext\sunjce_provider.jar;C:\Program Files\Java\jdk1.8.0_131\jre\lib\ext\sunmscapi.jar;C:\Program Files\Java\jdk1.8.0_131\jre\lib\ext\sunpkcs11.jar;C:\Program Files\Java\jdk1.8.0_131\jre\lib\ext\zipfs.jar;C:\Program Files\Java\jdk1.8.0_131\jre\lib\javaws.jar;C:\Program Files\Java\jdk1.8.0_131\jre\lib\jce.jar;C:\Program Files\Java\jdk1.8.0_131\jre\lib\jfr.jar;C:\Program Files\Java\jdk1.8.0_131\jre\lib\jfxswt.jar;C:\Program Files\Java\jdk1.8.0_131\jre\lib\jsse.jar;C:\Program Files\Java\jdk1.8.0_131\jre\lib\management-agent.jar;C:\Program Files\Java\jdk1.8.0_131\jre\lib\plugin.jar;C:\Program Files\Java\jdk1.8.0_131\jre\lib\resources.jar;C:\Program Files\Java\jdk1.8.0_131\jre\lib\rt.jar;E:\projectCode\springBoot\target\classes" com.thread.UseCyclicBarrier
16....is await
Thread_12 ....do something 
12....is await
Thread_13 ....do something 
13....is await
Thread_14 ....do something 
14....is await
Thread_15 ....do something 
15....is await
 the result = [12][13][14][15][16]
do other business........
Thread_12 ....do its business 
Thread_13 ....do its business 
Thread_14 ....do its business 
Thread_15 ....do its business 
Thread_16 ....do its business 

Process finished with exit code 0

结果:
必须等所有线程到达barrier.await()方法时,才能执行new CollectThread()的任务

CountDownLatch和CyclicBarrier辨析
1、countdownlatch放行由第三者控制(只要线程中调用相同对象的countDown方法,就会扣减一次),CyclicBarrier放行由一组线程本身控制
2、countdownlatch放行条件》=线程数,CyclicBarrier放行条件=线程数

1.3 Semaphore

控制同时访问某个特定资源的线程数量,用在流量控制
Semaphore代码如下:

package com.thread;
import java.sql.Connection;
import java.util.LinkedList;
import java.util.concurrent.Semaphore;

/**
 * 类说明:演示Semaphore用法,一个数据库连接池的实现
 */
public class DBPoolSemaphore {
    private final static int POOL_SIZE = 10;
    private final Semaphore useful,useless;//useful表示可用的数据库连接,useless表示已用的数据库连接

    public DBPoolSemaphore() {
        this. useful = new Semaphore(POOL_SIZE);
        this.useless = new Semaphore(0);
    }

    //存放数据库连接的容器
    private static LinkedList<Connection> pool = new LinkedList<Connection>();
    //初始化池
    static {
        for (int i = 0; i < POOL_SIZE; i++) {
            pool.addLast(SqlConnectImpl.fetchConnection());
        }
    }

    /*归还连接*/
    public void returnConnect(Connection connection) throws InterruptedException {
        if(connection!=null) {
            System.out.println("当前有"+useful.getQueueLength()+"个线程等待数据库连接!!"
                    +"可用连接数:"+useful.availablePermits());
            useless.acquire();// 阻塞式方法
            synchronized (pool) {
                pool.addLast(connection);
            }
            useful.release();
        }
    }

    /*从池子拿连接*/
    public Connection takeConnect() throws InterruptedException {
        useful.acquire();
        Connection conn;
        synchronized (pool) {
            conn = pool.removeFirst();
        }
        useless.release();
        return conn;
    }
}

package com.xiangxue.ch2.tools.semaphore;

import java.sql.Array;
import java.sql.Blob;
import java.sql.CallableStatement;
import java.sql.Clob;
import java.sql.Connection;
import java.sql.DatabaseMetaData;
import java.sql.NClob;
import java.sql.PreparedStatement;
import java.sql.SQLClientInfoException;
import java.sql.SQLException;
import java.sql.SQLWarning;
import java.sql.SQLXML;
import java.sql.Savepoint;
import java.sql.Statement;
import java.sql.Struct;
import java.util.Map;
import java.util.Properties;
import java.util.concurrent.Executor;

/**
 *
 *类说明:数据库连接的平庸实现
 */
public class SqlConnectImpl implements Connection{
	// 复写方法太多
}

package com.xiangxue.ch2.tools.semaphore;

import java.sql.Connection;
import java.util.Random;

import com.xiangxue.tools.SleepTools;

/**
 *
 *类说明:测试数据库连接池
 */
public class AppTest {

	private static DBPoolSemaphore dbPool = new DBPoolSemaphore();
	
	//业务线程
	private static class BusiThread extends Thread{
		@Override
		public void run() {
			Random r = new Random();//让每个线程持有连接的时间不一样
			long start = System.currentTimeMillis();
			try {
				Connection connect = dbPool.takeConnect();
				System.out.println("Thread_"+Thread.currentThread().getId()
						+"_获取数据库连接共耗时【"+(System.currentTimeMillis()-start)+"】ms.");
				SleepTools.ms(100+r.nextInt(100));//模拟业务操作,线程持有连接查询数据
				System.out.println("查询数据完成,归还连接!");
				dbPool.returnConnect(connect);
			} catch (InterruptedException e) {
			}
		}
	}
	
	public static void main(String[] args) {
        for (int i = 0; i < 50; i++) {
            Thread thread = new BusiThread();
            thread.start();
        }
	}
	
}

acquire()需要消费一个许可数(阻塞式方法)
release()添加一个许可数

1.4 Exchange

两个线程间的数据交换,
Exchange代码如下:
使用于两个线程间的数据交换

package com.xiangxue.ch2.tools;

import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.concurrent.Exchanger;

/**
 *
 *类说明:Exchange的使用
 */
public class UseExchange {
    private static final Exchanger<Set<String>> exchange 
    	= new Exchanger<Set<String>>();

    public static void main(String[] args) {

    	//第一个线程
        new Thread(new Runnable() {
            @Override
            public void run() {
            	Set<String> setA = new HashSet<String>();//存放数据的容器
                try {
                	/*添加数据
                	 * set.add(.....)
                	 * */
                	setA = exchange.exchange(setA);//交换set
                	/*处理交换后的数据*/
                } catch (InterruptedException e) {
                }
            }
        }).start();

      //第二个线程
        new Thread(new Runnable() {
            @Override
            public void run() {
            	Set<String> setB = new HashSet<String>();//存放数据的容器
                try {
                	/*添加数据
                	 * set.add(.....)
                	 * set.add(.....)
                	 * */
                	setB = exchange.exchange(setB);//交换set
                	/*处理交换后的数据*/
                } catch (InterruptedException e) {
                }
            }
        }).start();

    }
}

1.5 Callable、Future和FutureTask*

isDone,结束,正常还是异常结束,或者自己取消,返回true;
isCancelled 任务完成前被取消,返回true;
cancel(boolean):
1、任务还没开始,返回false
2、任务已经启动,cancel(true),中断正在运行的任务,中断成功,返回true,cancel(false),不会去中断已经运行的任务
3、任务已经结束,返回false
包含图片和文字的文档的处理:图片(云上),可以用future去取图片,主线程继续解析文字。

Callable、Future和FutureTask 代码如下:
在这里插入图片描述

package com.xiangxue.ch2.future;

import java.util.Random;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;

import com.xiangxue.tools.SleepTools;


/**
 *
 *类说明:演示Future等的使用
 */
public class UseFuture {
	
	/*实现Callable接口,允许有返回值*/
	private static class UseCallable implements Callable<Integer>{

		private int sum;
		@Override
		public Integer call() throws Exception {
			System.out.println("Callable子线程开始计算");
			Thread.sleep(2000);
			for(int i=0;i<5000;i++) {
				sum = sum+i;
			}
			System.out.println("Callable子线程计算完成,结果="+sum);
			return sum;
		}

	}
	
	public static void main(String[] args) 
			throws InterruptedException, ExecutionException {
		
		UseCallable useCallable = new UseCallable();
		FutureTask<Integer> futureTask = new FutureTask<Integer>(useCallable);
		new Thread(futureTask).start();
		Random r = new Random();
		SleepTools.second(1);
		if(r.nextBoolean()) {//随机决定是获得结果还是终止任务
			System.out.println("Get UseCallable result = "+futureTask.get());
		}else {
			System.out.println("中断计算");
			futureTask.cancel(true);
		}
		
	}

}

在这里插入图片描述
Atom(不可分割)
什么是原子操作?如何实现原子操作?
syn基于阻塞的锁的机制,1、被阻塞的线程优先级很高,2、拿到锁的线程一直不释放锁怎么办?3、大量的竞争,消耗cpu,同时带来死锁或者其他安全。
CAS的原理
CAS(Compare And Swap),指令级别保证这是一个原子操作
三个运算符: 一个内存地址V,一个期望的值A,一个新值B
基本思路:如果地址V上的值和期望的值A相等,就给地址V赋给新值B,如果不是,不做任何操作。
循环(死循环,自旋)里不断的进行CAS操作

CAS的问题
A—》B----》A,版本号: A1B2-A3
CAS操作长期不成功,cpu不断的循环,
Jdk中相关原子操作类的使用
AtomicMarkableReference,boolean 有没有动过
AtomicStampedReference 动过几次
在这里插入图片描述
代码如下:

package com;

import java.util.concurrent.atomic.AtomicIntegerArray;

/**
 *
 * 当使用锁开销比较大,可以使用原子操作类
 */
public class AtomicArray {
    static int[] value = new int[] { 1, 2 };
    
    static AtomicIntegerArray ai = new AtomicIntegerArray(value);
    
    public static void main(String[] args) {
    	ai.getAndSet(0, 3);
    	System.out.println(ai.get(0));
    	System.out.println(value[0]);

    }
}

package com;

import java.util.concurrent.atomic.AtomicInteger;

public class UseAtomicInt {
	
	static AtomicInteger ai = new AtomicInteger(10);
	
    public static void main(String[] args) {
    	System.out.println(ai.getAndIncrement());//10--->11
    	System.out.println(ai.incrementAndGet());//11--->12--->out
    	System.out.println(ai.get());
    }
}

package com;

import java.util.concurrent.atomic.AtomicReference;

/*
 *
 *类说明:演示引用类型的原子操作类
 */
public class UseAtomicReference {
	
	static AtomicReference<UserInfo> userRef = new AtomicReference<UserInfo>();
	
    public static void main(String[] args) {
        UserInfo user = new UserInfo("Mark", 15);//要修改的实体的实例
        userRef.set(user);
        
        UserInfo updateUser = new UserInfo("Bill", 17);//要变化的新实例
        userRef.compareAndSet(user, updateUser);
        System.out.println(userRef.get().getName());
        System.out.println(userRef.get().getAge());
        System.out.println(user.getName());
        System.out.println(user.getAge());        
    }
    
    //定义一个实体类
    static class UserInfo {
        private String name;
        private int age;
        public UserInfo(String name, int age) {
            this.name = name;
            this.age = age;
        }
        public String getName() {
            return name;
        }
        public int getAge() {
            return age;
        }
    }

}

package com;

import java.util.concurrent.atomic.AtomicStampedReference;

/**
 *
 *类说明:演示带版本戳的原子操作类
 */
public class UseAtomicStampedReference {
	
	static AtomicStampedReference<String> asr = 
			new AtomicStampedReference<>("Mark",0);
	

    public static void main(String[] args) throws InterruptedException {
    	final int oldStamp = asr.getStamp();//那初始的版本号
    	final String oldReferenc = asr.getReference();
    	
    	System.out.println(oldReferenc+"==========="+oldStamp);
    	
    	Thread rightStampThread = new Thread(new Runnable() {

			@Override
			public void run() {
				System.out.println(Thread.currentThread().getName()
						+"当前变量值:"+oldReferenc+"当前版本戳:"+oldStamp+"-"
						+asr.compareAndSet(oldReferenc, oldReferenc+"Java",
								oldStamp, oldStamp+1));
				
			}
    		
    	});
    	
    	Thread errorStampThread = new Thread(new Runnable() {

			@Override
			public void run() {
				String reference = asr.getReference();
				System.out.println(Thread.currentThread().getName()
						+"当前变量值:"+reference+"当前版本戳:"+asr.getStamp()+"-"
						+asr.compareAndSet(reference, reference+"C",
								oldStamp, oldStamp+1));
				
			}
    		
    	});   	
    	
    	rightStampThread.start();
    	rightStampThread.join();
    	errorStampThread.start();
    	errorStampThread.join();
    	System.out.println(asr.getReference()+"==========="+asr.getStamp());
    	
    }
}

一、显式锁
Lock接口和核心方法
在这里插入图片描述

package com.lock;

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

/**
 * 显示锁的用法
 */
public class LockDemo {
    private Lock lock = new ReentrantLock();
    private int count;

    /**
     * 显式锁
     */
    private void increatNum() {
        lock.lock();
        try {
            count++;
        } finally {
            lock.unlock();
        }
    }

    /**
     * synchronized属于内置锁
     */
    private synchronized void increatNum1() {
        count++;
    }
}


二、lock和synchronized比较
synchronized 代码简洁;
Lock:获取锁可以被中断,超时获取锁,尝试获取锁,读多写少用读写锁;
可重入锁ReentrantLock;
锁的公平和非公平
如果在时间上,先对锁进行获取的请求,一定先被满足,这个锁就是公平的,不满足,就是非公平的。非公平的效率一般来讲更高(假如线程A,B,C分别请求一个锁,如果遇到公平锁的话,A先进入,B被操作系统挂起,C过来时也别挂起,当A执行完毕后,B开始执行,B执行完毕后,C才开始执行;当遇到是非公平锁时,线程A执行完,B这时正在挂起状态,C刚好请求锁,那么C就可以直接拿到锁,B还在挂起)
ReadWriteLock接口和读写锁ReentrantReadWriteLock
ReentrantLock和Syn关键字,都是排他锁,
读写锁:同一时刻允许多个读线程同时访问,但是写线程访问的时候,所有的读和写都被阻塞,最适宜读多写少的情况

package com.lock;
public class GoodsInfo {
    private final String name;
    private double totalMoney;//总销售额
    private int storeNumber;//库存数

    public GoodsInfo(String name, int totalMoney, int storeNumber) {
        this.name = name;
        this.totalMoney = totalMoney;
        this.storeNumber = storeNumber;
    }

    public double getTotalMoney() {
        return totalMoney;
    }

    public int getStoreNumber() {
        return storeNumber;
    }

    public void changeNumber(int sellNumber){
        this.totalMoney += sellNumber*25;
        this.storeNumber -= sellNumber;
    }
}

package com.lock;
public interface GoodsService {
	public GoodsInfo getNum() throws InterruptedException;//获得商品的信息
	public void setNum(int number) throws InterruptedException;//设置商品的数量
}

package com.lock;

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
public class UseRwLock implements GoodsService {
	
    private GoodsInfo goodsInfo;
    private final ReadWriteLock lock = new ReentrantReadWriteLock();
    private final Lock getLock = lock.readLock();//读操作的锁
    private final Lock setLock = lock.writeLock();//写操作的锁

    public UseRwLock(GoodsInfo goodsInfo) {
        this.goodsInfo = goodsInfo;
    }

	@Override
	public GoodsInfo getNum() throws InterruptedException {
		getLock.lock();
		try {
			Thread.sleep(1000);
			return this.goodsInfo;
		}finally {
			getLock.unlock();
		}
		
	}

	@Override
	public void setNum(int number) throws InterruptedException {
		setLock.lock();
		try {
			Thread.sleep(1000);
			goodsInfo.changeNumber(number);
		}finally {
			setLock.unlock();
		}
	}

}

package com.lock;

/**
 *
 *类说明:用内置锁来实现商品服务接口
 */
public class UseSyn implements GoodsService {
	
	private GoodsInfo goodsInfo;
	
	public UseSyn(GoodsInfo goodsInfo) {
		this.goodsInfo = goodsInfo;
	}

	@Override
	public synchronized GoodsInfo getNum() throws InterruptedException {
		Thread.sleep(1000);
		return this.goodsInfo;
	}

	@Override
	public synchronized void setNum(int number) throws InterruptedException {
		Thread.sleep(1000);
		goodsInfo.changeNumber(number);

	}

}

三、了解LockSupport工具
park开头的方法
负责阻塞线程
unpark(Thread thread)方法
负责唤醒线程

Condition接口
用Lock和Condition实现等待通知(墙裂推荐:https://blog.csdn.net/fuyuwei2015/article/details/72602182)

五、各种锁的区别
ReentrantLock 锁的可重入实现(可重入锁:一个线程可以多次获取锁)
当每获取一次锁,都会给state加acquires(代码请求锁的次数)
java.util.concurrent.locks.AbstractQueuedSynchronizer#state
在这里插入图片描述
java.util.concurrent.locks.ReentrantLock.FairSync#tryAcquire
在这里插入图片描述
当每释放一次锁,都要给state减去释放的次数
在这里插入图片描述

公平锁和非公平锁的区别
如果是公平锁获取锁时先要调用此方法hasQueuedThreads(),查看是否有前驱节点,如果有先让前驱节点获取,非公平不会调用此方法、

读写锁(ReentrantReadWriteLock.class)
一个同步器如何表示锁的两个状态??
java.util.concurrent.locks.AbstractQueuedSynchronizer#state
private volatile int state;
该变量是int类型,32位,高16表示读线程,有个本地变量保存读线程访问锁的次数,低16位表示写线程。
写锁可以降低为读锁,但是读锁不能升为写锁。

独占锁和共享锁:
一个锁只能被一个线程占用
一个锁可以被多个线程占用(三元共享同步工具类)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值