项目主体源码可以从ConcurrenceBasics获得,喜欢的朋友可以点个star~。
/**
* synchronized优化
* 同步代码块中的语句越少越好,比较m1和m2
*/
public class T {
int count = 0;
synchronized void m1(){
//do sth need not sync
try {
TimeUnit.SECONDS.sleep(2);
} catch (InterruptedException e) {
e.printStackTrace();
}
//业务逻辑上只有下面这个语句需要sync,这时就不需要给整个方法上锁
count++;
//do sth need not sync
try {
TimeUnit.SECONDS.sleep(2);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
void m2(){
//do sth need not sync
try {
TimeUnit.SECONDS.sleep(2);
} catch (InterruptedException e) {
e.printStackTrace();
}
//业务逻辑中只有下面这句需要sync,这时不应该给整个方法上锁。
//采用细粒度的锁,可以使线程争用时间变短,从而提高效率。
synchronized (this){
count++;
}
//do sth need not sync
try {
TimeUnit.SECONDS.sleep(2);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
/**
* 锁定某个对象o,如果o的属性发生改变,不影响锁的使用。
* 但是如果o变成另一个对象,则锁定的对象发生改变。
* 应该避免将锁定对象的引用变成另外的对象。
*/
public class T {
Object o = new Object();
void m(){
synchronized (o){
while (true){
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName());
}
}
}
public static void main(String[] args) {
T t = new T();
//启动第一个线程
new Thread(t::m, "t1").start();
try {
TimeUnit.SECONDS.sleep(3);
} catch (InterruptedException e) {
e.printStackTrace();
}
//创建第二个线程
Thread t2 = new Thread(t::m,"t2");
//锁对象发生改变,所以t2线程得以执行,如果注释掉这句话,线程t2将永远都不会有执行的机会
t.o = new Object();
t2.start();
}
}
/**
* 不要以字符串常量作为锁定对象,
* 在下面的例子中,m1和m2其实锁定的是同一个对象,
* 这种情况还会发生比较诡异的现象,比如你用到了一个类库,在该类库中代码锁定了字符串"hello",
* 但是你读不到源码,所以你在自己的代码中也锁定了"hello",这时候就有可能发生非常诡异的死锁阻塞,
* 因为你的程序和你用到的类库不经意间使用了同一把锁。
*/
public class T {
String s1 = "Hello";
String s2 = "Hello";
void m1(){
synchronized (s1){
}
}
void m2(){
synchronized (s2){
}
}
}
/**
* 实现一个容器,提供两个方法,add,size
* 写两个线程,线程1添加10个元素到容器中,线程2实现监控元素的个数,当个数到5时,线程2给出提示并结束。
* 解决方法一:给lists添加volatile之后,t2能够接收通知,但是t2线程的死循环很浪费cpu,如果不用死循环,该怎么做?
* 解决方法二:可以使用wait和notify做到,wait会释放锁,而notify不会释放锁,需要注意的是,运行这种方法,必须要保证
* t2先执行,也就是首先先让t2监听才可以,notify之后,t1必须释放锁,t2退出后也必须notify,通知t1继续执行。
* 解决方法三:使用Latch(门闩)代替wait notify来进行通知,
* 好处是通信方式简单,同时也可以指定等待时间,使用await和countdown方法代替wait和notify,
* 当不涉及同步,只是涉及线程通信的时候,用synchronized + wait/notify就显得太重了,
* 这是应该考虑countdownlatch/cyclicbarrier/semaphore
*/
public class MyContainer {
//添加volatile,使t2能够得到通知
volatile List list = new ArrayList();
public void add(Object o){
list.add(o);
}
public int size(){
return list.size();
}
public static void main(String[] args) {
MyContainer c = new MyContainer();
CountDownLatch latch = new CountDownLatch(1);
new Thread(()->{
System.out.println("t2 start ...");
if(c.size() != 5){
try {
//也可以指定等待时间
//latch.await(5000, TimeUnit.MILLISECONDS);
latch.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("t2 over ...");
}, "t2").start();
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
new Thread(()->{
System.out.println("t1 start...");
for(int i = 0; i < 10; i++){
c.add(new Object());
System.out.println("add" + i);
if(c.size() == 5){
//打开门闩,让t2得以执行
latch.countDown();
}
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}, "t1").start();
}
}
/**
* 使用reentrantlock可以完成synchronized同样的功能
* 需要注意的是,必须要手动释放锁(重点),使用syn锁定的话如果遇到异常,
* jvm会自动释放,但是lock必须手动释放锁,因此经常在finally中进行锁释放。
*/
public class ReentrantLock2 {
Lock lock = new ReentrantLock();
void m1(){
try {
lock.lock();
for (int i = 0; i < 10; i++){
TimeUnit.SECONDS.sleep(1);
System.out.println(i);
}
} catch (InterruptedException e) {
e.printStackTrace();
}finally {
lock.unlock();
}
}
void m2(){
lock.lock();
System.out.println("m2...");
lock.unlock();
}
public static void main(String[] args) {
ReentrantLock2 r2 = new ReentrantLock2();
new Thread(r2::m1).start();
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
new Thread(r2::m2).start();
}
}
/**
* 使用reentrantlock可以进行"尝试锁定"tryLock,这样无法锁定,或者在指定时间内无法锁定,线程可以决定是否继续等待。
*/
public class ReentrantLock3 {
Lock lock = new ReentrantLock();
void m1(){
try {
lock.lock();
for(int i = 0; i < 10; i++){
TimeUnit.SECONDS.sleep(1);
System.out.println(i);
}
}catch (InterruptedException e){
e.printStackTrace();
}finally {
lock.unlock();
}
}
/**
* 使用tryLock进行尝试锁定,不管锁定与否,方法都将继续执行。可以根据tryLock的返回值来判定是否锁定。
* 也可以指定tryLock的时间,由于tryLock(time)抛出异常,所以要注意unclock的处理,必须放到finally中。
*/
void m2(){
boolean locked = lock.tryLock();
System.out.println("m2..." + locked);
if (locked) lock.unlock();
/*boolean locked = false;
try {
locked = lock.tryLock(5, TimeUnit.SECONDS);
System.out.println("m2..." + locked);
} catch (InterruptedException e) {
e.printStackTrace();
}finally {
if(locked) lock.unlock();
}*/
}
public static void main(String[] args) {
ReentrantLock3 r3 = new ReentrantLock3();
new Thread(r3::m1).start();
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
new Thread(r3::m2).start();
}
}
/**
* 使用reentranlock可以调用lockInterruptibly方法,可以对线程interrupt方法做出响应。
* 在一个线程等待锁的过程中,可以被打断。
*/
public class ReentrantLock4 {
public static void main(String[] args) {
Lock lock = new ReentrantLock();
Thread t1 = new Thread(()->{
try {
lock.lock();
System.out.println("t1 start");
TimeUnit.SECONDS.sleep(Integer.MAX_VALUE);
System.out.println("t1 end");
}catch (InterruptedException e){
System.out.println("Interrupted...");
}finally {
lock.unlock();
}
});
t1.start();
Thread t2 = new Thread(()->{
try {
//可以对Interrupt()方法做出响应
lock.lockInterruptibly();
System.out.println("t2 start");
TimeUnit.SECONDS.sleep(5);
System.out.println("t2 end");
}catch (InterruptedException e){
System.out.println("Interrupted...");
}finally {
lock.unlock();
}
});
t2.start();
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
t2.interrupt();
}
}
/**
* reentrantLock实现公平锁
*/
public class ReentrantLock5 extends Thread{
//参数为true表示公平锁
private static ReentrantLock lock = new ReentrantLock(true);
@Override
public void run() {
for (int i = 0; i < 100; i++){
lock.lock();
try {
System.out.println(Thread.currentThread().getName() + "获得锁");
}finally {
lock.unlock();
}
}
}
public static void main(String[] args) {
ReentrantLock5 r5 = new ReentrantLock5();
Thread th1 = new Thread(r5);
Thread th2 = new Thread(r5);
th1.start();
th2.start();
}
}
/**
* 实现功能:写一个固定容器同步容器,拥有put和get方法,以及getCount方法,
* 能够支持2个生产者线程以及十个消费者线程的阻塞调用。
*/
public class MyContainer1<T> {
final private LinkedList<T> linkedList = new LinkedList<>();
final private int MAX = 10; //最多10个元素
private int count = 0;
public synchronized void put(T t){
//这里if和while的区别,如果用if可能会发生这样的问题,
//容器满了,线程a在等待,然后容器空了一个位置,突然来了一个线程b插了进去,
//然后容器满了,线程a收到通知会继续执行下去,因为容器满了产生报错。
//而while不会出现这个问题,因为每次线程消费时,都会判断线程是否已满,然后进行等待。
//if会继续执行这个,while会进行判断。
//wait一般都是和while一起使用。
while (linkedList.size() == MAX){
try {
this.wait();
}catch (InterruptedException e){
e.printStackTrace();
}
}
linkedList.add(t);
++count;
this.notifyAll();//通知消费者线程进行消费
}
public synchronized T get(){
T t = null;
while (linkedList.size() == 0){
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
t = linkedList.removeFirst();
count--;
this.notifyAll();//通知生产者进行生产
return t;
}
public static void main(String[] args) {
MyContainer1<String> c = new MyContainer1<>();
//启动消费者线程
for(int i = 0; i < 10; i++){
new Thread(()->{
for (int j = 0; j < 5; j++)
System.out.println(c.get());
}, "c" + i).start();
}
try {
TimeUnit.SECONDS.sleep(2);
} catch (InterruptedException e) {
e.printStackTrace();
}
//启动生产者
for(int i = 0; i < 2; i++){
new Thread(()->{
for(int j = 0; j < 25; j++)
c.put(Thread.currentThread().getName() + " " + j);
}, "p" + i).start();
}
}
}
/**
* 实现功能:写一个固定容器同步容器,拥有put和get方法,以及getCount方法,
* 能够支持2个生产者线程以及十个消费者线程的阻塞调用。
* 使用Lock和Condition来实现
* 对比两种方法,Condition的方式可以更加精确的指定哪些线程被唤醒。
*/
public class MyContainer2<T> {
final private LinkedList<T> linkedList = new LinkedList<>();
//最多10个元素
final private int MAX = 10;
private int count = 0;
private Lock lock = new ReentrantLock();
//生产者
private Condition producer = lock.newCondition();
//消费者
private Condition consumer = lock.newCondition();
public void put(T t){
try {
lock.lock();
while (linkedList.size() == MAX){
producer.await();
}
linkedList.add(t);
++count;
//通知消费者线程进行消费
consumer.signalAll();
}catch (InterruptedException e){
e.printStackTrace();
}finally {
lock.unlock();
}
}
public T get(){
T t = null;
try {
lock.lock();
while (linkedList.size() == 0){
consumer.await();
}
t = linkedList.removeFirst();
count--;
//通知生产者进行生产
producer.signalAll();
}catch (InterruptedException e){
e.printStackTrace();
}finally {
lock.unlock();
}
return t;
}
public static void main(String[] args) {
MyContainer2<String> c = new MyContainer2<>();
//启动消费者线程
for(int i = 0; i < 10; i++){
new Thread(()->{
for (int j = 0; j < 5; j++)
System.out.println(c.get());
}, "c" + i).start();
}
try {
TimeUnit.SECONDS.sleep(2);
} catch (InterruptedException e) {
e.printStackTrace();
}
//启动生产者线程
for(int i = 0; i < 2; i++){
new Thread(()->{
for (int j = 0; j < 25; j++)
c.put(Thread.currentThread().getName() + " " + j);
}, "p" + i).start();
}
}
}