相关连接:
相关概念:
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 startsa pretask is done which weight is 3a pretask is done which weight is 1a latetask startsa latetask is done which requireWeight is 4import 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 toolzhangsan finish a work and release toollisi 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) { ArrayListthreadList = 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 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 twiceblackDoor will unlock onceblackDoor will unlock twicewhiteDoor lock oncewhiteDoor lock twicewhiteDoor will unlock oncewhiteDoor will unlock twice