目录
t.interrupt()/Thread.interrupted()
1.Thread下的方法
Thread.currentThread()
可以获取当前的线程,拿到这个后可以获取线程的一些信息,例如名字,状态,id
public class Test2 {
public static void main(String[] args) {
Thread t = new Thread(() ->{
System.out.println("你好 "+Thread.currentThread().getState());
});
t.start();
System.out.println(t.getName());
System.out.println(Thread.currentThread().getName());
}
}
运行结果:
t.join()
join的意思是连接合并,所以这里可以看作是合并执行流,下面的例子就是主线程等待子线程,合并成一个线程。
public class Test2 {
public static void main(String[] args) throws InterruptedException {
Thread t = new Thread(() ->{
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("你好 "+Thread.currentThread().getState());
});
t.start();
//会一直的阻塞等待,直到子线程结束
t.join();
System.out.println("我是主线程,已经等了5秒了~~");
}
}
重载形式:t.join(1000),里面的单位是毫秒,所以这里表示等待一秒之后就不等待了 ,而是也执行主线程,但是如果主线程执行完毕后,子线程没执行完毕也会继续的执行,直到结束。
public class Test2 {
public static void main(String[] args) throws InterruptedException {
Thread t = new Thread(() ->{
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("你好 "+Thread.currentThread().getState());
});
t.start();
t.join(1000);
System.out.println("等了一秒不等了");
System.out.println("主线程结束");
}
}
运行结果:
t.interrupt()/Thread.interrupted()
对于interrupt(),只是给t线程发了消息(给线程t设置了停止标值,但是实际不会影响t的执行),前提是t不处于阻塞状态,如果此时t处于阻塞的状态则会以异常的形式通知t。
public class Test2 {
public static void main(String[] args) throws InterruptedException {
Thread t = new Thread(() ->{
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("你好 "+Thread.currentThread().getState());
});
t.start();
//先睡完一秒后,线程t一定处于睡眠状态
Thread.sleep(1000);
t.interrupt();
System.out.println("主线程结束");
}
}
运行结果:捕获异常后继续执行后面的代码。
对于Thread.interrupted(),用于检测当前的线程是否被终止,这个就是完全有我们自己控制了。如果我们通过上述的方法,检测到希望被终止了,我们可以选择继续做其他的事情,也可以停止,这在于我们自己写的代码。
Thread.yield()
作用:让出CPU,线程从运行->就绪(随时可以被调度)
2.volatile
对于volatile是修饰的变量,作用可以保护变量的内存可见性、保护代码重排序。
1.volatile保证变量对所有线程的可见性:当volatile变量被修改,新值对所有线程会立即更新。或者理解为多线程环境下使用volatile修饰的变量的值一定是最新的。
2.jdk1.5以后volatile完全避免了指令重排优化,实现了有序性。
原理:
获取JIT(即时Java编译器,把字节码解释为机器语言发送给处理器)的汇编代码,发现volatile多加了lock addl指令,这个操作相当于一个内存屏障,使得lock指令后的指令不能重排序到内存屏障前的位置。这也是为什么JDK1.5以后可以使用双锁检测实现单例模式。
lock前缀的另一层意义是使得本线程工作内存中的volatile变量值立即写入到主内存中,并且使得其他线程共享的该volatile变量无效化,这样其他线程必须重新从主内存中读取变量值。
volatile和synchronized的区别
1.volatile 本质是在告诉 JVM 当前变量在寄存器(工作内存)中的值是不确定的,需要从主存中读取; synchronized 则是锁定当前变量,只有当前线程可以访问该变量,其他线程被阻塞住。
2.volatile 仅能使用在变量级别;synchronized 则可以使用在 变量. 方法. 和类级别。
3.volatile 仅能实现变量的修改可见性,不能保证原子性;而synchronized 则可以 保证变量的修改可见性和原子性
4.volatile 不会造成线程的阻塞;synchronized 可能会造成线程的阻塞。
5.volatile 标记的变量不会被编译器优化;synchronized 标记的变量可以被编译器优化。
3. synchronized
用法
1.修饰普通方法:作用于当前对象实例,进入同步代码前要获得当前对象实例的锁
2.修饰静态方法:作用于当前类,进入同步代码前要获得当前类对象的锁,synchronized 关键字加到 static 静态方法和 synchronized(class)代码块上都是是给 Class 类上锁
3.修饰代码块:指定加锁对象,对给定对象加锁,进入同步代码库前要获得给定对象的锁
作用
1.原子性:确保线程互斥的访问同步代码;
2.可见性:保证共享变量的修改能够及时可见,其实是通过Java内存模型中的 “对一个变量unlock操作之前,必须要同步到主内存中;如果对一个变量进行lock操作,则将会清空工作内存中此变量的值,在执行引擎使用此变量前,需要重新从主内存中load操作或assign操作初始化变量值” 来保证的;
3.有序性:有效解决重排序问题,即 “一个unlock操作先行发生(happen-before)于后面对同一个锁的lock操作”。
wait()/notify()
对于wait()和notify()方法,都是Object类中的方法,所以Java中的对象都有这两个方法。如果要使用这两个方法,必须对对象进行加synchronized锁。
代码演示:
public class Test2 {
static class MyThread extends Thread {
Object o;
public MyThread(Object o){
this.o = o;
}
@Override
public void run() {
synchronized (o){
try {
o.wait(); //1.释放o锁 2.阻塞等待被notify 3.再次加锁
System.out.println("子线程被唤醒了");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
public static void main(String[] args) throws InterruptedException {
Object o = new Object();
//确保是同一个对象
MyThread myThread = new MyThread(o);
myThread.start();
System.out.println("两秒后唤醒子线程");
Thread.sleep(2000);
synchronized (o){
o.notify();
}
}
}
注意:wait-notify是没有状态的,就是如果先notify然后在wait,wait是无法感知的,会永远等下去。
面试wait和sleep的区别
1.sleep方法:是Thread类的静态方法,当前线程将睡眠n毫秒,线程进入阻塞状态。当睡眠时间到了,会解除阻塞,进入可运行状态,等待CPU的到来。睡眠不释放锁(如果有的话)。
2.wait方法:是Object的方法,必须与synchronized关键字一起使用,线程进入阻塞状态,当notify或者notifyall被调用后,会解除阻塞。但是,只有重新占用互斥锁之后才会进入可运行状态。睡眠时,会释放互斥锁。
3.sleep 方法没有释放锁;而 wait 方法释放了锁 。
4.sleep 通常被用于暂停执行;wait 通常被用于线程间交互/通信
5.sleep() 方法执行完成后,线程会自动苏醒。wait的重载可以使用 wait(long timeout)超时后线程会自动苏醒。wait() 方法被调用后,线程不会自动苏醒,需要别的线程调用同一个对象上的 notify() 或者 notifyAll() 方法。
总结
加油哦~~