创建线程的方式
- 继承Thread,重写线程执行的任务方法run();
- 实现Runnable接口,重写线程执行的任务方法run(); 创建线程时, 任务接口的实例做为构造参数传入,接口的run方法将成为线程要执行的目标方法.
- 实现Callable的call方法,(call方法可以异步返回结果)
多线程并发数据安全问题
在多线程并发执行时,有多条语句需要对共享数据
做读写不同的操作时,很可能出现 一个线程的读
操作没执行完成,被另一个线程写
改变掉部分共享数据
,从而导致读
到的数据是错误的; 或者,一个线程的写
操作没执行完成,被另一个线程的读
操作读
完了没写
完的错误的数据;
synchronized
为了防止多线程共享数据访问出错,把操作多线程共享数据
的代码都标记出来,安排一个保安
时刻盯着,不管哪个线程哪个方法, 让同一个保安
盯着的,就是来操作不能同时操作的共享数据
的, 必须排个队一个执行完成下一个才能执行,以防出错。
同步锁,需要一个监视器(保安
)对象(任意对象可以做为监视器);
同步锁的使用方式有 同步代码块
和同步方法
两种,同步代码块的监视器需要传入个任意对象
,同步(实例)方法的监视器是this
,同步(静态)方法的监视器是当前类的class对象
;
死锁
多线程并发的时候, 多个嵌套的同步代码,交叉使用相同的多个锁,就很有可能产生死锁
public class DeadLockDemo {
private static Object lock_a = new Object();
private static Object lock_b = new Object();
public static void main (String[] args){
new Thread(){
@Override
public void run(){
while(true){
synchronized(lock_a){
System.out.println(Thread.currentThread().getName()+", 持有lock_a,等待lock_b......");
synchronized(lock_b){
System.out.println(Thread.currentThread().getName()+", 持有lock_a和lock_b");
}
}
}
}
}.start();
Thread t1 = new Thread(){
@Override
public void run(){
while(true){
synchronized(lock_b){
System.out.println(Thread.currentThread().getName()+", 持有lock_b,等待lock_a......");
synchronized(lock_a){
System.out.println(Thread.currentThread().getName()+", 持有lock_b和lock_a");
}
}
}
}
}.start();
}
}
sleep
当前线程暂停执行一段时间,之后继续。不会释放锁
,也就是说想要执行此锁监视的同步代码的别的线程仍然需要傻傻地等待。
wait
当前线程进入阻塞状态,会释放锁
,需要被唤醒之后,继续竞争执行的机会
守护线程
Thread.setDaemon(true/false) // 在Thread.start()开启线程之前设置
守护线程是为用户线程提供辅助的线程,当运行的只剩守护线程时,程序结束.
停止线程
-
Thread.stop(); // 过时方法,不推荐使用
-
使用条件变量停止一个线程
class StopThreadByFlag extends Thread {
// volatile 保证内存的可见性, 禁止指令重排序
private volatile boolean flag = true;
private int counter;
public void terminate() {
terminateFlag = false;
}
@Override
public void run() {
while (flag) { // 标记值为false自然会停止该线程
try {
Thread.sleep(1000);
// Thread.wait(); // 如果线程处于冻结状态, 修改标记的值不能马上停止线程
System.out.println("counter:" + counter++);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
- 使用线程的intercept方法停止线程
如果希望能够立刻退出线程,可以使用intercept方法。
public static void main(String[] args) {
Thread t = new Thread(()->{
int counter =0;
while(!Thread.currentThread().isInterrupted()){ // 3. 再次执行到这里 线程状态为 "已中断", 该线程将停止执行
try {
Thread.sleep(1000);
System.out.println("cuonter:"+counter++);
} catch (Exception e) {
e.printStackTrace();
// 2. 注意这里,需要再次调用intercept
Thread.currentThread().interrupt();
}
}
});
t.start();
try { Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); }
t.interrupt(); // 1. 强制唤醒该线程,中断该线程, 会抛出 InterruptedException 异常
}
每个线程存在interrupted status,当线程在sleep或wait,join,此时如果别的线程调用此线程的 interrupt()方法,此线程会被唤醒并被要求处理InterruptedException;
如果此线程在运行中,则不会收到提醒。但是此线程的 interrupted status 会被设置,可以通过isInterrupted()查看并作出处理。
- join方法
join(), 对于线程A和线程B两个线程,如果在线程B中调用线程A的join方法,则B由wait方法进入阻塞状态,直到A执行完之后notifyAll重新唤醒B。
public static void main(String[] args) {
final Thread t0 = new Thread(()->{
for(int counter = 0; counter < 100000000; counter++){
System.out.println("counter============"counter);
}
});
t0.start();
Thread t1 = new Thread(()->{
for(int counter = 0; counter < 100000000; counter++){
t0.join(); // 当前线程t1 会 等待线程t0完全执行完成,才继续执行.
// t0.join(n 毫秒/纳秒); // 当前线程t1会 暂时放弃执行权, 等待线程t0执行n毫秒/纳秒,t1才继续竞争执行.
System.out.println("counter============"counter);
}
});
t1.start();
}