新建线程
继承Thread
实现Runnable
停止线程
stop方法是一个被废弃的方法,因为其是暴力的,即不管线程执行的状态如何,都会立刻停止线程,可能会引起数据不一致的情况,数据不一致的情况见如下代码:
public class UnformmatedStop {
static class Person{
private String username;
private String password;
public String getUsename() {
return username;
}
public void setUsename(String usename) {
this.username = usename;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public synchronized void change(String username,String password){
this.username = username;
try {
//TODO other things
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
this.password = password;
}
public synchronized String read(){
return username + ","+password;
}
}
static class Task implements Runnable{
private volatile Person p;
public Task(Person p) {
super();
this.p = p;
}
@Override
public void run() {
System.out.println(p.read());
}
}
static class Task2 implements Runnable{
private volatile Person p;
public Task2(Person p) {
super();
this.p = p;
}
@Override
public void run() {
p.change("lq", "084012151");
}
}
public static void main(String[] args) throws InterruptedException {
Person p = new Person();
Thread tChange = new Thread(new Task2(p));
tChange.start();
Thread.sleep(3000);
tChange.stop();
Thread tRead = new Thread(new Task(p));
tRead.start();
}
}
结果如下:
lq,null
结果分析:tChange线程在写入username和password的时候,被强迫停止了,造成password没有被写成功,tRead读取的时候就会造成password为null了,这就造成了丢失更新的出现
正确的停止一个线程的方法
public class Stop {
static class Task implements Runnable{
public volatile boolean stooped = false;
@Override
public void run() {
while(!stooped){
System.out.println(Thread.currentThread().getName()+"正在运行");
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
public static void main(String[] args) throws InterruptedException {
Task task = new Task();
Thread t1 = new Thread(task);
t1.start();
Thread.sleep(3000);
System.out.println("下面开始暂停线程t1的执行");
task.stooped = true;
}
}
注意:volatile的使用,volatile会保证可见性,但是不保证原子性(load,use,rewrite)这个整个过程的原子性
线程中断
线程中断并不会使线程立即退出,而是给线程发送一个通知,告知目标线程, 有人希望你退出啦!至于目标线程接到通知后如何处理,则完全由目标线程自行决定。这点很 重要,如果中断后,线程立即无条件退出,我们就又会遇到stop()方法的老问题。
/**
* 线程中断并不会使线程立即退出,而是给线程发送一个通知,告知目标线程有人希望你退出了
* 至于目标线程接到通知后何时如何处理则由目标线程自行决定
*/
public class Interrupt implements Runnable{
@Override
public void run() {
while(true){
//中断的两个方法,interupt和isInterrupted(判断当前线程是否被中断)两个方法
/*if(Thread.currentThread().isInterrupted()){
System.out.println(Thread.currentThread().getName()+"已经被中断");
break;
}*/
Thread.yield();
}
}
public static void main(String[] args) {
Thread thread = new Thread(new Interrupt());
thread.start();
//在这里因为Runnable实现类中并没有对中断处理的逻辑,因此即使thread线程被设置了中断状态
//但是这个中断并不会发生任何作用,所以需要放开上面的注释部分,对线程的中断逻辑进行处理
thread.interrupt();
}
}
当sleep遇上interrupt
public static native void sleep(long millis) throws InterruptedException
sleep方法会抛出InterruptedException异常
InterruptedException不是运行时异常,也就是说程序必须捕获并且处理它,当线程在sleep() 休眠时,如果被中断,这个异常就会产生
public class Sleep {
static class Task implements Runnable{
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"正在运行");
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"结束运行");
}
}
public static void main(String[] args) {
Task task = new Task();
Thread t1 = new Thread(task);
t1.start();
//阻断正在睡眠的线程,会使线程抛出中断异常,然后线程会退出休眠的状态,执行线程后面的代码
t1.interrupt();
}
}
Thread-0正在运行
java.lang.InterruptedException: sleep interrupted
at java.lang.Thread.sleep(Native Method)
at sync.Sleep$Task.run(Sleep.java:11)
at java.lang.Thread.run(Unknown Source)
Thread-0结束运行
线程的等待(wait)和通知(notify)
这里还需要强调一点,Objectwait()方法并不是可以随便调用的。它必须包含在对应的 synchronzied语句中,无论是wait()或者notify()都需要首先获得目标对象的一个监视器。
/**
* 假设线程A调用了obj.wait方法,那么线程A就会被停止执行,转为等待状态(并且会释放锁),等待到何时结束呢?线程A会一直
* 等待到其他线程调用了obj.notify方法为止(调用obj.notify并不会释放锁),但是线程A在被唤醒后要做的第一件事并不是执行后续的代码,而是要
* 重新获取obj这个监视器,如果暂时获取不到就会等待这个监视器,当监视器到来后才会真正意义上的唤醒
*
* 就像这里的线程2并不会在线程1,notify后就立刻执行后面的代码一样,这样就会存在延时
*/
public class WaitNotify {
public static void main(String[] args) {
final ListAdd listAdd = new ListAdd();
final Object lock = new Object();
Thread thread1 = new Thread(new Runnable() {
@Override
public void run() {
synchronized (lock) {
for(int i = 0;i<10;i++){
listAdd.add();
System.out.println(Thread.currentThread().getName()+"添加了一个元素");
if(listAdd.size()==5){
//notify必须与sychronized一起使用,notify表示不释放锁,wait表示释放锁
lock.notify();
}
try {
Thread.sleep(500);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
});
Thread thread2 = new Thread(new Runnable() {
@Override
public void run() {
synchronized (lock) {
if(listAdd.size()!=5){
try {
lock.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println(Thread.currentThread().getName()+"遇到了size为5的情况");
throw new RuntimeException("size为5");
}
}
});
/**
* 必须先启动线程2,因为只有这样,会先进入线程2的wait方法进行等待,并且释放锁,然后执行线程1
* 如果先启动线程1的话,线程1在size等于5的时候,并不会释放锁,而是继续往list里面添加数据直到全部完成释放锁,
* 然后线程2执行的时候size已经是10了,不存在为5的情况了,实验结果就不对了
*/
thread2.start();
thread1.start();
}
}
class ListAdd{
private volatile List list = new ArrayList();
public void add(){
list.add("jyw");
}
public int size(){
return list.size();
}
}
运行结果
Thread-0添加了一个元素
Thread-0添加了一个元素
Thread-0添加了一个元素
Thread-0添加了一个元素
Thread-0添加了一个元素
Thread-0添加了一个元素
Thread-0添加了一个元素
Thread-0添加了一个元素
Thread-0添加了一个元素
Thread-0添加了一个元素
Thread-1遇到了size为5的情况
Exception in thread "Thread-1" java.lang.RuntimeException: size为5
at sync.WaitNotify$2.run(WaitNotify.java:56)
at java.lang.Thread.run(Unknown Source)
结果分析:刚开始的时候,listAdd的size不是5,所以就会wait,使得当前线程被挂起,等待notify,然后当i=5的时候,thread1会notify,但是此时thread1并没有释放lock对象监视器,所以thread2并不会立刻执行打印抛出异常,一定需要重新获得锁