volatile关键字
1.不具备“互斥性”。
2.不保证“原子性”。
3.保证共享数据在缓存中的可见性,相较于synchronized是一种较为轻量级的同步策略。
说明:线程运行时,其实是在运行当先线程的副本,运行结束后,将结果覆盖,所以出现两个线程操作(改变)共享数据的情况下,会出现A副本修改了之后没有覆盖,B线程已经开始了对共享数据的操作,这就是”不可见“,就出现了问题。而volatile关键字修饰变量之后就会使得内存中的数据保持可见性,线程副本修改之后立即更新。
原子性
java.util.concurrent.atomic包下提供了很多CAS算法保证原子性的类并且具有内存可见性。
CAS【当内存值等于预估值时(预估值就是再次读取当前变量)就将变量修改】
同步容器类【简介】
ConcurrentHashMap【锁分段机制,默认16个分段,每个分段里有一个HashMap(1.8之后升级成CAS算法,有锁变无锁)】
ConcurrentSkipListSet
ConcurrentSkipListMap【ConcurrentSkipListMap通常优于同步的TreeMap】
CopyOnWriteArrayList 【期望的读和遍历远远大于更新时CopyOnWriteArrayList优于同步的ArrayList】
CopyOnWriteArraySet
闭锁
CountDownLatch,一个同步辅助类,在完成一组正在其他线程中执行的操作之前,它允许一个或多个线程一直等待。【大致意思是,其他线程完成之前,改线程一直等待】
代码样例
package com.yuan.learn;
import java.util.concurrent.CountDownLatch;
public class 闭锁 {
public static void main(String[] args) {
final long start = System.currentTimeMillis();
final CountDownLatch countDownLatch = new CountDownLatch(5);
final LatchDemo latchDemo = new LatchDemo(countDownLatch);
for (int i = 0; i < 5; i++) {
new Thread(latchDemo).start();
}
try {
countDownLatch.await();
} catch (InterruptedException e) {
}
final long end = System.currentTimeMillis();
System.out.println("耗时" + (end - start) + "毫秒");
}
}
class LatchDemo implements Runnable {
/**
* 倒计时锁
*/
private CountDownLatch latch;
public LatchDemo(CountDownLatch latch) {
this.latch = latch;
}
@Override
public synchronized void run() {
try {
for (int i = 0; i < 50000; i++) {
if (i % 2 == 0) {
System.out.println(i);
}
}
} finally {
/**
* 减少闩锁的计数,如果计数达到零,则释放所有等待的线程。如果当前计数大于零,则将其递减。
* 如果新计数为零,则所有等待的线程都将重新启用以进行线程调度。
* 如果当前计数等于零,则不会发生任何事情。
*/
latch.countDown();
}
}
}
Callable<T>
相较于实现Runnable接口的方式,多出了返回值,以及可以抛出异常。
代码样例
package com.yuan.learn;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;
public class CallableLearn {
public static void main(String[] args) {
final CallableDemo callableDemo = new CallableDemo();
//1.需要FutureTask的支持,用于接收结果
final FutureTask<Integer> futureTask = new FutureTask<Integer>(callableDemo);
//2.执行
new Thread(futureTask).start();
try {
//在2执行完之后才会执行 可以用于闭锁
final Integer integer = futureTask.get();
System.out.println("integer = " + integer);
} catch (InterruptedException | ExecutionException e) {
e.printStackTrace();
}
}
}
class CallableDemo implements Callable<Integer> {
private int a = 0;
@Override
public Integer call() throws Exception {
for (int i = 0; i < 100; i++) {
a += i;
}
return a;
}
}
同步锁案例:
ABCABCABCABCABC......交替打印十次
package com.yuan.learn;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class 线程按序交替打印 {
public static void main(String[] args) {
final AlternateDamo ad = new AlternateDamo();
new Thread(new Runnable() {
@Override
public void run() {
for (int i = 0; i < 10; i++) {
ad.loopA(i);
}
}
}, "A").start();
new Thread(new Runnable() {
@Override
public void run() {
for (int i = 0; i < 10; i++) {
ad.loopB(i);
}
}
}, "B").start();
new Thread(new Runnable() {
@Override
public void run() {
for (int i = 0; i < 10; i++) {
ad.loopC(i);
}
}
}, "C").start();
}
}
class AlternateDamo {
/**
* 当前线程的标记
*/
private String sign = "A";
/**
* 同步锁
*/
private Lock lock = new ReentrantLock();
Condition conditionA = lock.newCondition();
Condition conditionB = lock.newCondition();
Condition conditionC = lock.newCondition();
/**
* 打印A线程
*/
public void loopA(int totalloop) {
lock.lock();
try {
//1.判断
if (!"A".equals(sign)) {
conditionA.await();
}
//2.打印
// for (int i = 0; i < 5; i++) {
System.out.println(Thread.currentThread().getName() + "\t" + "" + "\t" + totalloop);
// }
//3.唤醒
sign = "B";
conditionB.signal();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
/**
* 打印B线程
*/
public void loopB(int totalloop) {
lock.lock();
try {
//1.判断
if (!"B".equals(sign)) {
conditionB.await();
}
//2.打印
// for (int i = 0; i < 5; i++) {
System.out.println(Thread.currentThread().getName() + "\t" + "" + "\t" + totalloop);
// }
//3.唤醒
sign = "C";
conditionC.signal();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
/**
* 打印C线程
*/
public void loopC(int totalloop) {
lock.lock();
try {
//1.判断
if (!"C".equals(sign)) {
conditionC.await();
}
//2.打印
// for (int i = 0; i < 5; i++) {
System.out.println(Thread.currentThread().getName() + "\t" + "" + "\t" + totalloop);
// }
//3.唤醒
sign = "A";
conditionA.signal();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
}
读写锁案例
package com.yuan.learn;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
public class 读写锁 {
/**
* 读读 不互斥
* 读写 互斥
* 写写 互斥
*/
public static void main(String[] args) {
final RWDemo rwDemo = new RWDemo();
new Thread(new Runnable() {
@Override
public void run() {
rwDemo.setNum((int) (Math.random() * 101));
}
}, "Write").start();
for (int i = 0; i < 100; i++) {
new Thread(new Runnable() {
@Override
public void run() {
rwDemo.getNum();
}
}, "Read").start();
}
}
}
class RWDemo {
int num = 0;
ReadWriteLock lock = new ReentrantReadWriteLock();
/**
* 读方法用读锁
* @return
*/
public int getNum() {
lock.readLock().lock();
try {
System.out.println(Thread.currentThread().getName() +" " + num);
} finally {
lock.readLock().unlock();
}
return num;
}
/**
* 写方法用写锁
*
* @param num
*/
public void setNum(int num) {
lock.writeLock().lock();
try {
this.num = num;
System.out.println(Thread.currentThread().getName() +" " + num);
} finally {
lock.writeLock().unlock();
}
}
}
线程八锁
1.两个普通同步方法,两个线程,标准打印,结果是【one two】
2.getOne()方法新增sleep(3000),结果是【one two】
3.新增普通方法getThree() 不加 synchronize 并且调用 结果是【Three one two】
4.去掉getThree()方法,两个同步方法,两个线程对象,同时调用,【two one】
5.修改getOne()方法为静态方法 ,一个线程对象调用【two one】
6.getTwo()方法也改为静态同步方法,一个线程对象调用【one two】
7.将getTwo()方法 去掉static ,两个线程对象调用【two one】
8.两个静态方法,俩个独享调用【one two】
结论:非静态方法的锁默认是this,静态方法的默认锁是class实例,无论是几个方法,只要是一个线程对象调用,只有一个方法会拿到所
网友评论