版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
JUC
(java.concurrent.util
)详细解析
1.volatile
关键字
volatile
关键字解决了多线程访问同一数据时,数据可见性的问题,但是volatile
并不能保证多个操作的原子性;如果需要保证操作的原子性
,可以使用类似于AtomicInteger
这样的原子变量,或者结合 CAS(compare and swap)
或者synchronized
关键字。
/**
* 这里一共有2个线程 一个main线程 ,一个thread线程
*
* 最开始flag在主存中 ,每个线程都会将主存中的共享数据读在自己对应的缓存 ,操作完之后再写入主存
* 这样的话会有线程安全问题(高内聚 ,低耦合 ,避免虚假唤醒)
*
*解决办法 :
* 1.synchronized(){代码体} //效率低
* 2.volatile 关键字 //多个线程操作共享数据时 内存中的数据是可见的
* (1)但是不能保证变量原子性 Atomic
*(2) volatile不能保证“互斥性”
*/
public class Volatile_demo {
public static void main(String[] args) {
ThreadA t = new ThreadA();
new Thread(t).start(); //调用run方法
while (true){//调用数据底层的代码 ,非常的快
// synchronized (t){ //用sychronized关键字 可以解决共享数据不一致的问题
// if (t.isFlag()){
// System.out.println("-------");
// break;
// }
// }
if (t.isFlag()){
System.out.println("-------");
break;
}
}
}
}
class ThreadA implements Runnable {
private volatile boolean flag = false; //volatile 每次都从主存中读取数据
public boolean isFlag() {
return flag;
}
public void setFlag(boolean flag) {
this.flag = flag;
}
@Override
public void run() {
try{
Thread.sleep(2000);
}catch(Exception e){
System.out.println("exception xxx");
}
flag = true;
System.out.println("flag = "+ isFlag());
}
}
2.Atomic变量
Atomic
底层就是volatile
与CAS
的结合,实际上是一种乐观锁
CAS算法实际上是通过:(假如操作的共享数据是 int a = 1,目的都是将
a = a + 1)
while(true){
//
// source int a = 1
// expect int a = 1
// result int a = 2
// 只有当source = expect值时,当前线程才确定没有其他线程
// 已经修改元数据,此时将result写入主存
if(source == expect) a = a + 1 ;
// 否则什么也不做,继续循环
else continue;
}
3.synhronized
关键字
synhronized
主要可以通过修饰方法或者代码块,给方法或者代码块上锁,保证线程安全,但是效率可能较慢。
public synchronized void fun1(){
//this.wait()必须在while循环中,否则可能造成线程的虚假唤醒
while(condition){
if() this.wait
}
//do something
//唤醒其他线程
this.notifyAll();
}
4.CountDownLatch
闭锁
闭锁主要解决允许一个或者多个线程在其他线程执行完成之前一直处于等待状态。
import java.util.concurrent.CountDownLatch;
/**
* 闭锁 :解决多线程问题中 允许多个线程处于等待状态 await
*/
public class CountDownLatchDemo {
public static void main(String[] args) {
//5为支持等待的线程容量,每完成一个线程-1
CountDownLatch countDownLatch = new CountDownLatch(5);
Latch latch = new Latch(countDownLatch);
long start = System.currentTimeMillis();
for (int i = 0; i < 5; i++) { //这个5必须与上面的latch容量5一致
new Thread(latch).start();
}
//保证上面5个线程都执行完了 主线程才开始执行
try {
countDownLatch.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
long end = System.currentTimeMillis();
System.out.printf("所消耗的时间长度为:%d",end-start);
}
}
class Latch implements Runnable{
private CountDownLatch countDownLatch;
public Latch(CountDownLatch countDownLatch) {
this.countDownLatch = countDownLatch;
}
@Override
public void run() {
synchronized (this) {
try {
for (int i = 0; i < 50000; i++) {
if (i%2==0){
System.out.println(i);
}
}
} finally {
// 这个也必须放在finally中
countDownLatch.countDown();
}
}
}
}
5.ReentrantLock
/**
* 解决线程安全问题的方法有3种
* 1.同步代码块 synchronized{demo}
* 2.同步方法 public sychronized int def(){demo}
* 3.同步锁 jdk1.5之后引入的 通过 ReentrantLock lock()上锁 通过unlock()方法解锁
* unlock()方法必须放在finally{}中,保证必须执行
* lock()是显式
*
* 重点:当多个线程调用共享类不同的方法操作同一个属性时,避免虚假唤醒的方法有2种:
*
* Condition 3个方法 分别对应的this的3个方法:
* await()[CountDownLatch 也有一个await方法,是让线程等待的方法,不一样] ->
* this.wait
* signal() -> notify()
* signalAll() -> notifyAll()
*
* 1. class SourceClass{
* private int num = 0; //方法操作的属性
*
* public [synchronized] void def1(){
*
* while(){
* this.wait
* xxxx---demo
* }
*
* this.notifyAll()
* }
* }
*
* 2.class SourceClass{
* private int num = 0;
* private Lock lock = new ReentrantLock()
* private Condition condition = lock.newCondition();
* private
* try{
* lock.lock()
*
* while{
* condition.await() //堵塞等待
* xxxxxxxx demo
* }
* condition.signalAll() //唤醒
*
* }finally{
* lock.unlock()
* }
* }
*
*/
public class LockSychronizedDemo {
public static void main(String[] args) {
A a = new A();
new Thread(a, "1").start();
new Thread(a, "2").start();
new Thread(a, "3").start();
}
}
class A implements Runnable {
private int ticket = 100;
private Lock lock = new ReentrantLock();
@Override
public void run() {
while (true) {
try {
lock.lock(); //上锁
if (ticket > 0) {
try {
Thread.sleep(20);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "卖出一张票,余票为:" + --ticket);
}
} finally {
lock.unlock(); //解锁 释放资源 切记一定要放在finally中
}
}
}
}
6.静态锁与非静态锁
/**
* 普通synchronized方法的锁是 this 同一个实例在同一时间被同一线程操作
* 静态synchronized方法的锁是 Class.class 与 this锁互不干扰,同时只可以一个线程可以当前类的一个实例
* 普通方法没有锁
*/
public class Thread_8Lock_demo {
public static void main(String[] args) {
TetsDemo tetsDemo = new TetsDemo();
TetsDemo tetsDemo1 = new TetsDemo();
new Thread(new Runnable() {
@Override
public void run() {
tetsDemo.getOne();
}
}).start();
new Thread(new Runnable() {
@Override
public void run() {
tetsDemo1.getTwo();
}
}).start();
// new Thread(new Runnable() {
// @Override
// public void run() {
// tetsDemo.getThree();
// }
// }).start();
}
}
class TetsDemo{
public static synchronized void getOne(){
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("one");
}
public static synchronized void getTwo(){
System.out.println("two");
}
public void getThree(){
System.out.println("three");
}
}
7.线程交替打印的问题
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class JiaoTi_loop_print {
public static void main(String[] args) {
PrintDemo printDemo = new PrintDemo();
new Thread(new Runnable() {
@Override
public void run() {
for (int i = 0; i < 10 ; i++) {
printDemo.printA(i);
}
}
},"A").start();
new Thread(new Runnable() {
@Override
public void run() {
for (int i = 0; i < 10 ; i++) {
printDemo.printB(i);
}
}
},"B").start();
new Thread(new Runnable() {
@Override
public void run() {
for (int i = 0; i < 10 ; i++) {
printDemo.printC(i);
System.out.println("'----------------");
}
}
},"C").start();
}
}
class PrintDemo{
private int num = 1;
private Lock lock = new ReentrantLock();
private Condition condition1 = lock.newCondition();
private Condition condition2 = lock.newCondition();
private Condition condition3 = lock.newCondition();
public void printA(int lun){
lock.lock();
try{
if (num != 1){
try {
condition1.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
for (int i = 1; i <= 5; i++) {
System.out.println(Thread.currentThread().getName()+": "+i+" lunshu:"+lun);
}
num = 2;
condition2.signal();
}finally {
lock.unlock();
}
}
public void printB(int lun){
lock.lock();
try{
if (num != 2){
try {
condition2.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
for (int i = 1; i <= 5; i++) {
System.out.println(Thread.currentThread().getName()+": "+i+" lunshu:"+lun);
}
num = 3;
condition3.signal();
}finally {
lock.unlock();
}
}
public void printC(int lun){
lock.lock();
try{
if (num != 3){
try {
condition3.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
for (int i = 1; i <= 5; i++) {
System.out.println(Thread.currentThread().getName()+": "+i+" lunshu:"+lun);
}
num = 1;
condition1.signal();
}finally {
lock.unlock();
}
}
}