java多线程入门(三)练习题中的使用

171 阅读2分钟

一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第5天,点击查看活动详情

1.死锁

例子每个线程两个锁都要用到,但是每个线程分别拿了一个锁,然后获取另外的锁,但是当前锁得不到不释放。最终造成卡死的现象

public class TestThreadDeadlock implements Runnable {

	private static String lockA = "lockA";
	private static String lockB = "lockB";
	private AtomicInteger num = new AtomicInteger(1);

	@Override
	public void run() {
		if (num.get() % 2 == 1) {
			synchronized (lockA) {
				System.out.println("1获取A锁");
				num.set(2);
				try {
					Thread.sleep(1000);
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
				synchronized (lockB) {
					System.out.println("1获取B锁");
				}
			}
		} else {
			synchronized (lockB) {
				System.out.println("2获取B锁");
				try {
					Thread.sleep(1000);
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
				synchronized (lockA) {
					System.out.println("2获取A锁");
					num.set(1);
				}
			}
		}
	}
    public static void main(String[] args) throws InterruptedException {
    	TestThreadDeadlock deadLock=new TestThreadDeadlock();
    	Thread t1=new Thread(deadLock);
    	Thread t2=new Thread(deadLock);
    	t1.start();
    	Thread.sleep(100);
    	t2.start();
	}
}

效果

image.png

2.join方法说明

  • join 方法可以让线程的并行变为串行
  • join 的作用是放弃当前的程序执行,去执行join的线程直到执行结束
  • join(long) 设置时间过后变并行 这里会释放锁的,sleep不释放的
  • join 放在start前面无意义
  • join 实现原理是wait

3.Semaphore信号量

  • 信号量可以控制并发访问的个数 例如生产5个可以并发的消费5个
  • 信号量和锁之间的区别是锁只有一个线程拿到锁,信号量可以是一批次的
Semaphore semaphore = new Semaphore(10,true);//可以有多少个请求 公平/非公平
semaphore.acquire();  //获取请求 阻塞的
semaphore.release();  //释放请求

一个基于Semaphore客服的例子

4.wait/notify

5.Lock/Condition

可以声明多次:private static Condition c1 = lock.newCondition();

Condition.await();=Object.wait

Condition.await(long,timeUnit);=Object.wait(long)

Condition.signal();=Object.notify()

Condition.signalAll();=Object.notifyAll()

Codition和lock捆绑,wait/notify和同步块捆绑

6.LockSupport

  • LockSupport是一个线程阻塞工具类,所有的方法都是静态方法,
  • 和wait和notify的功能类似,wait/notify只能在同步块里面使用,LockSupport可以让线程在任意位置阻塞
 // 唤醒下一个线程
 LockSupport.unpark(t1);
 // 当前线程阻塞
 LockSupport.park();

7.新建 T1、T2、T3 三个线程,如何保证它们按顺序执行

大概逻辑就是T1执行、唤醒T2执行、唤醒T3执行

  • 根据上面学习用join
T1.start();
T1.join();

T2.start();
T2.join();

T3.start();
T3.join();
  • 根据上面学习用信号量
Semaphore semaphore1=new Semaphore(1);
Semaphore semaphore2=new Semaphore(0);
Semaphore semaphore3=new Semaphore(0);
默认线程1可以拿到一个许可
线程1拿到许可执行,设置线程2
线程2拿到许可执行,设置线程3
线程3执行
  • 根据上面的LockSupport
//执行
//唤醒t2
LockSupport.unpark(t2);
//当前阻塞
LockSupport.park();

//t2先阻塞
LockSupport.park();
//执行
//唤醒t3
LockSupport.unpark(t3);

//t3先阻塞
LockSupport.park();
//执行
//唤醒t1
LockSupport.unpark(t1);
  • 锁加状态
    /**
     * name 打印输出
     * targetNum 目标值的时候才匹配输出
     **/
    private void printLetter(String name, int targetNum) {
       //注意这里的循环是匹配状态后才++的 控制打印的次数
        for (int i = 0; i < times; ) {
            //获取锁
            lock.lock();
            //状态控制当为目标状态的时候输出
            if (state % 3 == targetNum) {
                state++;//另一个线程匹配
                i++;//循环打印的次数+1
                System.out.print(name);
            }
            lock.unlock();
        }
    }