相关连接: https://blog.csdn.net/wojiushiwo945you/article/details/42292999

https://www.cnblogs.com/dolphin0520/p/3920373.html

https://www.cnblogs.com/qifengshi/p/6831055.html

相关概念:

1,独占资源和共享资源。这里说的独占和共享,是指是否只有一个线程可以占有这个锁。独占并不是说这个资源只可以lock一次,有可能一个线程可以lock多次,这被叫做可重入。

2.,公平、非公平。一个锁资源是否应该按照线程申请的先后顺序分配,还是允许后申请的线程先获得锁。前者是公平锁,后者叫非公平锁。

3,并发相关的三原则:原子性、可见性、有序性。原子性指一组操作不可分割,要么执行,要么不执行。计算机的rom分高速缓存和主存。Java中线程会有一个工作内存,当变量的值被修改的时候,可能只写入了线程相关的高速缓存,而没有写去主存。这种情况下,其他线程读取不到正确的数值。这样的现象成为变量不可见。有序性是关于Java指令重排的。Java的指令重排可能会引发多线程数据异常,例如 一个 初始化操作在状态变量标记为完成后才执行。

 

volatile 修饰的变量具有可见性,它的修改会被直接写入主存。 java指令重排的时候不会重排volatile语句,写在volatile语句(读或写)之前的语句一定在volatile语句之前执行,写在之后的一定在volatile语句之后执行。

volatile应用场景,标记状态量,double check

 

公平锁举例:new ReentrantLock(true)

非公平锁举例:new ReentrantLock(false)、synchronized

独享锁举例:ReentrantLock、synchronized 

 

注意点:资源的释放需要在finally语句中进行。

 

Java中synchronized关键字锁定的是对象。验证代码如下:

class Demo
{
    public synchronized void  sayHello(){
        try{
            System.out.println("hello start");
            Thread.sleep(100);
            System.out.println("hello end");
        }catch (InterruptedException e){
            e.printStackTrace();
        }
    }

    public synchronized void doAction()
    {
        try{
            System.out.println("put up hand");
            Thread.sleep(100);
            System.out.println("put down hand");
        }catch (InterruptedException e){
            e.printStackTrace();
        }
    }
}

public class Test {
    public static void main(String[] args)
    {
        Demo demo = new Demo();
        Demo demoA = new Demo();
        Thread sayThread = new Thread(new Runnable() {
            @Override
            public void run() {
                demo.sayHello();
            }
        });

        Thread actionThread = new Thread(new Runnable() {
            @Override
            public void run() {
//                demo.doAction();
                demoA.doAction();
            }
        });

        sayThread.start();
        actionThread.start();
    }
}

三种情况:

1、两个线程都使用demo变量,方法用synchronized修饰。 结果: sayHello和doAction一定是一个执行完另一个才执行。

2,两个线程都使用demo变量,方法不使用synchronized修饰。结果:sayHello和doAction可以交叉执行。

3、两个线程分别使用demo和demoA变量,方法用synchronized修饰。 结果: sayHello和doAction可以交叉执行。

 synchronized 和 static 同时修饰一个方法

class Worker
{
    public static synchronized void doWork(String name)
    {
        try {
            System.out.println(name+ " starts a work");
            Thread.sleep(100);
            System.out.println(name+ " finish a work");
        }catch (InterruptedException e)
        {
            e.printStackTrace();
        }
    }
}

public class Test {
    public static void main(String[]args)
    {

        Thread threadA = new Thread(new Runnable() {
            @Override
            public void run() {
                new Worker().doWork("zhangsan");
            }
        });

        Thread threadB = new Thread(new Runnable() {
            @Override
            public void run() {
                new Worker().doWork("lisi");
            }
        });

        threadA.start();
        threadB.start();
    }

}

  

Semaphore 用法:

import java.util.concurrent.Semaphore;

class PreTask implements Runnable
{
    private Semaphore sem;
    private int weight;
    PreTask(Semaphore sem, int weight){
        this.sem = sem;
        this.weight = weight;
    }
    public void run()
    {
        try{
            System.out.println("a pretask starts");
            Thread.sleep(100);
            System.out.println("a pretask is done which weight is "+this.weight);
            
        }catch (InterruptedException e){
            e.printStackTrace();
        }finally{
// 在finally中释放资源
this.sem.release(this.weight);
}
} } class LateTask implements Runnable { private Semaphore sem; private int requireWeight; LateTask(Semaphore sem, int requireWeight) { this.sem = sem; this.requireWeight = requireWeight; } public void run() { try{ this.sem.acquire(this.requireWeight); System.out.println("a latetask starts"); Thread.sleep(100); System.out.println("a latetask is done which requireWeight is "+this.requireWeight); }catch (InterruptedException e){ e.printStackTrace(); } } } public class Test { public static void main(String[] args) { Semaphore semaphore = new Semaphore(0); PreTask preA = new PreTask(semaphore, 1); PreTask preB = new PreTask(semaphore, 3); LateTask late = new LateTask(semaphore, 4); Thread preThreadA = new Thread(preA); Thread preThreadB = new Thread(preB); Thread lateThreadB = new Thread(late); preThreadA.start(); preThreadB.start(); lateThreadB.start(); } }

 输出结果如下:

a pretask starts
a pretask starts
a pretask is done which weight is 3
a pretask is done which weight is 1
a latetask starts
a latetask is done which requireWeight is 4

import java.util.concurrent.Semaphore;

