【Java】多线程

题目

面试题如下:

考点

本题考查的是线程通信的问题。更侧重的是考查sync+wait+notify的组合使用。

代码

Solution1: LockSupport

比较优的解法。

import java.util.concurrent.locks.LockSupport;

public class Maureen_LockSupport {
	static Thread t1 = null, t2 = null;

	public static void main(String[] args) throws Exception {
		char[] aI = "1234567".toCharArray();
		char[] aC = "ABCDEFG".toCharArray();

		t1 = new Thread(() -> {
			for (char c : aI) {
				System.out.print(c);
				LockSupport.unpark(t2); // t2线程继续运行
				LockSupport.park(); // 当前线程暂停
			}
		}, "t1");

		t2 = new Thread(() -> {
			for (char c : aC) {
				LockSupport.park();
				System.out.print(c);
				LockSupport.unpark(t1);
			}
		}, "t2");

		t1.start();
		t2.start();
	}
}

运行结果:1A2B3C4D5E6F7G

Solution2:CAS -- 自旋锁


public class Maureen_CAS {
	enum ReadyToRun {
		T1, T2
	}

	static volatile ReadyToRun r = ReadyToRun.T1;

	public static void main(String[] args) {
		char[] aI = "1234567".toCharArray();
		char[] aC = "ABCDEFG".toCharArray();

		new Thread(() -> {
			for (char c : aI) {
				while (r != ReadyToRun.T1) {
					//空转,一直占着CPU,也就是自旋(原地打转)
				}
				System.out.print(c);
				r = ReadyToRun.T2; //每输出一个字符,就将r标记进行修改 
			}
		}, "t1").start();

		new Thread(() -> {
			for (char c : aC) {
				while (r != ReadyToRun.T2) {
				}
				System.out.print(c);
				r = ReadyToRun.T1;
			}
		}, "t2").start();

	}
}

输出结果:1A2B3C4D5E6F7G

Solution3:AtomicInteger

import java.util.concurrent.atomic.AtomicInteger;

public class Maureen_AtomicInteger {

	static AtomicInteger threadNo = new AtomicInteger(1);

	public static void main(String[] args) {
		char[] aI = "1234567".toCharArray();
		char[] aC = "ABCDEFG".toCharArray();

		new Thread(() -> {
			for (char c : aI) {
				while (threadNo.get() != 1) {
				}
				System.out.print(c);
				threadNo.set(2);
			}
		}, "t1").start();

		new Thread(() -> {
			for (char c : aC) {
				while (threadNo.get() != 2) {
				}
				System.out.print(c);
				threadNo.set(1);
			}
		}, "t2").start();
	}
}

输出结果:1A2B3C4D5E6F7G

Solution4:BlockingQueue

import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;

public class Maureen_BlockingQueue {

	static BlockingQueue q1 = new ArrayBlockingQueue(1);
	static BlockingQueue q2 = new ArrayBlockingQueue(1);

