1.线程可以被sleep()中断,但这种中断属于阻塞线程,线程仍然可以获得cpu的执行权,如果睡眠中的线程不是后台进程且当主线程执行完时jvm不会停止还会等到睡眠中的线程。当然如果睡眠中被interrupted中断会抛出异常从而被真正的中断。
下面例子中主线程执行完后,子线程睡完后才会停止运行。
public class MyThread extends Thread{
@Override
public void run() {
try {
Thread.sleep(5000);
System.out.println("子线程睡完了,运行结束");
} catch (Exception e) {
// TODO: handle exception
}
}
public static void main(String[] args) {
MyThread myThread = new MyThread();
myThread.start();
}
}
2.线程也可以被synchronized同步锁中断,synchronized属于重型锁,获取到锁的线程就能执行锁内的程序,其他线程只能在外面等待,拿到锁的对象执行完后就会返回锁,然后所有等待这个锁的线程包括刚刚返回锁的线程都会重新争取这个锁。
synchronezed可以锁住一个对象,这个对象可以是任意Object类或者子类。当synchronized锁住一个对象后,别的线程如果也想拿到这个对象的锁,就必须等待这个线程执行完成释放锁,才能再次给对象加锁,这样才达到线程同步的目的。即使两个不同的代码段,都要锁同一个对象,那么这两个代码段也不能在多线程环境下同时运行。所以我们在用synchronized关键字的时候,能缩小代码段的范围就尽量缩小,能在代码段上加同步就不要再整个方法上加同步。这叫减小锁的粒度,使代码更大程度的并发。原因是基于以上的思想,锁的代码段太长了,别的线程是不是要等很久,还不如早点执行一些不需要同步的程序是不是?
举个栗子:
public class MyThread extends Thread{
public MyThread() {
System.out.println(Thread.currentThread().getName());
}
@Override
public void run() {
while(true){
synchronized("123"){
try {
sleep(500);
} catch (Exception e) {
// TODO: handle exception
}
System.out.println(Thread.currentThread().getName()+"执行完了run");
}
}
}
public static void main(String[] args) {
MyThread myThread = new MyThread();
MyThread myThread2 = new MyThread();
myThread.setName("1号线程");
myThread2.setName("2号线程");
myThread.start();
myThread2.start();
}
}
结果:
值得提的一点是,Thread.currentThread()方法是个Thread类的类方法,返回的是当前调用这个方法的对象,所以初始化两个线程的时候打印的名称是main线程的名字,因为是主线程创建了这个线程。
synchronized也可以对方法使用,但是实质仍是对象的锁。当jvm在创建一个对象时,这个对象的头几个字节码中会产生一个锁标志位,调用锁时调用锁的对象就会获取这个标志位,这样其他对象就获取不到了。
举个栗子:
public class MyThread extends Thread{
private Student student;
public MyThread(Student student) {
this.student=student;
}
@Override
public void run() {
while(true){
if (Thread.currentThread().getName().equals("1号线程"))
student.set();
else if (Thread.currentThread().getName().equals("2号线程"))
student.get();
}
}
public static void main(String[] args) {
Student student=new Student();
MyThread myThread = new MyThread(student);
MyThread myThread2 = new MyThread(student);
myThread.setName("1号线程");
myThread2.setName("2号线程");
myThread2.start();
myThread.start();
}
}
public class Student {
public int id=0;
synchronized public void set(){
try {
Thread.sleep(1000);
} catch (Exception e) {
}
System.out.println(Thread.currentThread().getName()+"调用了set,id++");
id++;
}
synchronized public void get(){
try {
Thread.sleep(1000);
} catch (Exception e) {
}
System.out.println(id);
}
}
结果:
实现了同步,且锁住的不是方法而是这个成员方法的对象Student。一般大程序里用的同步对象比较多。
3.wait()方法也能中断线程,这种中断属于一种"挂起"状态,wait()属于Object类的方法,所有对象都能调用这个方法,而且wait()方法必须写到synchronized同步代码块内,线程一旦调用wait()就会进入一个线程等待队列,这个队列跟数据结构里的队列类似,先调用wait()的线程先被notify()唤醒。
wait()和sleep()一样即使被阻塞程序也不会结束,sleep()睡眠时间结束执行完之后的程序就结束,wait()则会一直等待被唤醒。
举个栗子:
public class MyThread extends Thread{
@Override
public void run() {
synchronized("123"){
try {
"123".wait();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
public static void main(String[] args) {
MyThread myThread = new MyThread();
myThread.start();
}
}
这个程序一直执行下去直到其他线程调用notify(),因为notify()只能唤醒第一个wai()的线程,所以在线程多的时候一般不用这个,用的是notifyAll()方法,唤醒所有调用wait()方法的对象。
4.抛出异常可以中断线程,这个中断是真正意义的中断,程序在异常处结束执行。
举个栗子:
public class MyThread extends Thread{
@Override
public void run() {
int i=0;
for(;i<10;i++){
try {
Thread.sleep(1000);
} catch (Exception e) {
}
if (i==5) {
Integer.parseInt("a");
}
}
}
public static void main(String[] args) {
MyThread myThread = new MyThread();
myThread.start();
}
}
结果,程序执行了5秒后因为异常而结束了,也抛出了java.lang.NumberFormatException异常。
5.join()方法可以让调用此线程的方法进入等待状态直到执行此方法的线程结束。join()放数值可以设置最多执行时间ms。
举个栗子:Mom线程,在炒菜时没有盐,让儿子去买盐,只有儿子买到盐回来才能继续炒饭
public class Mom extends Thread{
@Override
public void run() {
try {
System.out.println("mom准备做饭");
System.out.println("mom洗完菜了,炒菜呀");
System.out.println("mom发现没有盐了");
System.out.println("找孩子买盐去");
Son son = new Son();
son.start();
son.join();//会等待son线程的结束
System.out.println("mom继续炒菜");
System.out.println("mom打了一你一顿,因为你藏钱");
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
Son线程负责买盐
public class Son extends Thread{
@Override
public void run() {
try {
System.out.println("你拿着5块蹦蹦跳跳跳的去买盐");
Thread.sleep(2000);
System.out.println("买回盐了。顺便藏点钱");
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
测试方法:
public class Son extends Thread{
@Override
public void run() {
try {
System.out.println("你拿着5块蹦蹦跳跳跳的去买盐");
Thread.sleep(2000);
System.out.println("买回盐了。顺便藏点钱");
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
结果:通过打印的顺序可以发现Mom线程确实必须等Son线程执行完才能继续执行。
6.interrupt()方法,调用该方法会将线程的中断标志位设置为true而不是立刻中断,真正中断需要在线程里判断。
public class MyThread extends Thread{
@Override
public void run() {
while(true){
//interrupted()方法是成员方法,会清除标志位,而类方法isIntertupted()不会
if (this.interrupted()) {
System.out.println(this.isInterrupted());
}
}
}
}
public class Test {
public static void main(String[] args) {
MyThread myThread=new MyThread();
myThread.start();
try {
Thread.sleep(1000);
System.out.println(myThread.isAlive());
} catch (Exception e) {
}
myThread.interrupt();
}
}
结果:true
false
因为在主线程里myThread.interrupt()这里讲myThread的中断标志位设置成了true,所以子线程里this.interrupted()判断结果为true,isInterrupted()也有这个功能,不过interrupted()方法是成员方法,会清除标志位,而类方法isIntertupted()不会。