synchronized的学习
1、synchronized的用处,确保一个时刻只有一个线程可以访问该代码
确保一个时刻只有一个线程可以访问该代码
解决的问题,举例:
创建两个线程thred1和thred2,分别调用method1将全局变量i加十万次,不加线程锁,每次都会小于20万次,原因是因为每次线程拿到的可能不是最新的数据,比如thread1可能还没将i加到2,thread2此时获取到的i不是最新的,也就造成了数据错误, 这也就是我们此次要解决的问题
static Integer i = 0;
public static void main(String[] args) throws InterruptedException {
Thread thread1 = new Thread(() -> { method1();}, "thread1");
Thread thread2 = new Thread(() -> {method1();}, "thread2");
thread1.start();
thread2.start();
//join等待执行完再执行打印
thread1.join();
thread2.join();
System.out.println("此时i等于:" + i);
}
private static void method1() {
for(int j = 0;j<100000;j++){
i = i + 1;
}
}
2、synchronized的使用方式
1、同步代码块,synchronized (this), 注意一点,这种用法前提是方法为普通方法
Runnable runnable = new Runnable() {
@Override
public void run() {
//同步方法块需要方法为普通方法,静态方法不可加this
synchronized (this){
for(int j = 0;j<100000;j++){
i = i + 1;
}
}
}
};
Thread thread1 = new Thread(runnable, "thread1");
Thread thread2 = new Thread(runnable, "thread2");
thread1.start();
thread2.start();
//join等待执行完再执行打印
thread1.join();
thread2.join();
System.out.println("此时i等于:" + i);
2、类方法块, synchronized (Object.class), 即使是new多个对象访问这一个方法,也是起作用的
static Integer i = 0;
public static void main(String[] args) throws InterruptedException {
Thread thread1 = new Thread(() ->{new ThreadDemo1().method1();}, "thread1");
Thread thread2 = new Thread(() ->{new ThreadDemo1().method1();}, "thread2");
thread1.start();
thread2.start();
//join等待执行完再执行打印
thread1.join();
thread2.join();
System.out.println("此时i等于:" + i);
}
public static void method1() {
synchronized (ThreadDemo1.class){
for(int j = 0;j<100000;j++){
i = i + 1;
}
}
}
3、普通方法块,必须为同一个类中的同一个方法,如果new一个新的类再去调用,锁就不起作用
Runnable runnable = new Runnable() {
@Override
public synchronized void run() {
//同步方法块需要方法为普通方法,静态方法不可加this
for(int j = 0;j<100000;j++){
i = i + 1;
}
}
};
Thread thread1 = new Thread(runnable, "thread1");
Thread thread2 = new Thread(runnable, "thread2");
thread1.start();
thread2.start();
//join等待执行完再执行打印
thread1.join();
thread2.join();
System.out.println("此时i等于:" + i);
4、静态方法块 ,即使new多个对象,访问静态方法块锁也会起作用
static Integer i = 0;
public static void main(String[] args) throws InterruptedException {
Thread thread1 = new Thread(() -> { method1();}, "thread1");
Thread thread2 = new Thread(() -> {method1();}, "thread2");
thread1.start();
thread2.start();
//join等待执行完再执行打印
thread1.join();
thread2.join();
System.out.println("此时i等于:" + i);
}
private static synchronized void method1() {
for(int j = 0;j<100000;j++){
i = i + 1;
}
}
3、方法块使用总结
如果你认真看了以上代码,下面总结一下
1、静态方法锁和类锁都是即使new多个对象,只要访问该方法,锁就会起作用。这是因为类锁和静态方法锁都是在系统启动时就加了锁,这块被锁住的方法一直也只会有一个锁
2、普通方法锁和同步代码块都是只作用于当前,如果再有new一个新对象,就是重新的一个锁
3、当方法抛出异常,会直接释放锁,被下一个线程调用
4、synchronized的特性
1、互斥性(这名字起的真高大上):其实就是保证方法在一个时刻只能被一个线程所调用,只有把锁释放时,才可以被其他线程调用
2、可重入性:当拿到锁的使用权时,可以再次调用该锁。
好处:避免死锁,其实就是因为锁的特性是互斥性的,必须先释放锁然后才能被其他锁调用,如果没有可重入性,锁还被自己调用着呢,就不让自己再调用了,那就死锁了
举例:thread1调用method1获取到了ThreadDemo1.class这个锁,又去调用了method2再次拿了ThreadDemo1.class这个锁,也就证明了可重入性
static Integer i = 0;
public static void main(String[] args) throws InterruptedException {
Thread thread1 = new Thread(() -> {
new ThreadDemo1().method1();
}, "thread1");
thread1.start();
//join等待执行完再执行打印
thread1.join();
System.out.println("此时i等于:" + i);
}
private void method1() {
synchronized (ThreadDemo1.class) {
for (int j = 0; j < 100000; j++) {
i = i + 1;
}
}
method2();
}
private void method2() {
synchronized (ThreadDemo1.class) {
for (int j = 0; j < 100000; j++) {
i = i + 1;
}
}
}
5、死锁的理解
两个线程已经在外层获取到了各自的锁,再去内层获取对方锁的时候,发现被对方占用,也就造成了死锁
代码解释:线程1调用方法1得到了锁1,线程2调用方法2得到了锁2,线程1再去调用锁2时发现锁2被线程2锁着呢,也就调用不了,同理,线程2再去调用锁1时发现锁1被线程1锁着呢
public static final String lock1 = "lock1";
public static final String lock2 = "lock2";
public static void main(String[] ars) {
Thread thread1 = new Thread(() -> {method1();}, "线程1");
Thread thread2 = new Thread(() -> {method2();}, "线程2");
//两个线程已经在外层获取到了各自的锁,再去内层获取对方锁的时候,发现被对方占用,也就造成了死锁
thread1.start();
thread2.start();
}
private static void method1() {
try{
Thread thread = Thread.currentThread();
System.out.println(thread.getName() + "启动");
while(true){
synchronized(lock1){
System.out.println(thread.getName() + "拿到了lock1锁");
//等待时间让线程锁住
Thread.sleep(100);
System.out.println(thread.getName() + "要去锁lock2锁");
synchronized(lock2){
System.out.println(thread.getName() + "要去锁lock2锁");
}
}
}
}catch(Exception e){
e.printStackTrace();
}
}
private static void method2() {
try{
Thread thread = Thread.currentThread();
System.out.println(thread.getName() + "启动");
while(true){
synchronized(Demo1.lock2){
System.out.println(thread.getName() + "拿到了lock2锁");
//等待时间让线程锁住
Thread.sleep(100);
System.out.println(thread.getName() + "要去锁lock1锁");
synchronized(Demo1.lock1){
System.out.println(thread.getName() + "要去锁lock1锁");
}
}
}
}catch(Exception e){
e.printStackTrace();
}
}