关于volatile关键字的使用:
public class Test { static boolean flag = true; public static void main(String[] args) { new Thread(){ @Override public void run() { System.out.println("thread1 begin"); while(flag){ } System.out.println("thread1 end"); } }.start(); try { Thread.sleep(4000); } catch (InterruptedException e) { e.printStackTrace(); } new Thread(){ @Override public void run() { System.out.println("thread2 begin"); flag = false; System.out.println("thread2 end"); } }.start(); } }
在线程1执行的时候,控制台只能输出thread1 begin的语句,代表thread1线程已经开始运行,原因是我在thread1线程中设置了一个while死循环。在线程睡眠了几秒之后,thread2线程开始运行,控制台输出了thread2 begin的语句,随后执行将flag标识赋值为false,再输出thread2 end语句。
但是thread1线程并没有结束,可以看到控制台并没有输出thread1 end语句。
结果如下所示:
分析原因:
每一个线程的栈内存都是独立的,而我们设置的flag是存储在堆内存当中的,当线程要操作堆内存当中的资源时,需要将其复制到栈内存中进行操作,然后将执行的结果再赋给堆内存中的资源。但是此例子当中flag虽然已经被赋值为false,而thread1线程中的死循环并没有停止。原因是线程与线程之间是互相独立私有的,只能通过以上方法(利用堆内存交换更新的数据)来达到线程之间的通信效果。但是这样的通信是不及时的,也可能永远都不会更新,可能会出现多个线程操作同一个堆内存当中的资源时,其中一个线程对资源进行修改之后,其他线程不知道的情况。因此我们需要给变量加上volatile关键字,这样的话,其中一个线程对资源进行修改之后,别的线程也能够知道。
修改后的代码以及控制台输出结果如下所示:
关于多线程中lock的使用方法:
如下举例:
public class Test { public static void main(String[] args) { Action action = new Action(); Thread1 thread1 = new Thread1(action,"d"); Thread1 thread11 = new Thread1(action,"k"); Thread t1 = new Thread(thread1); Thread t2 = new Thread(thread11); t1.start(); t2.start(); try { t1.join(); t2.join(); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(action.str); System.out.println(action.str.length()); } } class Action{ String str = ""; public void add(String value){ str += value; } } class Thread1 implements Runnable{ Action action; String value; public Thread1(Action action, String value) { this.action = action; this.value = value; } @Override public void run() { int count = 0; while(count<30){ action.add(value); count++; } } }
由于没有给资源上锁,多个线程操作同一个资源,导致出现了线程不安全的情况,如下为控制台输出结果:
将原有代码进行改进(给资源上锁):
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class Test {
public static void main(String[] args) {
Action action = new Action();
Thread1 thread1 = new Thread1(action,"d");
Thread1 thread11 = new Thread1(action,"k");
Thread t1 = new Thread(thread1);
Thread t2 = new Thread(thread11);
t1.start();
t2.start();
try {
t1.join();
t2.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(action.str);
System.out.println(action.str.length());
}
}
class Action{
String str = "";
public void add(String value){
str += value;
}
}
class Thread1 implements Runnable{
//构造一把锁
static Lock lock = new ReentrantLock();(此处注意需要用static来修饰lock,只可以设置一把锁,多个线程来争抢一把锁的使用权)
Action action;
String value;
public Thread1(Action action, String value) {
this.action = action;
this.value = value;
}
@Override
public void run() {
int count = 0;
while(count<30){
//对于资源的操作上锁
lock.lock();
action.add(value);
//解锁
lock.unlock();
count++;
}
}
}
修改后的控制台输出情况如下:
再次将代码修改
经过修改后的情况:
此时的代码出现了一个问题,就是在第一个拿到锁使用权的线程抛出错误后,中断了程序,从而导致锁一直被锁着,没有进行解锁的操作,导致其他的线程都没有任何办法进行对资源的操作。因此在使用lock对资源上锁的时候,一定要设置解锁,需要保证在任何情况下无论出现什么意外程序都会执行unlock()操作。
修改方法如下所示(将unlock()操作写入finally代码块中):
异常虽然被抛出,但是依然执行了解锁操作,没有影响程序的运行。