等待通知之交叉备份
请看如下代码
package chaprer3;
/**
* @program: demo
* @description: 数据库帮助类
* @author: lee
* @create: 2018-09-01
**/
public class DBtools {
volatile private boolean prevIsA = false;
synchronized public void backupA() {
try {
while (prevIsA == true) {
wait();
}
for (int i = 0; i < 5; i++) {
System.out.println("***********");
}
prevIsA = true;
notifyAll();
} catch (InterruptedException ex) {
ex.printStackTrace();
}
}
synchronized public void backupB() {
try {
while (prevIsA == false) {
wait();
}
for (int i = 0; i < 5; i++) {
System.out.println("-----------");
}
prevIsA = false;
notifyAll();
} catch (InterruptedException ex) {
ex.printStackTrace();
}
}
}
package chaprer3;
/**
* @program: demo
* @description: 线程A
* @author: lee
* @create: 2018-09-01
**/
public class ThreadA implements Runnable{
private DBtools dBtools;
public ThreadA(DBtools dBtools) {
this.dBtools = dBtools;
}
@Override
public void run() {
this.dBtools.backupA();
}
}
package chaprer3;
/**
* @program: demo
* @description: 线程A
* @author: lee
* @create: 2018-09-01
**/
public class ThreadB implements Runnable{
private DBtools dBtools;
public ThreadB(DBtools dBtools) {
this.dBtools = dBtools;
}
@Override
public void run() {
this.dBtools.backupB();
}
}
package chaprer3;
/**
* @program: demo
* @description: 主线程
* @author: lee
* @create: 2018-09-01
**/
public class Run {
public static void main(String[] args) {
DBtools dBtools = new DBtools();
for (int i = 0; i < 5000; i++) {
new Thread(new ThreadA(dBtools)).start();
new Thread(new ThreadB(dBtools)).start();
}
}
}
输出结果
...
-----------
-----------
-----------
-----------
-----------
***********
***********
***********
***********
***********
-----------
-----------
-----------
-----------
-----------
***********
***********
***********
***********
***********
-----------
-----------
-----------
-----------
-----------
...
不断的交替输出,结果非常快。
思考一个问题,当notifyall改为了notify以后会出现什么情况,会出现假死状态,即很可能notify唤醒的都是同类线程,最终所有的同类线程都处于阻塞状态.
package chaprer3;
/**
* @program: demo
* @description: 数据库帮助类
* @author: lee
* @create: 2018-09-01
**/
public class DBtools {
volatile private boolean prevIsA = false;
synchronized public void backupA() {
try {
while (prevIsA == true) {
wait();
}
for (int i = 0; i < 5; i++) {
System.out.println("***********");
}
prevIsA = true;
notify();
} catch (InterruptedException ex) {
ex.printStackTrace();
}
}
synchronized public void backupB() {
try {
while (prevIsA == false) {
wait();
}
for (int i = 0; i < 5; i++) {
System.out.println("-----------");
}
prevIsA = false;
notify();
} catch (InterruptedException ex) {
ex.printStackTrace();
}
}
}
程序会一直挂起不停止.
join方法的使用
join方法的作用就是等待线程对象销毁
实际用法,请参考如下代码
package chaprer3;
/**
* @program: demo
* @description: 数据库帮助类
* @author: lee
* @create: 2018-09-01
**/
public class DBtools {
volatile private boolean prevIsA = false;
synchronized public void backupA() {
try {
while (prevIsA == true) {
wait();
}
for (int i = 0; i < 5; i++) {
System.out.println("***********");
}
prevIsA = true;
notify();
} catch (InterruptedException ex) {
ex.printStackTrace();
}
}
synchronized public void backupB() {
try {
while (prevIsA == false) {
wait();
}
for (int i = 0; i < 5; i++) {
System.out.println("-----------");
}
prevIsA = false;
notify();
} catch (InterruptedException ex) {
ex.printStackTrace();
}
}
}
package chaprer3;
/**
* @program: demo
* @description: 线程A
* @author: lee
* @create: 2018-09-01
**/
public class ThreadA implements Runnable{
private DBtools dBtools;
public ThreadA(DBtools dBtools) {
this.dBtools = dBtools;
}
@Override
public void run() {
this.dBtools.backupA();
}
}
package chaprer3;
/**
* @program: demo
* @description: 主线程
* @author: lee
* @create: 2018-09-01
**/
public class Run {
public static void main(String[] args) {
DBtools dBtools = new DBtools();
ThreadA threadA = new ThreadA(dBtools);
Thread thread = new Thread(threadA);
thread.start();
System.out.println("我想等thread执行完毕后执行");
}
}
输出结果
我想等thread执行完毕后执行
***********
***********
***********
***********
***********
显然这并没有等线程执行完了再执行后面的结果。程序做如下修改
public class Run {
public static void main(String[] args) {
try {
DBtools dBtools = new DBtools();
ThreadA threadA = new ThreadA(dBtools);
Thread thread = new Thread(threadA);
thread.start();
thread.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("我想等thread执行完毕后执行");
}
}
输出结果
***********
***********
***********
***********
***********
我想等thread执行完毕后执行
达到了们想要的结果,如果用sleep方法也可以,但是不能确定具体的等待时间。
join(long)的使用
方法join中的参数是设置等待时间,请看如下代码
/**
* @program: demo
* @description: 数据库帮助类
* @author: lee
* @create: 2018-09-01
**/
public class DBtools {
volatile private boolean prevIsA = false;
synchronized public void backupA() {
try {
while (prevIsA == true) {
wait();
}
Thread.sleep(5000);
for (int i = 0; i < 5; i++) {
System.out.println("***********");
}
prevIsA = true;
notify();
} catch (InterruptedException ex) {
ex.printStackTrace();
}
}
synchronized public void backupB() {
try {
while (prevIsA == false) {
wait();
}
for (int i = 0; i < 5; i++) {
System.out.println("-----------");
}
prevIsA = false;
notify();
} catch (InterruptedException ex) {
ex.printStackTrace();
}
}
}
public class Run {
public static void main(String[] args) {
try {
DBtools dBtools = new DBtools();
ThreadA threadA = new ThreadA(dBtools);
Thread thread = new Thread(threadA);
thread.start();
thread.join(2000);//等待两秒
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("我想等thread执行完毕后执行");
}
}
输出结果
我想等thread执行完毕后执行
***********
***********
***********
***********
***********
join的实现方法
/**
* Waits at most {@code millis} milliseconds for this thread to
* die. A timeout of {@code 0} means to wait forever.
*
* <p> This implementation uses a loop of {@code this.wait} calls
* conditioned on {@code this.isAlive}. As a thread terminates the
* {@code this.notifyAll} method is invoked. It is recommended that
* applications not use {@code wait}, {@code notify}, or
* {@code notifyAll} on {@code Thread} instances.
*
* @param millis
* the time to wait in milliseconds
*
* @throws IllegalArgumentException
* if the value of {@code millis} is negative
*
* @throws InterruptedException
* if any thread has interrupted the current thread. The
* <i>interrupted status</i> of the current thread is
* cleared when this exception is thrown.
*/
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;
}
}
}
join方法的内部是用wait方法实现的,所以join方法具有释放锁的特点,sleep方法则没有释放锁的特点,即释放了锁以后其他的线程可以调用此线程中的同步方法了。
方法join和sleep的区别
请看如下代码
public class ThreadB extends Thread{
@Override
public void run() {
try {
System.out.println("b开始运行了");
Thread.sleep(5000);
System.out.println("b运行结束了");
} catch (InterruptedException ex) {
}
}
synchronized public void bService() {
System.out.println("b服务启动了");
}
}
public class ThreadA extends Thread {
private ThreadB threadB;
public ThreadA(ThreadB threadB) {
this.threadB = threadB;
}
@Override
public void run() {
try {
synchronized (threadB) {
threadB.start();
Thread.sleep(5000);
//threadB.join(5000);
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public class ThreadC extends Thread{
private ThreadB threadB;
public ThreadC(ThreadB threadB) {
this.threadB = threadB;
}
@Override
public void run() {
threadB.bService();
}
}
public class Run {
public static void main(String[] args) {
try {
ThreadB threadB = new ThreadB();
ThreadA threadA = new ThreadA(threadB);
threadA.start();
Thread.sleep(1000);
ThreadC threadC = new ThreadC(threadB);
threadC.start();
} catch ( InterruptedException ex) {
ex.printStackTrace();
}
}
}
输出结果
b开始运行了
b运行结束了
b服务启动了
稍微修改代码
public class ThreadA extends Thread {
private ThreadB threadB;
public ThreadA(ThreadB threadB) {
this.threadB = threadB;
}
@Override
public void run() {
try {
synchronized (threadB) {
threadB.start();
//Thread.sleep(5000);
//释放掉threadB的锁
threadB.join(5000);
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
join之后的代码提前运行
轻卡如下代码
public class ThreadA extends Thread {
private ThreadB threadB;
public ThreadA(ThreadB threadB) {
this.threadB = threadB;
}
@Override
public void run() {
try {
synchronized (threadB) {
System.out.println("threadA开始运行了");
Thread.sleep(5000);
System.out.println("threadA结束运行了");
}
} catch (InterruptedException ex) {
ex.printStackTrace();
}
}
}
public class ThreadB extends Thread {
@Override
synchronized public void run() {
System.out.println("threadB开始运行了");
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("threadB结束运行了");
}
}
public class Run {
public static void main(String[] args) {
for (int i =0 ;i<10;i++) {
try {
ThreadB threadB = new ThreadB();
ThreadA threadA = new ThreadA(threadB);
threadA.start();
threadB.start();
threadB.join(2000);
System.out.println("主线程即将结束");
} catch ( InterruptedException ex) {
ex.printStackTrace();
}
}
}
}
如果线程A先获得线程锁,则输出结果如下,则A线程会先执行完成,如果线程B先获得对象锁,两秒以后释放对对象B的加锁,其他线程也会有机会进行执行,所以这里的数据结果将会有差异。 输出结果 一下是一种情况
...
threadA结束运行了
threadA结束运行了
主线程即将结束
threadB开始运行了
threadB结束运行了
...
ThreadLocal使用
变量值的共享可以使用public static的变量形式,所有的线程都使用一个public static变量。如果想要实现每一个线程都有自己的变量共享,则可以采用如下方式。
public class Tools {
public static ThreadLocal local = new ThreadLocal();
}
public class ThreadA extends Thread {
@Override
synchronized public void run() {
try {
for (int i = 0; i < 100; i++) {
Tools.local.set(i);
System.out.println(Thread.currentThread().getName() + "----" + Tools.local.get());
Thread.sleep(1000);
}
} catch (InterruptedException ex) {
ex.printStackTrace();
}
}
}
public class ThreadB extends Thread {
@Override
synchronized public void run() {
try {
for (int i = 0; i < 100; i++) {
Tools.local.set(i);
System.out.println(Thread.currentThread().getName() + "----" + Tools.local.get());
Thread.sleep(1000);
}
} catch (InterruptedException ex) {
ex.printStackTrace();
}
}
}
public class Run {
public static void main(String[] args) {
try {
ThreadB threadB = new ThreadB();
threadB.setName("thread-b");
ThreadA threadA = new ThreadA();
threadA.setName("thread-a");
threadA.start();
threadB.start();
for (int i = 0; i < 100; i++) {
Tools.local.set(i);
System.out.println("main--"+Tools.local.get());
Thread.sleep(1000);
}
} catch (InterruptedException ex) {
ex.printStackTrace();
}
}
}
输出结果
主线程即将结束
main--0
thread-a----0
thread-b----0
main--1
thread-a----1
thread-b----1
main--2
thread-a----2
thread-b----2
main--3
thread-a----3
thread-b----3
main--4
thread-a----4
thread-b----4
main--5
thread-a----5
thread-b----5
....
从结果可以看出,线程之间的变量互相不影响。
解决ThreadLocal中get为null
请看如下代码
public class Tools extends ThreadLocal{
@Override
protected Object initialValue() {
return "不会再返回null值了";
}
public static void main(String[] args) {
System.out.println(new Tools().get());
}
}
输出结果
不会再返回null值了