class Duck {
private synchronized void swim() {
System.out.println(Thread.currentThread().getName() + "游泳游泳");
sound();
}
private synchronized void sound() {
System.out.println(Thread.currentThread().getName() + "叫唤");
}
}
class Bird {
Lock mLock = new ReentrantLock();
void fly() {
mLock.lock();
System.out.println(Thread.currentThread().getName() + "fly fly~~");
try {
TimeUnit.SECONDS.sleep(2);
} catch (InterruptedException e) {
e.printStackTrace();
}
eat();
mLock.unlock();
}
private void eat() {
mLock.lock();
System.out.println(Thread.currentThread().getName() + "eat eat~~");
mLock.unlock();
}
}
public class LockTest {
//可重入锁
public static void main(String args[]) {
Duck duck = new Duck();
new Thread(duck::swim, "t1").start();
new Thread(duck::swim, "t2").start();
Bird bird = new Bird();
new Thread(bird::fly, "t3").start();
new Thread(bird::fly, "t4").start();
}
}
t1游泳游泳
t1叫唤
t2游泳游泳
t2叫唤
t3fly fly~~
t3eat eat~~
t4fly fly~~
t4eat eat~~Process finished with exit code 0
值得注意的是,使用Lock后,一段代码可以重复进行加锁,但加锁后一定要匹配释放锁,否则少一个释放锁线程不会结束。
public class MySpinLock {
private AtomicReference<Thread> mThreadAtomicReference = new AtomicReference<>();
private volatile AtomicInteger version;
private volatile AtomicInteger count;
public static void main(String[] args) throws InterruptedException {
spinLock1();
}
private static void spinLock1() throws InterruptedException {
MySpinLock mySpinLock = new MySpinLock();
TimeUnit.SECONDS.sleep(1);
new Thread(() -> {
mySpinLock.myLock();
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
mySpinLock.myUnlock();
}
}, "t1").start();
new Thread(() -> {
mySpinLock.myLock();
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
mySpinLock.myUnlock();
}
}, "t2").start();
}
private void myLock() {
Thread thread = Thread.currentThread();
System.out.println(thread.getName() + "lock");
while (!mThreadAtomicReference.compareAndSet(null, thread)) {
}
}
private void myUnlock() {
Thread thread = Thread.currentThread();
mThreadAtomicReference.compareAndSet(thread, null);
System.out.println(thread.getName() + "unlock");
}
}
读写锁,只要包含写操作都应该是独占的,读与读之间可以共享。写的时候加写锁,必须保证线程互斥,不能有其他线程干扰,读的时候可以共享,所以是共享锁。
也就是说,读锁可以在没有写锁的时候被多个线程同时持有,而写锁是独占读写锁的。当有读锁时,写锁就不能获得;而当有写锁时,除了已经获得写锁的这个线程外,其他线程不能获取该读写锁的任何读锁。
读写锁升降级:
//资源类
class MyCache {
private volatile Map<String, Object> map = new HashMap<>();
private ReentrantReadWriteLock rwLock = new ReentrantReadWriteLock();
public void put(String key, Object value) {
rwLock.writeLock().lock();
try {
System.out.println(Thread.currentThread().getName() + "正在写入");
try {
Thread.sleep(300);
} catch (Exception e) {
e.printStackTrace();
}
map.put(key, value);
System.out.println(Thread.currentThread().getName() + "写入完成");
} catch (Exception e) {
e.printStackTrace();
} finally {
rwLock.writeLock().unlock();
}
}
//读共享且时间段不一样
public void get(String key) {
rwLock.readLock().lock();
try {
System.out.println(Thread.currentThread().getName() + "正在读取");
try {
Thread.sleep(300);
} catch (Exception e) {
e.printStackTrace();
}
Object res = map.get(key);
System.out.println(Thread.currentThread().getName() + "读取完成:" + res);
} catch (Exception e) {
e.printStackTrace();
} finally {
rwLock.readLock().unlock();
}
}
}
public class MyReadWriteLock {
public static void main(String[] args) {
MyCache myCache = new MyCache();
for (int i = 0; i < 5; i++) {
int finalI = i;
new Thread(() -> {
myCache.put(finalI + "", finalI);
}, "t" + i).start();
}
for (int i = 5; i < 10; i++) {
int finalI = i;
new Thread(() -> {
myCache.get((finalI - 5) + "");
}, "t" + i).start();
}
}
}
非同步代码:
public class CountDownLatchTest {
public static void main(String[] args) {
for (int i = 0; i < 6; i++) {
new Thread(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + "上完自习走人,离开教室");
}
},"t" + i).start();
}
new Thread(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + "班长上完自习走人,锁上门窗,离开教室");
}
},"班长").start();
}
}
t1上完自习走人,离开教室
t0上完自习走人,离开教室
t2上完自习走人,离开教室
t4上完自习走人,离开教室
t5上完自习走人,离开教室
班长班长上完自习走人,锁上门窗,离开教室
t3上完自习走人,离开教室Process finished with exit code 0
使用CountDownLatch进行计数同步后,前六个线程执行完(顺序随机)才能执行被CountDownLatch锁住的线程:
public class CountDownLatchTest {
public static void main(String[] args) {
CountDownLatch countDownLatch = new CountDownLatch(6);
for (int i = 0; i < 6; i++) {
new Thread(() -> {
System.out.println(Thread.currentThread().getName() + "上完自习走人,离开教室");
countDownLatch.countDown();
},"t" + i).start();
}
new Thread(() -> {
try {
countDownLatch.await();
System.out.println(Thread.currentThread().getName() + "班长上完自习走人,锁上门窗,离开教室");
} catch (InterruptedException e) {
e.printStackTrace();
}
},"班长").start();
}
}
t0上完自习走人,离开教室
t3上完自习走人,离开教室
t2上完自习走人,离开教室
t1上完自习走人,离开教室
t4上完自习走人,离开教室
t5上完自习走人,离开教室
班长班长上完自习走人,锁上门窗,离开教室Process finished with exit code 0
同理,和CountDownLatch类似,不过它是做加法:
public class CyclicBarrierTest {
public static void main(String[] args) {
CyclicBarrier cyclicBarrier = new CyclicBarrier(6, new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + "班长上完自习走人,锁上门窗,离开教室");
}
});
for (int i = 0; i < 6; i++) {
new Thread(() -> {
try {
System.out.println(Thread.currentThread().getName() + "上完自习走人,离开教室");
cyclicBarrier.await();//在此阻塞班长线程
} catch (BrokenBarrierException e) {
e.printStackTrace();
} catch (InterruptedException e) {
e.printStackTrace();
}
},"t" + i).start();
}
}
}
t1上完自习走人,离开教室
t0上完自习走人,离开教室
t3上完自习走人,离开教室
t4上完自习走人,离开教室
t5上完自习走人,离开教室
t2上完自习走人,离开教室
t2班长上完自习走人,锁上门窗,离开教室Process finished with exit code 0
抢车位,车位资源有限,先抢到先停,停满了后面的排队等里面的释放。
public class SemaphoreTest {
public static void main(String[] args) {
//模拟3个停车位
Semaphore semaphore = new Semaphore(3);
for (int i = 0; i < 6; i++) {
new Thread(new Runnable() {
@Override
public void run() {
try {
semaphore.acquire();
System.out.println(Thread.currentThread().getName() + "抢到车位");
Thread.sleep(300);
System.out.println(Thread.currentThread().getName() + "停车后离开");
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
semaphore.release();
}
}
}, "t" + i).start();
}
}
}
t0抢到车位
t2抢到车位
t1抢到车位
t0停车后离开
t1停车后离开
t2停车后离开
t3抢到车位
t4抢到车位
t5抢到车位
t3停车后离开
t5停车后离开
t4停车后离开Process finished with exit code 0
死锁是指两个或两个以上的进程在执行过程中,因争夺资源而造成的一种互相等待的现象,若无外力干涉那他们都无法推进下去,如果系统资源充足,进程的资源请求都能得到满足,死锁出现的概率就很低。
代码验证:
class HoldLockThread implements Runnable {
private String lockA;
private String lockB;
public HoldLockThread(String lockA, String lockB) {
this.lockA = lockA;
this.lockB = lockB;
}
@Override
public void run() {
synchronized (lockA) {
System.out.println(Thread.currentThread().getName() + ", 自己持有" + lockA + "尝试获得" + lockB);
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (lockB) {
System.out.println(Thread.currentThread().getName() + ", 自己持有" + lockB + "尝试获得" + lockA);
}
}
}
}
public class DeadLockTest {
public static void main(String[] args) {
String lockA = "lockA";
String lockB = "lockB";
new Thread(new HoldLockThread(lockA, lockB)).start();
new Thread(new HoldLockThread(lockB, lockA)).start();
}
}
死锁排查:
jps -l查看所有java进程
找到当前程序的进程号:jstack {进程号},就可以看到栈信息,有没有死锁了。
解决办法:重启程序、优化代码,避免出现死锁。