	public static void main(String[] args) throws Exception {
		char[] aI = "1234567".toCharArray();
		char[] aC = "ABCDEFG".toCharArray();

		new Thread(() -> {
			for (char c : aI) {
				System.out.print(c);
				try {
					q1.put("ok");
					q2.take();
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			}
		}, "t1").start();

		new Thread(() -> {
			for (char c : aC) {
				try {
					q1.take(); //等着q1中有内容,取出内容再继续运行;如果没有内容,就阻塞
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
				System.out.print(c);
				try {
					q2.put("ok");
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			}
		}, "t2").start();
	}

}

输出结果:1A2B3C4D5E6F7G

Solution5:PipedStream

import java.io.IOException;
import java.io.PipedInputStream;
import java.io.PipedOutputStream;

public class Maureen_PipedStream {

	public static void main(String[] args) throws IOException {
		char[] aI = "1234567".toCharArray();
		char[] aC = "ABCDEFG".toCharArray();

		PipedInputStream input1 = new PipedInputStream();
		PipedInputStream input2 = new PipedInputStream();
		PipedOutputStream output1 = new PipedOutputStream();
		PipedOutputStream output2 = new PipedOutputStream();

		input1.connect(output2);
		input2.connect(output1);

		String msg = "Your Turn";

		new Thread(() -> {
			byte[] buffer = new byte[9];
			try {
				for (char c : aI) {
					input1.read(buffer);
					if (new String(buffer).equals(msg)) {
						System.out.print(c);
					}
					output1.write(msg.getBytes());
				}
			} catch (IOException e) {
				e.printStackTrace();
			}
		}, "t1").start();

		new Thread(() -> {
			byte[] buffer = new byte[9];
			try {
				for (char c : aC) {
					System.out.print(c);
					output2.write(msg.getBytes());
					input2.read(buffer);
					if (new String(buffer).equals(msg)) {
						continue;
					}
				}
			} catch (IOException e) {
				e.printStackTrace();
			}
		}, "t2").start();

	}

}

输出结果:A1B2C3D4E5F6G7。该方法效率很低。

Solution6:sync_wait_notify

①两个线程交替输出,但是无法确保哪个线程先输出,可能会先输出字母


//要使用wait和notify,要对一个对象加锁,才能在其中进行notify和wait的操作
public class Maureen_sync_wait_notify_00 {

	public static void main(String[] args) {
		final Object o = new Object();

		char[] aI = "1234567".toCharArray();
		char[] aC = "ABCDEFG".toCharArray();

		new Thread(() -> {
			synchronized (o) {// 不能使用this,因为代码块中是一个匿名内部类,如果用this,那么和下面代码块的this不是同一个对象,不能构成同步
				for (char c : aI) {
					System.out.print(c);
					try {
						o.notify(); // 叫醒等待队列里的任意一个,notifyAll是叫醒所有线程
						o.wait(); // 让出锁 运行中的线程进入等待队列中,与此同时将锁释放
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
				}
				o.notify(); // 必须,否则无法停止程序。因为无论哪个线程先运行完,总有个线程处于wait状态,只有叫醒该线程程序才会运行完
			}
		}, "t1").start();

		new Thread(() -> {
			synchronized (o) {// 拿不到锁的时候就在等待队列中
				for (char c : aC) {
					System.out.print(c);
					try {
						o.notify();
						o.wait();
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
				}
				o.notify();
			}
		}, "t2").start();

	}

}

输出结果:1A2B3C4D5E6F7G

执行流程如下:

持有锁之后就执行输出。

②使用CAS限制t2线程先运行

//sync_wait_notify程序无法限制哪个线程先运行,因此在这个程序里限制哪个线程先运行
public class Maureen_sync_wait_notify_01 {
	private static volatile boolean t2Started = false;

	public static void main(String[] args) {
		final Object o = new Object();

		char[] aI = "1234567".toCharArray();
		char[] aC = "ABCDEFG".toCharArray();

		new Thread(() -> {
			synchronized (o) {
				while (!t2Started) { //限制t2先运行,如果t2没有先运行,t1就先等待
					try {
						o.wait();
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
				}

				for (char c : aI) {
					System.out.print(c);
					try {
						o.notify();
						o.wait();
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
				}
				o.notify();
			}
		},"t1").start();
		
		new Thread(()->{
			synchronized(o) {
				for(char c: aC) {
					System.out.print(c);
					t2Started = true;
					try {
						o.notify();
						o.wait();
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
				}
				o.notify();
			}
		},"t2").start();

	}

}

输出结果:A1B2C3D4E5F6G7

③使用CountDownLatch限制t2先运行

import java.util.concurrent.CountDownLatch;

//使用CountDownLatch限制线程运行顺序,以下是限制线程2先运行
public class Maureen_sync_wait_notify_02 {

	private static CountDownLatch latch = new CountDownLatch(1);

	public static void main(String[] args) {
		final Object o = new Object();

		char[] aI = "1234567".toCharArray();
		char[] aC = "ABCDEFG".toCharArray();

		new Thread(() -> {
			try {
				latch.await();
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
			synchronized (o) {
				for (char c : aI) {
					System.out.print(c);
					try {
						o.notify();
						o.wait();
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
				}
				o.notify();
			}
		}, "t1").start();

		new Thread(() -> {
			synchronized (o) {
				for (char c : aC) {
					System.out.print(c);
					latch.countDown();
					try {
						o.notify();
						o.wait();
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
				}
				o.notify();
			}
		}, "t2").start();

	}

}

/**
 * 线程调用start()函数,并不意味着线程立即占用CPU运行,而是进入CPU的等待队列中,即进入Ready状态。根据操作系统的调度,决定选择哪个线程运行
 */

输出结果:A1B2C3D4E5F6G7

线程状态迁移图:

【Java】多线程_第1张图片

你可能感兴趣的:(Java)