前阵子,重新复习并学习了有关多线程的知识,下面是我的一些学习笔记。每一点,只为尽量简明的表达出知识内容,不会全部具体展开讲,并附上相关代码。(其实,我现在也还做不到全部展开来讲啦,啊哈哈),日后这个笔记会不断补充更新。
1.能中断的线程
public class Test1 {
public static void main(String[] args) throws Exception {
Thread thread = new Thread() {
public void run() {
while (!this.isInterrupted()) {
try {
Thread.sleep(10);
} catch (InterruptedException e) {
System.out.println("如果在睡眠(sleep)状态或者等待(wait)下被中断的话,就会进入到这里");
return;
}
System.out.println(1111);
}
};
};
thread.start();
Thread.sleep(100);
thread.interrupt();
}
}
tips:1.上面实际上,展示了两种中断线程的方法,一种是通过while循环,不断的判断this.isInterrupted()状态。不过这种判断方法,有一个缺点,就是万一循环里面的操作十分耗时,如果我们想在那操作完成前,就中断线程的话,其实是不可以的。2.利用异常的方法,中断线程也是可以的,但是缺点跟上面一样,我们不能依靠这种方法,中断一个正在进行严重耗时操作的线程,因为要么已经跳过了sleep或者还没到达sleep(wait应用场景就不一样了,这里不展开讨论),所以是进不了catch的。
所以,我们可以得到一个结论:interrupt()并不直接中断线程,而是设定一个中断标识,然后由程序进行中断检查,确定是否中断。
2.线程可以利用suspend方法(暂停),和resume方法(恢复),对线程进行暂停和恢复。需要注意的是,suspend()方法是不释放锁的,也就是说如果某个线程在同步代码块中暂停了,那其他线程,就无法再进入,利用这个同步(监听)对象的同步代码块里面了。而且这两个方法都是过时被废弃的方法。
3.synchronized锁重入
在使用synchronized时,当一个线程得到一个对象锁之后,再次请求此对象锁时,是可以再一次得到这个对象的锁的。(本来就得到了嘛= =)这也证明了在一个synchronized方法或者synchronized代码块内部,调用依赖于这个对象锁的synchronized方法/块时,是永远可以得到锁的。可重入锁也支持在父子类继承的环境中。即:
public synchronized void aa() {
// 某些操作
// 当一个方法进行到这里之后,因为bb方法也需要获得锁,而此时当前线程已经获得了这个当前对象的对象锁,所以bb方法的锁是可以马上获得的。即不会被其他的线程先获得锁,然后先执行了bb方法这样。
bb();
// 某些操作
}
public synchronized void bb() {
// 某些操作
}
public boolean cancel(boolean mayInterruptIfRunning)
Future
复制的描述
cancel
时,如果调用成功,而此任务尚未启动,则此任务将永不运行。如果任务已经启动,则
mayInterruptIfRunning
参数确定是否应该以试图停止任务的方式来中断执行此任务的线程。
此方法返回后,对 Future.isDone()
的后续调用将始终返回 true
。如果此方法返回 true
,则对 Future.isCancelled()
的后续调用将始终返回 true
。
Future
中的
cancel
mayInterruptIfRunning
- 如果应该中断执行此任务的线程,则为
true
;否则允许正在运行的任务运行完成
false
,这通常是由于它已经正常完成;否则返回
true
5.当一个线程执行的代码出现异常时,其所持有的锁会自动释放
6.同步不具有继承性,即父类的某个方法是有synchronized关键字的,但是子类继承父类时覆盖了这个方法后,把synchronized关键字去掉了的话,这个子类的这个方法就不再是同步的方法了。
7.synchronized关键字加到static静态方法上是给Class类上锁,而synchronized关键字加到非static静态方法上,是给对象加锁。而Class锁可以对类的所有对象实例起作用。
8.关键字volatile的作用,就是强制从公共堆栈中取得变量的值,而不是从线程私有数据栈中取得变量的值。但是,它只能保证可见性,并不能保持原子性,就是它是非线程安全的。
9.关键字synchronized可以使多个线程访问同一个资源具有与同步性,而且它还具有将线程工作内存中的私有变量与公共内存中变量同步的功能。
public class Test2 {
public static void main(String[] args) {
final A a = new A();
new Thread() {
public void run() {
a.startMethod();
};
}.start();
try {
// 模拟一些耗时操作等,确保上面的线程已经执行了startMethod
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
new Thread() {
public void run() {
a.stopMethod();
};
}.start();
}
}
class A {
// private volatile boolean isStop = false;如果使用volatile关键字的话,就不需要同步代码块了
private boolean isStop = false;
public void startMethod() {
while (!isStop) {
// 如果变量不用volatile修饰,这里又不加这个同步代码块的话,当A线程调用这个类的一个实例的startMethod后,B线程调用再调用这个实例的stopMethod方法的话,while循环还是不会停止的
// synchronized (this) {
//
// }
}
System.out.println("停下来了");
}
public void stopMethod() {
System.out.println("结束了吗?");
isStop = true;
}
}
10.在调用wait之前,线程必须获得该对象的对象级别锁,即只能在同步方法或同步块中调用wait方法。在执行wait方法之后,当前线程释放锁
11.在执行notify方法之后,当前线程不会马上释放该对象锁,要等到退出synchronized代码块范围之后,当前线程才会释放锁。当已经wait的方法,重新获得锁执行下面的代码时,也是如此。也是要执行到退出synchronized代码块范围之后再释放锁。
12.ThreadLocal主要解决的就是每个线程绑定自己的值,可以将ThreadLocal类比喻成全局存放数据的盒子,盒子中可以存储每个线程的私有数据。即即使将它弄成static(static ThreadLocal
13.创建一个Timer就是启动一个新的线程,这个新启动的线程并不是一个守护线程(守护线程的意思就是,如果当前没有非守护线程,只剩下守护线程时,守护线程会自动结束),它会一直在运行。new Timer(true);这样可以创建一个守护线程的Timer 。
14.TimerTask类中的cancel方法的作用是将自身从任务队列中清除。Timer类中的cancel方法的作用是将任务队列中的全部任务清空。但是,需要注意的是Timer类中的cancel方法有时不一定会停止执行计划任务,而是正常执行。这是因为Timer类中cancel方法有时并没有争抢到queue锁,所以TimerTask类中的任务继续正常执行。
15.ThreadGroup线程组,既可以添加线程也可以添加线程组,方便统一管理里面的线程。
16.ReentrantLock:一个可重入的互斥锁 Lock
,它具有与使用 synchronized
方法和语句所访问的隐式监视器锁相同的一些基本行为和语义,但功能更强大。
private ReentrantLock lock = new ReentrantLock();
public void aa(String s) {
lock.lock();
try {
System.out.println("进来了");
Thread.sleep(1000);
System.out.println(s);
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
17.使用Condition实现等待/通知(代替notify和wait)
public class Test {
public static void main(String args[]) throws Exception {
new Thread() {
@Override
public void run() {
await("aaa");
}
}.start();
new Thread() {
@Override
public void run() {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
signal("bbb");
}
}.start();
}
private static ReentrantLock lock = new ReentrantLock();
private static Condition condition = lock.newCondition();
public static void await(String s) {
lock.lock();
try {
System.out.println("准备等待");
condition.await();
System.out.println(s);
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
public static void signal(String s) {
lock.lock();
try {
System.out.println("准备唤醒");
condition.signal();
System.out.println(s);
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
}
if (lock.tryLock()) {
System.out.println(s + "拿到锁了");
} else {
System.out.println(s + "没有拿到锁");
}