继续根据大纲整理、
16:Synchronized代码块和锁失效
无论Synchronized锁的是方法还是代码块,本质上还是锁的当前对象。
package io.renren.controller;
/**
* @author zhaokkstart
* @create 2020-02-24 9:42
*/
public class TestSynchronizedObject {
public static void main(String[] args) {
final MyObject myObject_first = new MyObject();
final MyObject myObject_seconde = new MyObject();
new Thread(()->{
myObject_first.print("线程A");
},"A").start();
new Thread(()->{
myObject_seconde.print("线程B");
},"B").start();
}
}
class MyObject {
public void print(String str) {
System.out.println("线程" + Thread.currentThread().getName() + "开始执行");
synchronized (String.class) {
for (int i = 0; i <= 10; i++) {
System.out.println(str + " ." + i + ". ");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
}
}
}
System.out.println("线程" + Thread.currentThread().getName() + "执行结束");
}
}
运行结果:
线程A开始执行
线程A .0.
线程B开始执行
线程A .1.
线程A .2.
线程A .3.
线程A .4.
线程A .5.
线程A .6.
线程A .7.
线程A .8.
线程A .9.
线程A .10.
线程B .0.
线程A执行结束
线程B .1.
线程B .2.
线程B .3.
线程B .4.
线程B .5.
线程B .6.
线程B .7.
线程B .8.
线程B .9.
线程B .10.
线程B执行结束
为什么我锁了String的所有实例化对象,对Myobject没有加锁,他仍会互斥执行呢?
因为 sychoronized
是使用括号里的对象来上锁,而 String.class
不是String 类的全部实例,而是一个对象,是一个类型为 Class 的对象,因此,如果你使用不同的这样的 class 对象来同步,则获得的是不同的锁。
synchronized (str) {
for (int i = 0; i <= 10; i++) {
System.out.println(str + " ." + i + ". ");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
}
}
}
线程A开始执行
线程A .0.
线程B开始执行
线程B .0.
线程A .1.
线程B .1.
线程A .2.
线程B .2.
线程A .3.
线程B .3.
线程A .4.
线程B .4.
线程B .5.
线程A .5.
线程A .6.
线程B .6.
线程B .7.
线程A .7.
线程B .8.
线程A .8.
线程A .9.
线程B .9.
线程A .10.
线程B .10.
线程A执行结束
线程B执行结束
如果的想锁住这段代码,只需要锁住同一个对象即可
synchronized (TestSynchronizedObject.class) {
for (int i = 0; i <= 10; i++) {
System.out.println(str + " ." + i + ". ");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
}
}
}
线程A开始执行
线程A .0.
线程B开始执行
线程A .1.
线程A .2.
线程A .3.
线程A .4.
线程A .5.
线程A .6.
线程A .7.
线程A .8.
线程A .9.
线程A .10.
线程A执行结束
线程B .0.
线程B .1.
线程B .2.
线程B .3.
线程B .4.
线程B .5.
线程B .6.
线程B .7.
线程B .8.
线程B .9.
线程B .10.
线程B执行结束
17:线程间通信
多个线程实现顺序打印,synchronized对应wait().notifiy().notifyAll(),搭配使用,如果wait不与之配合使用会报非法监控异常,从而解释了synchronized锁的是对象,所以将其设计为Object方法,利用lock实现,对应await(),sigbal(),那么countDownLatch等肯定也能实现
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
class ShareResource {
private int number = 1;//1:A 2:B 3:C
private Lock lock = new ReentrantLock();
private Condition cd1 = lock.newCondition();
private Condition cd2 = lock.newCondition();
private Condition cd3 = lock.newCondition();
public void print5(int totalLoopNumber) {
lock.lock();
try {
//判断
while (number != 1) {
cd1.await();
}
//干活
for (int i = 1; i <= 5; i++) {
System.out.println(Thread.currentThread().getName() + "\t totalLoopNumber="
+ totalLoopNumber + "\t i=" + i);
}
//通知
number = 2;
cd2.signal();
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
public void print10(int totalLoopNumber) {
lock.lock();
try {
//判断
while (number != 2) {
cd2.await();
}
//干活
for (int i = 1; i <= 10; i++) {
System.out.println(Thread.currentThread().getName() + "\t totalLoopNumber="
+ totalLoopNumber + "\t i=" + i);
}
//通知
number = 3;
cd3.signal();
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
public void print15(int totalLoopNumber) {
lock.lock();
try {
//判断
while (number != 3) {
cd3.await();
}
//干活
for (int i = 1; i <= 15; i++) {
System.out.println(Thread.currentThread().getName() + "\t totalLoopNumber="
+ totalLoopNumber + "\t i=" + i);
}
//通知
number = 1;
cd1.signal();
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
}
/**
* @author xialei
* @Description: 多线程之间按顺序调用,实现A->B->C
* 三个线程启动,要求如下:
* <p>
* AA打印5次,BB打印10次,CC打印15次
* 接着
* AA打印5次,BB打印10次,CC打印15次
* ......来10轮
*/
public class ThreadOrderAccess {
public static void main(String[] args) {
ShareResource shareResource = new ShareResource();
new Thread(() -> {
for (int i = 1; i <= 10; i++) {
shareResource.print5(i);
}
}, "AA").start();
new Thread(() -> {
for (int i = 1; i <= 10; i++) {
shareResource.print10(i);
}
}, "BB").start();
new Thread(() -> {
for (int i = 1; i <= 10; i++) {
shareResource.print15(i);
}
}, "CC").start();
}
}
18:阻塞式线程安全队列
阻塞:在某些情况下挂起线程,一旦条件满足,被挂起的线程自动唤醒。
LinkedBlockingqueue:一个由链表结构组成的有界阻塞队列
Synchronousqueue:一个不存储元素的阻塞队列
当阻塞队列满时,再往队列add元素会异常
当队列空时,再从队列remove元素会异常
public class BlockingQueueDemo {
public static void main(String[] args) {
BlockingQueue<Object> blockingQueue = new ArrayBlockingQueue<>(3);
blockingQueue.add("1");
blockingQueue.add("2");
blockingQueue.add("3");
blockingQueue.add("4");
}
}
Exception in thread "main" java.lang.IllegalStateException: Queue full
at java.util.AbstractQueue.add(AbstractQueue.java:98)
at java.util.concurrent.ArrayBlockingQueue.add(ArrayBlockingQueue.java:312)
at com.atkk.thread.BlockingQueueDemo.main(BlockingQueueDemo.java:20)
blockingQueue.add("1");
blockingQueue.add("2");
blockingQueue.add("3");
// blockingQueue.add("4");
blockingQueue.remove();
blockingQueue.remove();
blockingQueue.remove();
blockingQueue.remove();
Exception in thread "main" java.util.NoSuchElementException
at java.util.AbstractQueue.remove(AbstractQueue.java:117)
at com.atkk.thread.BlockingQueueDemo.main(BlockingQueueDemo.java:24)
Synchronousqueue为不存储元素,即单个元素队列
package com.kk.thread;
import java.util.List;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.SynchronousQueue;
/**
* @author zhaokkstart
* @create 2020-02-24 13:25
*/
public class BlockingQueueDemo {
public static void main(String[] args) {
BlockingQueue<Object> blockingQueue = new SynchronousQueue<>();
new Thread(() -> {
try {
System.out.println(Thread.currentThread().getName() + "\t put 1");
blockingQueue.put("1");
System.out.println(Thread.currentThread().getName() + "\t put 2");
blockingQueue.put("2");
System.out.println(Thread.currentThread().getName() + "\t put 3");
blockingQueue.put("3");
} catch (InterruptedException e) {
e.printStackTrace();
}
}, "A").start();
new Thread(() -> {
try {
Thread.sleep(5000);
System.out.println(Thread.currentThread().getName()+blockingQueue.take()+"\t get");
Thread.sleep(5000);
System.out.println(Thread.currentThread().getName()+blockingQueue.take()+"\t get");
Thread.sleep(5000);
System.out.println(Thread.currentThread().getName()+blockingQueue.take()+"\t get");
} catch (InterruptedException e) {
e.printStackTrace();
}
}, "B").start();
}
}
19:守护线程和用户线程
守护线程:服务其他的线程
用户线程:程序自定义的线程
那么当主线程退出时,守候子线程会执行完毕吗?不一定执行
package com.kk.thread;
/**
* @author zhaokkstart
* @create 2020-02-24 13:55
*/
public class ThreadException {
public static void main(String[] args) throws InterruptedException {
Thread.setDefaultUncaughtExceptionHandler((thread, throwable) -> {
System.out.printf("线程遇到异常。。。"+thread.getName()+"\n \t", thread.getName(), throwable.getMessage());
});
Thread a = new Thread(() -> {
System.out.printf("线程[name: %s,A:%s]Hello world \n ",);
}, "A");
a.setDaemon(true);
a.start();
}
}
守候线程执行依赖于执行时间
20:线程上下文切换
多线程环境中,
当一个线程的状态由Runnable转换为非Runnable(Blocked、Waiting、Timed_Waiting)时,相应线程的上下文信息(包括cpu的寄存器和程序计数器在某一时间点的内容等)需要被保存,以便相应线程稍后再次进入Runnable状态时能够在之前的执行进度的基础上继续前进。而一个线程从非Runnable状态进入Runnable状态可能涉及恢复之前保存的上下文信息。这个对线程的上下文进行保存和恢复的过程就被称为上下文切换。
原文链接:
https://blog.csdn.net/u014527912/article/details/78939781