接java线程知识自我总结(一)
jion()方法
当前线程调用了jion方法后,就会是主线程暂停执行,直到当前线程结束后,主线程才能继续执行。
当主线程需要得到子线程操作后的数据时,子线程需要调用jion方法,否则主线程取到的值很可能是一个不确定值。
join方法的功能就是使异步执行的线程变成同步执行。
也就是说,A线程和B线程中,在B线程中调用了A.jion(),B线程将会被挂起,等到A线程执行完毕之后才有机会获得执行的机会。
//join
package com.link.jion;
public class Test1 extends Thread {
private static int n;
public void run() {
for(int i=0;i<1000;i++) {
n++;
}
}
public static void main(String[] args) {
Test1 t = new Test1();
t.start();
try {
t.join(); //等到t线程结束后再继续执行
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(n); //打印在线程t执行完毕后的n的值
}
}
wait()和notify()方法
wait方法表示将当前调用线程自己给锁定进入阻塞状态,和sleep方法功能类似,但也有不同。在没有被调用notify方法之前,在wait之后的代码永远不可能被执行。而锁定线程自身是无法调用notify方法把自己给“解放”的,要在另外一个线程中调用此方法才可行。
//例子程序
package com.link.jion;
class A extends Thread {
private Test1 test;
public A(Test1 test) {
this.test = test;
}
public void run() {
try {
test.addN();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
class B extends Thread {
private Test1 test;
public B(Test1 test) {
this.test = test;
}
public void run() {
test.redN();
}
}
public class Test1 {
private int n;
public synchronized void addN() throws InterruptedException {
while (n < 10) {
Thread.sleep(500);
n++;
}
notify();
}
public synchronized void redN() {
while (n != 10) {
try {
wait();
System.out.println(n);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public static void main(String[] args) throws InterruptedException {
Test1 test = new Test1();
A a = new A(test);
B b = new B(test);
b.start();
a.start();
}
}
让b线程先执行,此时此刻n的值还未达到10,所以使得b线程放弃当前获得的“锁“,把”锁“交给别的线程进入阻塞状态。
在b线程调用了wait方法之后,a线程有机会拿到了锁进入addN()方法,把n的值修改到了10,至此a的任务已经完成,调用了notify方法通知b线程并且自己放弃了锁,线程结束。
此时b线程接到了”通知“后,从阻塞状态回到了执行状态,继续执行wait后面的代码,最后打印出了n的值。
这个程序只是简单的演示了2个方法的作用。
在上述程序中,在定义方法的时候使用了synchronized 关键字。他表示对定义的方法进行了同步。
通常大家在刚认识wait和notify的时候,在程序中调用这些方法,代码会报错。
java.lang.IllegalMonitorStateException
这个异常会在三种情况下抛出:
//考虑一下代码最后输出的值
package com.link.volatil;
public class Test1 extends Thread {
volatile static int n = 0;
public void run() {
for (int i = 0; i < 100; i++)
try {
n++;
sleep(3); // 使运行结果更加随机
} catch (Exception e) {
}
}
public static int getN() {
return n;
}
public static void setN(int n) {
Test1.n = n;
}
public static void main(String[] args) {
Test1[] t = new Test1[100];
for (int i = 0; i < t.length; i++) {
t[i] = new Test1();
}
for (int i = 0; i < t.length; i++) {
t[i].start();
}
for (int i = 0; i < t.length; i++) {
try {
t[i].join();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println(n);
}
}
//使用同步方法是的线程变得安全
package com.link.volatil;
public class Test1 extends Thread {
volatile static int n = 0;
static synchronized void inc() {
n++;
}
public void run() {
for (int i = 0; i < 100; i++)
try {
inc();
sleep(3); // 使运行结果更加随机
} catch (Exception e) {
}
}
public static int getN() {
return n;
}
public static void setN(int n) {
Test1.n = n;
}
public static void main(String[] args) {
Test1[] t = new Test1[100];
for (int i = 0; i < t.length; i++) {
t[i] = new Test1();
}
for (int i = 0; i < t.length; i++) {
t[i].start();
}
for (int i = 0; i < t.length; i++) {
try {
t[i].join();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println(n);
}
}
上面的程序把“三部曲”变成了“一部曲”,使得多个线程不可能读到同样的值,避免了过期数据的产生,使程序变得更加安全。最后程序的输出结果肯定是10000。