最进找了一些java关于java多线程的题目,觉得还比较有意思,还可以帮助我们理解多线程的一些理论知识。相比于枯燥的理论知识,一些例子更能让我们在更深层次的理解和运用。
题目一
- 多线程之间按顺序调用实现A->B->C;
- AA打印5次,BB打印10次,CC打印15次
- 循环打印10遍
- synchronized关键字与 wait() 和 notify/notifyAll() 方法相结合可以实现 等待/通知机制,ReentrantLock类当然也可以实现,但是需要借助于Condition接口与newCondition() 方法。Condition是JDK1.5之后才有的,它具有很好的灵活性,比如可以实现多路通知功能也就是在一个Lock对象中可以创建多个Condition实例(即对象监视器),线程对象可以注册在指定的Condition中,从而可以有选择性的进行线程通知,在调度线程上更加灵活。
- 在使用notify/notifyAll()方法进行通知时,被通知的线程是由JVM选择的,使用ReentrantLock类结合Condition实例可以实现“选择性通知”,这个功能非常重要,而且是Condition接口默认提供的。
而synchronized关键字就相当于整个Lock对象中只有一个Condition实例,所有的线程都注册在它一个身上。如果执行notifyAll()方法的话就会通知所有处于等待状态的线程这样会造成很大的效率问题,而Condition实例的signalAll()方法 只会唤醒注册在该Condition实例中的所有等待线程- 本题要求按顺序打印,涉及到多线程之间的通信问题,而且需要实现选择性通知,我们采用Condition接口来实现
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
class PrintR{
//初始标志位为一
private int signal = 1;
private Lock syc = new ReentrantLock();
//创建三种情况的“监视器”
private Condition condition1 = syc.newCondition();
private Condition condition2 = syc.newCondition();
private Condition condition3 = syc.newCondition();
public void print5(){
syc.lock();
try {
//此处不要使用if判断
while (signal != 1){
//标志位不为1时,就处于等待状态
condition1.await();
}
for (int i = 1; i <= 5; i++) {
System.out.println(Thread.currentThread().getName()+"\t"+i);
}
//操作完成后修改标志位
signal =2;
//精准唤醒
condition2.signal();
} catch (Exception e) {
e.printStackTrace();
} finally {
syc.unlock();
}
}
public void print10(){
syc.lock();
try {
while (signal != 2){
condition2.await();
}
for (int i = 1; i <= 10; i++) {
System.out.println(Thread.currentThread().getName()+"\t"+i);
}
signal =3;
condition3.signal();
} catch (Exception e) {
e.printStackTrace();
} finally {
syc.unlock();
}
}
public void print15(){
syc.lock();
try {
while (signal != 3){
condition3.await();
}
for (int i = 1; i <= 15; i++) {
System.out.println(Thread.currentThread().getName()+"\t"+i);
}
signal =1;
condition1.signal();
} catch (Exception e) {
e.printStackTrace();
} finally {
syc.unlock();
}
}
}
public class PrintNumberAndLetter {
public static void main(String[] args) {
//资源类
PrintR print = new PrintR();
//创建A,B,C三线程
new Thread(()->{for (int i = 0; i < 10; i++)print.print5();},"A").start();
new Thread(()->{for (int i = 0; i < 10; i++)print.print10();},"B").start();
new Thread(()->{for (int i = 0; i < 10; i++)print.print15();},"C").start();
}
}
题目二
- 题目:现在两个线程,可以操作初始值为零的一个变量,
- 实现一个线程对该变量加一,一个线程对该对象减一
- 实现交替10轮,变量初始值为零。
此题可以算是上题的简化版,我个人认为可以使用传统的synchronized关键字与 wait() 和 notify/notifyAll() 方法相结合可以实现,ReentrantLock类当然也可以实现,但是需要借助于Condition接口与newCondition() 方法。在此处两种方法都列出来了。并且我认为4个线程操作也是不会出问题的。
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
class Resource{
private int num = 0;
private Lock lock = new ReentrantLock();
private Condition condition = lock.newCondition();
public void add() throws Exception {
//老版本synchronized配合wait+notify
/*//判断
while (num!=0){
this.wait();
}
//干活
num++;
System.out.println(Thread.currentThread().getName()+"\t"+num);
//通知
this.notifyAll();*/
//新版本lock+condition的await+signal
lock.lock();
try {
while (num!=0){
condition.await();
}
num++;
System.out.println(Thread.currentThread().getName()+"\t"+num);
condition.signalAll();
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
public void cut() throws Exception {
/*//判断
while (num ==0 ){
this.wait();
}
//干活
num--;
System.out.println(Thread.currentThread().getName()+"\t"+num);
//通知
this.notifyAll();*/
lock.lock();
try {
while (num==0){
condition.await();
}
num--;
System.out.println(Thread.currentThread().getName()+"\t"+num);
condition.signalAll();
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
}
public class ThreadWaitNotify {
public static void main(String[] args) {
Resource resource = new Resource();
new Thread(()->{
for (int i = 0; i < 10; i++) {
try {
resource.add();
} catch (Exception e) {
e.printStackTrace();
}
}
},"A").start();
new Thread(()->{
for (int i = 0; i < 10; i++) {
try {
resource.add();
} catch (Exception e) {
e.printStackTrace();
}
}
},"C").start();
/*new Thread(()->{
for (int i = 0; i < 10; i++) {
try {
resource.cut();
} catch (Exception e) {
e.printStackTrace();
}
}
},"B").start();
new Thread(()->{
for (int i = 0; i < 10; i++) {
try {
resource.cut();
} catch (Exception e) {
e.printStackTrace();
}
}
},"D").start();*/
}
}
题目三
- 3个售票员同时卖出100张票,且不能出现超卖、少买
这是多线程问题中的典型案例,有一定的代表性。同样有两套解决方案。我只使用了一种Lock的方式实现。
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
class Ticket {
private int num = 100;
private Lock lock = new ReentrantLock();
public void sell(){
lock.lock();
try {
if (num > 0){
System.out.println(Thread.currentThread().getName()+"现在售出第"+num+"张票"+"\t还剩下"+--num+"张票");
}
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
//抽取模板
public void sellT(){
lock.lock();
try {
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
}
public class SaleTicket {
public static void main(String[] args) {
Ticket ticket = new Ticket();
new Thread(()->{
for (int i = 0; i < 110; i++) {
ticket.sell();
}
},"A").start();
new Thread(()->{
for (int i = 0; i < 110; i++) {
ticket.sell();
}
},"B").start();
new Thread(()->{
for (int i = 0; i < 110; i++) {
ticket.sell();
}
},"C").start();
}
}
题目四
- 实现一个读写锁的应用实例
- ReentrantLock(排他锁)具有完全互斥排他的效果,即同一时刻只允许一个线程访问,这样做虽然虽然保证了实例变量的线程安全性,但效率非常低下。ReadWriteLock接口就是为了解决这个问题。
读写锁维护了两个锁,一个是读操作相关的锁也成为共享锁,一个是写操作相关的锁 也称为排他锁。通过分离读锁和写锁,其并发性比一般排他锁有了很大提升。ReentrantReadWriteLock实现了上面讲的ReadWriteLock接口。读写锁的原则是读读可共存,读写不可共存,写写不可共存。- 不过要注意的是,如果有一个线程已经占用了读锁,则此时其他线程如果要申请写锁,则申请写锁的线程会一直等待释放读锁。如果有一个线程已经占用了写锁,则此时其他线程如果申请写锁或者读锁,则申请的线程会一直等待释放写锁。
class MapDemo{
private Map map = new HashMap<String,String>();
private ReadWriteLock lock = new ReentrantReadWriteLock();
public void addV(){
lock.writeLock().lock();
try {
System.out.println(Thread.currentThread().getName()+"\t---开始写入");
Thread.sleep(200);
map.put(Thread.currentThread().getName(),Thread.currentThread().getName());
System.out.println(Thread.currentThread().getName()+"\t---写入完成");
} catch (InterruptedException e) {
e.printStackTrace();
}finally {
lock.writeLock().unlock();
}
}
public void getV(){
lock.readLock().lock();
try {
System.out.println(Thread.currentThread().getName()+"\t开始读取");
Thread.sleep(200);
String o = (String) map.get(Thread.currentThread().getName());
System.out.println(Thread.currentThread().getName()+"\t读取完成"+o);
} catch (InterruptedException e) {
e.printStackTrace();
}finally {
lock.readLock().unlock();
}
}
}
public class ReadWriteLockDemo {
public static void main(String[] args) {
MapDemo mapDemo = new MapDemo();
for (int i = 1; i <=5 ; i++) {
new Thread(()->{
mapDemo.addV();
},String.valueOf(i)).start();
}
for (int i = 1; i <=5 ; i++) {
new Thread(()->{
mapDemo.getV();
},String.valueOf(i)).start();
}
}
}
文章参考:https://blog.csdn.net/qq_39150049/article/details/112652288
尚硅谷课程