锁:
Synchronized,多线程并发编程
package morning;
public class SynchronizedUse {
//同步普通方法,只能用在单例上,同一个实例只有一个线程能获取锁进入这个方法
public synchronized void method(){
System.out.println(1);
}
/*
同步静态方法,无论有多少实例,同时只能有一个线程能获取锁然后
进入这个方法,是类级别的锁,一旦任何一个线程进入这个方法,其他所有线程将无法
访问这个类的任何同步类锁的方法
*/
public synchronized static void method2(){};
/*
同步类,以下两种是同步块的用法,表示只有获取到这个类锁才能进入这个代码块
*/
public void s(){
synchronized (SynchronizedUse.class){}
}
public void s2(){
synchronized (this.getClass()){}
}
/*
同步this实例,表示锁住整个当前对象实例,只有获取到这个实例的锁才能进入这个方法,
用法和同步普通方法一样,都是锁住整个当前实例
*/
public void s3(){
synchronized (this){}
}
/*
类锁与实例锁不相互阻塞,但相同的当前实例锁,相同的类锁,相同的对象锁会相互阻塞
*/
}
重量级所,jdk1.6对synchronized进行了优化
jdk1.6为了减少获得锁和释放锁带来的性能消耗引入的偏向和轻量级锁
三种方式加锁
1,修饰实例方法,作用于当前实例加锁,进入同步代码之前获得当前实例的锁
2,静态方法,作用于当前类对象加锁,进入同步代码前要获得的是当前类对象的锁
3,代码块,指定加锁对象,给指定对象加锁,进入代码块之前要获得给定对象的锁
1,实例方法,调用该方法的实例
2,静态方法,类对象,类名.class
3,this,调用该方法的实例对象
4,类对象,类对象 (推荐使用)
关于同步方法:
同步方法依然会涉及到同步锁对象,不需要我们写出来
非静态的同步方法,同步锁就是this
静态的同步方法,同步监视器就是类本身
同步代码块
选好同步监视器,推荐使用类对象,第三方对象this
在实现接口创建的线程类中,同步代码块不可以用this来充当同步锁
同步的方式,解决了线程安全的问题,操作同步代码块时,只有一个线程能参与,其他线程等待,相当于一个单线程的过程,效率低
synchronize只针对当前的jvm可以解决线程安全的问题
synchronize不可以跨jvm解决问题
死锁:
多个线程同时被阻塞,他们中的一个或者全部都在等待某个资源的释放,由于线程无限制的阻塞,程序就不可能正常终止
java中死锁产生条件
1,互斥使用,当资源被一个线程使用,别的线程不能使用
2,不可抢占,资源请求者不能从占有者中抢夺资源,资源只能从占有者主动释放
3,请求和保持,,
4,循环等待,存在一个等待的队列,p1占有p2的资源 p2占有p3的资源p2占有p3的资源,形成一个等待环路
互相占有对方的资源但是都不释放
class LockA implements Runnable {
@Override
public void run() {
System.out.println(new Date().toString() + "LockA开始执行...");
try {
while (true) {
synchronized (Ch04.obj1) {
System.out.println(new Date().toString() + "LockA锁住了obj1");
Thread.sleep(8000);
}
synchronized (Ch04.obj2) {
System.out.println(new Date().toString() + "LockA锁住了obj2");
Thread.sleep(70*1000);
}
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
class LockB implements Runnable {
@Override
public void run() {
System.out.println(new Date().toString() + "LockB开始执行...");
try {
while (true) {
synchronized (Ch04.obj2) {
System.out.println(new Date().toString() + "LockB锁住了obj2");
Thread.sleep(6000); //3s
}
synchronized (Ch04.obj1) {
System.out.println(new Date().toString() + "LockB锁住了obj1");
Thread.sleep(100*1000); // 1min
}
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public class Ch04 {
public static String obj1 = "obj1";
public static String obj2 = "obj2";
public static void main(String[] args) {
LockA lockA = new LockA();
new Thread(lockA).start();
LockB lockB = new LockB();
new Thread(lockB).start();
}
}
线程重入
任意线程在拿到锁之后,再次获取该锁不会被该锁所阻碍
线程不会被自己锁死,SYNCHRONIZED可重入锁
private static final Object M1 = new Object();
private static final Object M2 = new Object();
public static void main(String[] args) {
new Thread(() -> {
synchronized (M1) {
synchronized (M2) {
synchronized (M1) {
synchronized (M2){
System.out.println("hello lock");
}
}
}
}
}).start();
}
}
jdk1.6之后锁升级
1,无锁,不加锁
2,偏向锁,不锁锁,当只有一个线程争夺时,偏向某一个线程,这个线程不加锁
3,轻量级锁,少量线程来了之后,先尝试自旋,不挂起线程,
4,重量级锁,排队挂起(暂停)线程(synchronized)
挂起线程和恢复线程需要转入内核态中完成这些操作,给系统的并发性带来很大压力,
在许多应用上共享数据的锁定状态,只会持续很短的时间,为了这段时间去恢复和挂起并不值得,让后面的线程等一下,不要放弃处理器的执行时间,锁为了让线程等待,让线程形成一个循环,自旋(自旋锁)
Object类
wait(); 不叫不能进来
wite(long timeout):让当前线程进入等待状态 到时间自己进来,或者叫
notify():唤醒正在等待的下一个线程 叫
notifyall();唤醒正在等待的所有线程 叫所有人进来
线程间的通信:
比如两条线程共同运行,如果线程a先走,线程b就要等待,等a走完,唤醒b,b再走
方法的总结:
1,thread的两个静态方法
sleep释放cpu资源,但是不会释放锁
yield释放cpu的执行权,但是保留了cpu的执行资格,不常用
join方法:插队,出让了执行权,join就加入进来
wait:释放资源,释放锁
面试题:sleep和wait的区别?
线程的退出
使用退出标志,让线程正常退出run方法结束后线程终止
interrupt方法:中断线程
* 调用interrupt方法会抛出InterruptedException异常,
* 捕获后再做停止线程的逻辑即可。
*
* 如果线程while(true)运行的状态,interrupt方法无法中断线程。
class MyThread02 extends Thread {
private boolean flag = true;
@Override
public void run() {
while(flag) {
synchronized (this){
// try {
// wait();
// } catch (InterruptedException e) {
// e.printStackTrace();
// }
try {
sleep(10000);
} catch (InterruptedException e) {
e.printStackTrace();
this.stopThread();
}
}
}
}
public void stopThread() {
System.out.println("线程停止运行...");
this.flag = false;
}
}
public class Ch04 {
// public void show() {
// try {
// wait();
// // 线程异常终止 异常
// } catch (InterruptedException e) {
// e.printStackTrace();
// }
// }
public static void main(String[] args) {
MyThread02 myThread02 = new MyThread02();
myThread02.start();
System.out.println("线程开始...");
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
// 中断线程的执行
myThread02.interrupt();
}
}
线程的常用方法:|
* Thread类中的方法
* 1.start:启动当前线程;执行run方法
* 2.run:
* 3.currentThread:静态方法,获取当前正在执行的线程
* 4.getId():返回此线程的唯一标识
* 5.setName(String):设置当前线程的name
* 6.getName():获取当前线程的name
* 7.getPriority():获取当前线程的优先级
* 8.setPriority(int):设置当前线程的优先级
* 9.getState():获取当前线程的声明周期
* 10.interrupt():中断线程的执行
* 11.interrupted():查看当前线程是否中断
package eveing;
public class Test {
public static void main(String[] args) {
Thread thread=Thread.currentThread();
System.out.println(thread);
MyThread thread1=new MyThread();
thread1.start();
thread1.setDaemon(true);
System.out.println(thread1.isDaemon());
}
}
class MyThread extends Thread{
@Override
public void run() {
System.out.println(getId());
setName("线程一");
System.out.println(getName());
}
}
valied修饰属性的两个作用
1,不许指令重排
2,不进缓存
synchronized锁的单例模式(线程安全)
package eveing;
public class Singleton {
private static Singleton instant;
private Singleton(){}
public static Singleton getInstance(){
if(instant==null){
synchronized (Singleton.class){
if(instant==null){
instant=new Singleton();
}
}
}
return instant;
}
}