3.1 等待、通知机制
3.1.3 等待/通知机制的实现
wait()方法作用是让当前线程进行等待,wait是Object类的方法,用来将当前线程置入“预执行队列”中。在wait所在代码行处停止执行,直到接到通知或被中断为止。
调用wait()之前,线程必须获得该对象的对象级别锁,也就是只能在同步方法或同步块中调用wait()方法。
如果调用wait时线程没有持有适当的锁,则抛出IllegalMonitorStateException,它是RuntimeException的子类,不需要try-catch。
notify()方法也要在同步方法或同步块中调用,也需要线程的对象级别锁。
用来通知那些可能等待该对象的对象锁的其他线程,如果有多个线程等待,则由线程规划器随机挑出一个wait状态的线程,对其notify,让该线程得到对象锁。
执行notify()方法后,当前线程不会马上释放该对象锁,要等执行notify()方法的线程将程序执行完,退出synchronized代码块后,当前线程才会释放锁。
使线程等待
//这样会报错,没有对象监视器,也就是同步加锁
public class Test {
public static void main(String[] args){
try{
String str = new String("");
str.wait();
}catch(InterruptedException e){
e.printStackTrace();
}
}
}
public class Test {
public static void main(String[] args){
try{
String lock = new String();
synchronized(lock){
System.out.println("syn第一行");
lock.wait();
System.out.println("syn第二个");
}
}catch(InterruptedException e){
e.printStackTrace();
}
}
}
通知线程,使线程继续运行下去
public class Test {
public static void main(String[] args) {
Object lock = new Object(); //需要是同一个对象锁
ThreadA a= new ThreadA(lock);
a.start();
ThreadB b =new ThreadB(lock);
b.start();
}
}
class ThreadA extends Thread{
private Object lock;
public ThreadA(Object lock){
this.lock = lock;
}
public void run(){
try {
synchronized (lock){
System.out.println("开始 wait time:" + System.currentTimeMillis());
lock.wait();
System.out.println("结束 wait time:" + System.currentTimeMillis());
}
}catch (InterruptedException e){
e.printStackTrace();
}
}
}
class ThreadB extends Thread{
private Object lock;
public ThreadB(Object lock){
this.lock = lock;
}
public void run(){
synchronized (lock){
System.out.println("开始 notify time:" + System.currentTimeMillis());
lock.notify();
System.out.println("结束 notify time:" + System.currentTimeMillis());
}
}
}
/*
开始 wait time:1525527916728
开始 notify time:1525527916729
结束 notify time:1525527916729
结束 wait time:1525527916729
*/
当线程被wait后,如果再被notify,代码就从wait()方法之后继续执行。
notifyAll()方法:可以唤醒所有正在等待队列中的同一共享资源的全部线程,进入可运行状态,优先级最高的线程最先执行,但也可能是随机执行。
3.1.4 方法wait锁释放和notify锁不释放
方法wait被执行后,锁被自动释放;执行完notify方法,锁不自动释放。
public class Test {
public static void main(String[] args) {
Object lock = new Object();
ThreadA a = new ThreadA(lock);
a.start();
NotifyThread notifyThread = new NotifyThread(lock);
notifyThread.start();
SynNotifyMethodThread synNotifyMethodThread = new SynNotifyMethodThread(lock);
synNotifyMethodThread.start();
}
}
class Service {
public void testWaitMethod(Object lock){
try{
synchronized (lock){
System.out.println("begin wait thread name:"+Thread.currentThread().getName());
lock.wait();
System.out.println("end wait thread name:"+Thread.currentThread().getName());
}
}catch (InterruptedException e){
e.printStackTrace();
}
}
public void synNotifyMethod(Object lock){
try{
synchronized (lock){
System.out.println("begin notify thread name:"+Thread.currentThread().getName());
lock.notify();
Thread.sleep(5000);
System.out.println("end notify thread name:"+Thread.currentThread().getName());
}
}catch (InterruptedException e){
e.printStackTrace();
}
}
}
class ThreadA extends Thread {
private Object lock;
public ThreadA(Object lock){
super();
this.lock = lock;
}
public void run(){
Service service= new Service();
service.testWaitMethod(lock);
}
}
class NotifyThread extends Thread{
private Object lock;
public NotifyThread(Object lock){
super();
this.lock = lock;
}
public void run(){
Service service= new Service();
service.synNotifyMethod(lock);
}
}
class SynNotifyMethodThread extends Thread{
private Object lock;
public SynNotifyMethodThread(Object lock){
super();
this.lock = lock;
}
public void run(){
Service service= new Service();
service.synNotifyMethod(lock);
}
}
/*
begin wait thread name:Thread-0
begin notify thread name:Thread-1
end notify thread name:Thread-1
end wait thread name:Thread-0
begin notify thread name:Thread-2
end notify thread name:Thread-2
*/
必须执行完notify方法所在的同步synchronized代码块后才释放锁。
3.1.5 interrupt方法和wait方法
线程呈wait()状态,调用interrupt()方法会出现InterruptedException异常。
class Service {
public void testWaitMethod(Object lock){
try{
synchronized (lock){
System.out.println("begin wait thread name:"+Thread.currentThread().getName());
lock.wait();
System.out.println("end wait thread name:"+Thread.currentThread().getName());
}
}catch (InterruptedException e){
e.printStackTrace();
}
}
}
class ThreadA extends Thread {
private Object lock;
public ThreadA(Object lock){
super();
this.lock = lock;
}
public void run(){
Service service= new Service();
service.testWaitMethod(lock);
}
}
public class Test {
public static void main(String[] args) {
Object lock = new Object();
ThreadA a = new ThreadA(lock);
a.start();
Thread.sleep(1000);
a.interrupt();
}
}
3.1.8 方法wait(long)的使用
wait(long)方法功能是等待某一时间内是否有线程对锁进行唤醒,如果超过这个时间则自动唤醒。
//5秒内自动唤醒
public class MyRunnable {
static private Object lock = new Object();
static private Runnable runnable = new Runnable(){
public void run(){
try{
synchronized(lock){
System.out.println("wait begin time="+System.currentTimeMillis());
lock.wait(5000);
System.out.println("wait end time="+System.currentTimeMillis());
}
}catch(InterruptedException e){
e.printStackTrace();
}
}
}
public static void main(String[] args){
Thread t = new Thread(runnable);
t.start();
}
}
3.1.11 生产者/消费者模式实现
3.1.11.1 一生产、一消费:
//存储值对象
class ValueObject{
public static String value;
}
//生产者
class Produce {
private String lock;
public Produce(String lock){
this.lock = lock;
}
public void setValue(){
try{
synchronized(lock){
if(!ValueObject.value.equals("")){
lock.wait();
}
String value = System.currentTimeMillis() + "_" + System.nanoTime();
System.out.println("set的值是:"+value);
ValueObject.value = value;
lock.notify();
}
}catch(InterruptedException e){
e.printStackTrace();
}
}
}
//消费者
class Consumer {
private String lock;
public Consumer(String lock){
this.lock = lock;
}
public void getValue(){
try{
synchronized(lock){
if(ValueObject.value.equals("")){
lock.wait();
}
System.out.println("get值是:"+ValueObject.value);
ValueObject.value = "";
lock.notify();
}
}catch(InterruptedException e){
e.printStackTrace();
}
}
}
3.1.12 管道进行线程通信:字节流
用于不同线程间的通信
PipedInputStream和PipedOutputStream
public class Test {
public static void main(String[] args) throws Exception {
WriteData writeData = new WriteData();
ReadData readData = new ReadData();
PipedInputStream inputStream = new PipedInputStream();
PipedOutputStream outputStream = new PipedOutputStream();
//outputStream.connect(inputStream);
inputStream.connect(outputStream);
ReadThread readThread = new ReadThread(readData,inputStream);
readThread.start();
WriteThread writeThread= new WriteThread(writeData,outputStream);
writeThread.start();
}
}
class WriteData {
public void writeMethod(PipedOutputStream out) {
try {
System.out.println("write:");
for (int i = 0; i < 300; i++) {
String outData = "" + (i + 1);
out.write(outData.getBytes());
System.out.print(outData);
}
System.out.println();
out.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
class ReadData {
public void readMethod(PipedInputStream in) {
try {
System.out.println("read:");
byte[] bytes = new byte[20];
int readLength = in.read(bytes);
while (readLength != -1){
String newData = new String(bytes, 0, readLength);
System.out.print(newData);
readLength = in.read(bytes);
}
System.out.println();
in.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
class WriteThread extends Thread {
private WriteData write;
private PipedOutputStream out;
public WriteThread(WriteData write,PipedOutputStream out){
super();
this.write = write;
this.out = out;
}
public void run(){
write.writeMethod(out);
}
}
class ReadThread extends Thread {
private ReadData read;
private PipedInputStream in;
public ReadThread(ReadData read,PipedInputStream in){
super();
this.read = read;
this.in = in;
}
public void run(){
read.readMethod(in);
}
}
3.1.13 管道进行线程通信:字符流
PipedWrite和PipedReader
代码同上
3.1.14 实战:等待/通知交叉备份
创建20个线程,10个线程将数据备份到A数据库中,另外10个线程将数据备份到B数据库中,备份A和B是交叉进行的。
public class Test {
public static void main(String[] args) {
DBTools dbTools = new DBTools();
for (int i = 0; i < 20; i++) {
ThreadA a = new ThreadA(dbTools);
a.start();
ThreadB b = new ThreadB(dbTools);
b.start();
}
}
}
class DBTools {
volatile private boolean preIsA = false;
synchronized public void backupA() {
try {
while (preIsA == true) {
wait();
}
for (int i = 0; i < 5; i++) {
System.out.println("********");
}
preIsA = true;
notifyAll();
} catch (Exception e) {
e.printStackTrace();
}
}
synchronized public void backupB() {
try {
while (preIsA == false) {
wait();
}
for (int i = 0; i < 5; i++) {
System.out.println("==========");
}
preIsA = false;
notifyAll();
} catch (Exception e) {
e.printStackTrace();
}
}
}
class ThreadA extends Thread {
private DBTools dbTools;
public ThreadA(DBTools dbTools) {
super();
this.dbTools = dbTools;
}
public void run() {
dbTools.backupA();
}
}
class ThreadB extends Thread {
private DBTools dbTools;
public ThreadB(DBTools dbTools) {
super();
this.dbTools = dbTools;
}
public void run() {
dbTools.backupB();
}
}
3.2 join()使用
主线程创建并启动子线程,如果子线程要进行大量耗时运算,主线程可能会早于子线程结束前结束。
如果这时主线程想等子线程执行完后再结束,并去子线程中取数据。就要用到join。
作用是等待线程对象销毁。
clas MyThread extends Thread {
public void run(){
try{
int secondValue = (int)(Math.random() * 1000);
//sleep的值不确定
System.out.println(secondValue);
Thread.sleep(secondValue);
}catch(InterruptedException e){
e.printStackTrace();
}
}
}
public class Test {
public static void main(String[] args){
try{
MyThread my = new MyThread();
my.start();
my.join();
//如果没有join,这句话会先于secondValue输出
System.out.println("我想当MyThread对象执行完后再执行!");
}catch(InterruptedException e){
e.printStackTrace();
}
}
}
join作用:
使所属线程对象X正常执行run()方法中的任务,使当前线程Z进行无限期的阻塞,等待线程X销毁后再继续执行线程Z后面代码。
join具有使线程排队运行的作用,类似于同步效果。
join和synchronized区别是:
join在内部用wait()方法进行等待,而synchronized使用的是“对象监视器”原理作为同步。
3.2.4 join(long)的使用
参数是设置等待的时间
class MyThread extends Thread {
@Override
public void run(){
try{
System.out.println("begin time="+System.currentTimeMillis());
Thread.sleep(5000); //原本是五秒
}catch(Exception e){
e.printStackTrace();
}
}
}
public class Test {
public static void main(String[] args){
try{
MyThread my = new MyThread();
ny.start();
my.join(2000); //只等2秒
//Thread.sleep(2000);
}catch(Exception e){
e.printStackTrace();
}
}
}
3.2.5 join(long)和sleep(long)的区别
join(long)的功能在内部是使用wait(long)来实现的,所以join具有释放锁的特点。
public final synchronized void join(long millis) throws InterruptedException {
long base = System.currentTimeMillis();
long now = 0;
if (millis < 0) {
throw new IllegalArgumentException("timeout value is negative");
}
if (millis == 0) {
while (isAlive()) {
wait(0);
}
} else {
while (isAlive()) {
long delay = millis - now;
if (delay <= 0) {
break;
}
wait(delay);
now = System.currentTimeMillis() - base;
}
}
}
3.3 类ThreadLocal的使用
变量值得共享可以用public static 声明的形式,所有线程都使用同一个public static变量。
如果每个线程都有自己的共享变量如何解决?用ThreadLocal
ThreadLocal主要解决的是每个线程绑定自己的值,可将ThreadLocal类比成全局存放数据的盒子,盒子中可存储每个线程的私有数据。
3.3.1 get()和null
public class Run{
public static ThreadLocal t1 = new ThreadLocal();
public static void main(String[] args){
if(t1.get() == null){
System.out.println("从未放过值");
t1.set("我的值");
}
System.out.println(t1.get());
System.out.println(t1.get());
}
}
多个线程同时调用ThreadLocal,测试数据隔离性
public class Test {
public static void main(String[] args) throws Exception {
ThreadA a = new ThreadA();
a.start();
Thread.sleep(1000);
ThreadB b = new ThreadB();
b.start();
}
}
class Tools {
public static ThreadLocal<Date> t1 = new ThreadLocal<Date>();
}
class ThreadA extends Thread {
@Override
public void run() {
try {
for (int i = 0; i < 20; i++) {
if (Tools.t1.get() == null) {
Tools.t1.set(new Date());
}
System.out.println("A " + Tools.t1.get().getTime());
Thread.sleep(100);
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
class ThreadB extends Thread {
@Override
public void run() {
try {
for (int i = 0; i < 20; i++) {
if (Tools.t1.get() == null) {
Tools.t1.set(new Date());
}
System.out.println("B " + Tools.t1.get().getTime());
Thread.sleep(100);
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
3.3.3 解决第一次get()返回null的问题
public class Test {
public static ThreadLocalExt t1 = new ThreadLocalExt();
public static void main(String[] args) {
System.out.println(t1.get());
}
}
//继承ThreadLocal
class ThreadLocalExt extends ThreadLocal {
@Override
protected Object initialValue(){
return "我是默认值";
}
}
3.4 类InheritableThreadLocal的使用
该类可以在子线程中取得父线程继承下来的值。
3.4.1 用InheritableThreadLocal让子线程从父线程中取值
public class Test {
public static void main(String[] args) {
try {
for(int i=0;i<10;i++){
System.out.println("在主方法中取值="+Tools.t1.get());
Thread.sleep(100);
}
Thread.sleep(5000);
ThreadA a = new ThreadA();
a.start();
}catch (Exception e){
e.printStackTrace();
}
}
}
class InheritableThreadLocalExt extends InheritableThreadLocal {
@Override
protected Object initialValue() {
return new Date().getTime();
}
//注意!这里的值可以用在子线程中
@Override
protected Object childValue(Object parentValue) {
return parentValue + "我在子线程中加的";
}
}
class Tools {
public static InheritableThreadLocalExt t1 = new InheritableThreadLocalExt();
}
class ThreadA extends Thread{
@Override
public void run() {
try {
for(int i=0;i<10;i++){
System.out.println("在线程A中取值="+Tools.t1.get());
Thread.sleep(100);
}
}catch (Exception e){
e.printStackTrace();
}
}
}
/*
在主方法中取值=1525754211326
在主方法中取值=1525754211326
在主方法中取值=1525754211326
在主方法中取值=1525754211326
在主方法中取值=1525754211326
在线程A中取值=1525754211326我在子线程中加的
在线程A中取值=1525754211326我在子线程中加的
在线程A中取值=1525754211326我在子线程中加的
在线程A中取值=1525754211326我在子线程中加的
在线程A中取值=1525754211326我在子线程中加的
*/