class User implements Runnable
{
    private Semaphore sem;
    private String name;
    User(Semaphore sem, String name){
        this.sem = sem;
        this.name = name;
    }
    public void run()
    {
        try{
            this.sem.acquire(1);
            System.out.println(this.name+" take up a pen");
            Thread.sleep(100);
            System.out.println(this.name+" put down a pen ");
            
        }catch (InterruptedException e){
            e.printStackTrace();
        }finally{
//在finally中释放资源
this.sem.release(1);

} } } public class Test { public static void main(String[] args) { Semaphore penSet = new Semaphore(2); User zhangsan = new User(penSet, "zhangsan"); User lisi = new User(penSet, "lisi"); User wangwu = new User(penSet, "wangwu"); User zhaoliu = new User(penSet, "zhaoliu"); Thread threadZhangsan = new Thread(zhangsan); Thread threadL = new Thread(lisi); Thread threadW = new Thread(wangwu); Thread threadZhaoliu = new Thread(zhaoliu); threadZhangsan.start(); threadL.start(); threadW.start(); threadZhaoliu.start(); } }

 结果:最多同时有两个人在使用铅笔

 Semaphore tryAquire,尝试获取一个数量的资源,这个方法是非阻塞的,返回一个boolean值表示是否获得了资源。

import java.util.concurrent.Semaphore;

class Worker
{
    public void doWork(Semaphore tool, String name)
    {
        if (tool.tryAcquire(1))
        {
            try {
                System.out.println(name+ " starts a work with tool");
                Thread.sleep(100);
                System.out.println(name+ " finish a work and release tool");
            }catch (InterruptedException e)
            {
                e.printStackTrace();
            }finally {
                tool.release();
            }
        }else
        {
            try {
                System.out.println(name+ " starts a work with hand");
                Thread.sleep(500);
                System.out.println(name+ " finish a work ");
            }catch (InterruptedException e)
            {
                e.printStackTrace();
            }finally {
            }
        }

    }
}

public class Test {
    public static void main(String[]args)
    {
        Semaphore tool = new Semaphore(1);

        Thread threadA = new Thread(new Runnable() {
            @Override
            public void run() {
                new Worker().doWork(tool,"zhangsan");
            }
        });

        Thread threadB = new Thread(new Runnable() {
            @Override
            public void run() {
                new Worker().doWork(tool,"lisi");
            }
        });

        threadA.start();
        threadB.start();
    }

}

  

两个人可以同时工作,但同一时间只有一个人使用工具工作,另一个人只能用手。

结果如下:

lisi starts a work with hand
zhangsan starts a work with tool
zhangsan finish a work and release tool
lisi finish a work

 

Java中的volatile关键字说,保证对变量的修改直接写入主存,线程读取变量的时候不会受到线程缓存的影响。轮询语句的条件变量就适合使用volatile关键字修饰    while( ! stop){}。 

volatile并不保证关于变量的操作是原子性的。i++不具有原子性,AtomicInteger 提供了原子操作。

import java.util.ArrayList;
import java.util.concurrent.Semaphore;
import java.util.concurrent.atomic.AtomicInteger;

class Room
{
    //    public static volatile int personCount = 0;
    public static AtomicInteger inc = new AtomicInteger(0);
    public static void personEnter()
    {
        try{
            Thread.sleep(100);
        }catch (InterruptedException e){

        }finally {

        }
//        Room.personCount = Room.personCount+1;
        inc.getAndIncrement();
    }
}

public class Test {
    public static void main(String[]args)
    {
        ArrayList<Thread> threadList = new ArrayList(10);
        for(int i=0; i< 10; i++){
            Thread thread = new Thread(new Runnable() {
                @Override
                public void run() {
                    Room.personEnter();
                }
            });
            threadList.add(thread);
        }

        for (int i =0;i<threadList.size(); i++){
            Thread thread = threadList.get(i);
            thread.start();
        }

        while (Thread.activeCount()>2)
            Thread.yield();
        System.out.println(Room.inc);
    }
}

  

使用volatile的结果是小于等于10的数,使用AtomicInteger结果一定是10

 

ReentrantLock 可重入锁,可重入是针对线程而言的。

import java.util.concurrent.locks.ReentrantLock;

class Door implements Runnable
{
    private ReentrantLock reentrantLock;
    private String name;
    public Door(ReentrantLock reentrantLock,String name)
    {
        this.reentrantLock = reentrantLock;
        this.name = name;
    }

    @Override
    public void run() {
        try{
            Thread.sleep(0);
        }catch (InterruptedException e){

        }finally {

        }
        this.reentrantLock.lock();
        System.out.println(this.name+" lock once");
        this.reentrantLock.lock();
        try{
            Thread.sleep(0);
        }catch (InterruptedException e){

        }finally {

        }
        System.out.println(this.name+" lock twice");
        System.out.println(this.name+" will unlock once");
        this.reentrantLock.unlock();
        System.out.println(this.name+" will unlock twice");
        this.reentrantLock.unlock();
    }
}

public class Test {
    public static void main(String[]args)
    {
        ReentrantLock reentrantLock = new ReentrantLock();

        Thread blackThread = new Thread(new Door(reentrantLock, "blackDoor"));
        Thread whiteThread = new Thread(new Door(reentrantLock, "whiteDoor"));

        blackThread.start();
        whiteThread.start();
    }
}

 结果:

blackDoor lock once
blackDoor lock twice
blackDoor will unlock once
blackDoor will unlock twice
whiteDoor lock once
whiteDoor lock twice
whiteDoor will unlock once
whiteDoor will unlock twice