java并发—小总结

1.sleep是否释放锁 【synchronized 和 lock】

1.synchronized 错误案例!

//错误示范:因为两个线程各自行为。不消费同个资源,无所谓的是否释放锁
@Slf4j
public class MyThread2 extends Thread {
    @SneakyThrows
    @Override
    public synchronized void run() {
        String name = Thread.currentThread().getName();
        log.info("线程获取=====》锁{}", name);
        try {
            Thread.sleep(3000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        log.info("线程释放=====》锁{}", name);

    }
    public static void main(String[] args) {
        MyThread2 mt=new MyThread2();
        mt.start();
        MyThread2 mt2=new MyThread2();
        mt2.start();
    }
//结果:
: 线程获取=====》锁Thread-0
: 线程获取=====》锁Thread-1
: 线程释放=====》锁Thread-1
: 线程释放=====》锁Thread-0

2.synchronized 正确案例——释放锁,至少两个线程针对同一资源消费

@Slf4j
public class MyThread2 implements Runnable {
    @SneakyThrows
    @Override
    public synchronized void run() {
        String name = Thread.currentThread().getName();
        log.info("线程获取=====》锁{}", name);
        try {
            Thread.sleep(3000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        log.info("线程释放=====》锁{}", name);

    }
    public static void main(String[] args) {
        MyThread2 run=new MyThread2();
        //两个线程消耗/竞争同资源run
        new Thread(run).start();
        new Thread(run).start();
    }
}
//
线程获取=====》锁Thread-0
 线程释放=====》锁Thread-0
 线程获取=====》锁Thread-1
 线程释放=====》锁Thread-1

结论:Thread-0从获取 直到 释放锁,其他线程无法获取该锁。sleep方法不释放锁,锁的对象是this/run实例   对象锁

3.lock案例

@Slf4j
public class Runn2 implements Runnable {
    private static final Lock lock = new ReentrantLock();
    @Override
    public void run() {
        String name = Thread.currentThread().getName();
        lock.lock();
        log.info("线程获取=====》锁{}", name);
        try {
            Thread.sleep(3000);
            log.info("线程释放=====》锁{}", name);
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }
    public static void main(String[] args) {
        Runn2 run = new Runn2();
        new Thread(run).start();
        new Thread(run).start();
    }
}
//
 线程获取=====》锁Thread-1
 线程释放=====》锁Thread-1
 线程获取=====》锁Thread-0
 线程释放=====》锁Thread-0

总结:上述synchronized、lock 证明sleep()方法不释放锁!,但不占用CPU资源

2. 响应中断

/**
 * 线程每隔1秒钟输出当前时间,运行时被中断
 */
public class Runn2 implements Runnable {

    @Override
    public void run() {
        for (int i = 0; i < 10; i++) {
            System.out.println(new Date());
            try {
                TimeUnit.SECONDS.sleep(1);
            } catch (InterruptedException e) {
                System.out.println("我被中断了!");
                e.printStackTrace();
            }
        }
    }

    public static void main(String[] args) throws InterruptedException {
        Thread thread = new Thread(new Runn2());
        thread.start();
        Thread.sleep(6500);
        thread.interrupt();//指定线程发送中断请求,并不是立马停止执行。
    }
}
//
Mon Oct 30 16:09:21 CST 2023
Mon Oct 30 16:09:22 CST 2023
Mon Oct 30 16:09:23 CST 2023
Mon Oct 30 16:09:24 CST 2023
Mon Oct 30 16:09:25 CST 2023
Mon Oct 30 16:09:26 CST 2023
Mon Oct 30 16:09:27 CST 2023
我被中断了!
Mon Oct 30 16:09:27 CST 2023
java.lang.InterruptedException: sleep interrupted
	at java.base/java.lang.Thread.sleep(Native Method)
	at java.base/java.lang.Thread.sleep(Thread.java:334)
	at java.base/java.util.concurrent.TimeUnit.sleep(TimeUnit.java:446)
	at com.Xx.XX.XX.common.Runn2.run(Runn2.java:28)
	at java.base/java.lang.Thread.run(Thread.java:829)
Mon Oct 30 16:09:28 CST 2023
Mon Oct 30 16:09:29 CST 2023

总结:线程中断是由线程自己决定的。其他线程只是给当前线程设置中断标志位。上述图中,主线程给副线程thread.interrupt()设置中断。  Thread.interrupted() 此方法检测线程是否中断,及清除中断标志位。第二次调用会返回false。 重点:图中副线程被中断,执行                TimeUnit.SECONDS.sleep(1)时抛出中断异常。【一般情况下,捕获中断异常,开发人员在代码中返回,或不再后续执行处理数据。

中断作用:

假设用户下载一个很大的文件,当前网络比较慢,导致下载很缓慢,用户需要取消下载。
此时则需要让当前正在运行的线程终止!

3.Thread中interrupted()方法和isInterrupted()方法区别总结

1.interrupted()是静态方法:内部实现是调用的当前线程的isInterrupted(),并且会重置当前线程的中断状态

2.isInterrupted()是实例方法,是调用该方法的对象所表示的那个线程的isInterrupted(),不会重置当前线程的中断状态

Thread中interrupted()方法和isInterrupted()方法区别总结-CSDN博客

4.synchronized关键字  ——锁指哪些?

synchronized的作用:可修饰方法、代码块。一个线程正在执行run()方法,其他线程需要等待,直到该方法执行完毕才能继续执行。

/**1.锁为某个对象,非自身 如 obj,此处修饰代码块*/
@Slf4j
public class Runn2 implements Runnable {
    private Object obj = new Object();

    @Override
    public void run() {
        test();
    }
    private int count = 0;
//代码块的锁 this 等同于 public sysnchronized void test(){...}
    public void test() {
        synchronized (obj) {  //任何线程进入都需要拿到obj的对象锁,如果对象已经被锁定,那就只能等待。
            count++;
            System.out.println(Thread.currentThread().getName()+"===>>"+count);
        }
    }
    public static void main(String[] args) throws InterruptedException {
        Runn2 runn2 = new Runn2();
        Thread thread1 = new Thread(runn2);
        Thread thread2 = new Thread(runn2);
        thread2.start();
        thread1.start();
/*
Thread-1===>>1
Thread-0===>>2
*/

    }
}

/**2.锁为自身  this关键字  资源对象本身*/
@Slf4j
public class Runn2 implements Runnable {
    private Object obj = new Object();

    @Override
    public void run() {
        test();
    }
    private int count = 0;

    public void test() {
        synchronized (this) {  //任何线程进入都需要拿到obj的对象锁,如果对象已经被锁定,那就只能等待。
            count++;
            System.out.println(Thread.currentThread().getName()+"===>>"+count);
        }
    }
    public static void main(String[] args) throws InterruptedException {
        Runn2 runn2 = new Runn2();
        Thread thread1 = new Thread(runn2);
        Thread thread2 = new Thread(runn2);
        thread2.start();
        thread1.start();
/*
Thread-0===>>1
Thread-1===>>2
*/

    }
}

/**3.类级别的锁 即类的Class对象锁*/
1中加个static 关键字
public static synchronized void test() {
    // 代码逻辑
}

5.多个线程执行一个加锁,一个未加锁方法效果

@Slf4j
public class MyThread2 {

    public synchronized void m1() {
        System.out.println(Thread.currentThread().getName() + " m1 start");
        try {
            TimeUnit.SECONDS.sleep(10);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(Thread.currentThread().getName() + " m1 end");
    }
    public void m2() {
        System.out.println(Thread.currentThread().getName() + " m2 start");
        try {
            TimeUnit.SECONDS.sleep(5);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(Thread.currentThread().getName() + " m2 end");
    }
    public static void main(String[] args) {
        MyThread2 t = new MyThread2();
        new Thread(t::m1).start();
        new Thread(t::m2).start();
        new Thread(t::m2).start();

    }
}
/*
Thread-1 m2 start
Thread-2 m2 start
Thread-0 m1 start
Thread-2 m2 end
Thread-1 m2 end
Thread-0 m1 end

*/

总结:加锁、未加锁方法两者互不影响

6.针对5做个存款加锁,查看存款的例子

public class MyThread2 {

    /**
     * 名称
     */
    private String acountName;
    /**
     * 存款
     */
    private double money;

    public synchronized void setValue(String acountName, double money) {
        this.acountName = acountName;
        try {
            TimeUnit.SECONDS.sleep(2);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        this.money = money;
    }
//查看存款的方法不加锁
    public /*synchronized*/ double getMoney() {
        return this.money;
    }
    public static void main(String[] args) {
        MyThread2 a = new MyThread2();
        new Thread(() -> a.setValue("张三", 100.0)).start();
        System.out.println(a.getMoney()); // 0.0
        try {
            TimeUnit.SECONDS.sleep(3);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(a.getMoney()); // 100.0
    }
/**
0.0
100.0
*/
}

7.同步的方法调用另一个同步方法,一个线程获得某个对象锁,再次申请仍会得到该对象锁

public class MyThread2 {
 
    synchronized void m1() {
        System.out.println("m1 start ");
        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        m2();

        System.out.println("m1 end");
    }
    synchronized void m2() {

        System.out.println("m2 start ");
        try {
            TimeUnit.SECONDS.sleep(2);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(" m2 end"); // 这句话会打印,调用m2时,不会发生死锁
    }
}

8.线程出现异常会释放锁,没处理完的数据其他线程访问 问题

/**
 * synchronized 代码块中,如果发生异常,锁会被释放.
 * 在并发处理过程中,有异常要多加小心,不然可能发生数据不一致的情况。
 */
public class MyThread2 {
 
    int count = 0;
    
    synchronized void m() {
        System.out.println(Thread.currentThread().getName() + " start");
        while (true) {
            count++;
            System.out.println(Thread.currentThread().getName() + " count=" + count);
            try {
                TimeUnit.SECONDS.sleep(1);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
 
            if (count == 5) {  // 当count == 5 时,synchronized代码块会抛出异常
                int i = 1 / 0; 
            }
        }
    }
 
    public static void main(String[] args) {
        MyThread2  t = new MyThread2 ();
        Runnable r = new Runnable() {
            @Override
            public void run() {
                t.m();
            }
        };
        new Thread(r, "t1").start(); // 执行到第5秒时,抛出 ArithmeticException 
        // 如果抛出异常后,t2 会继续执行,就代表t2拿到了锁,即t1在抛出异常后释放了锁
        
        try {
            TimeUnit.SECONDS.sleep(3);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        new Thread(r, "t2").start();
    }
 /**需要注意不被迷惑的点:t1、t2线程同时执行同一资源r,count为5产生异常,t2开始获取锁执行资源r,此时count值不会清零。同一份资源!同一资源!概念上易出错点
t1 start
t1 count=1
t1 count=2
t1 count=3
t1 count=4
t1 count=5
Exception in thread "t1" java.lang.ArithmeticException: / by zero
	at com.xx.MyThread2.m(MyThread2.java:33)
	at com.xx.MyThread2$1.run(MyThread2.java:43)
	at java.base/java.lang.Thread.run(Thread.java:829)
t2 start
t2 count=6
t2 count=7
t2 count=8
t2 count=9
t2 count=10
*/
}

9.volatile关键字,确保可见性和有序性。但不确定原子性

public class MyThread2 {
    volatile  boolean running = true;   // 对比有无volatile的情况下,整个程序运行结果的区别
    void m() {
        System.out.println(" m start ");
        while (running) { // 直到主线程将running设置为false,T线程才会退出
            // 在while中加入一些语句,可见性问题可能就会消失,这是因为加入语句后,CPU可能就会出现空闲,然后就会同步主内存中的内容到工作内存
            // 所以,可见性问题可能会消失
      /*      try {
                TimeUnit.MILLISECONDS.sleep(10);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }*/
        }
        System.out.println(" m end ");
    }
    public static void main(String[] args) {
        MyThread2 t = new MyThread2();
        new Thread(t::m, "t1").start();
        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        t.running = false;
    }
}
/**
1.当try处代码隐藏,加上volatile,会出现结果
 m start 
 m end 

Process finished with exit code 0
2.当try处代码隐藏,不加volatile,会出现结果
m start    一直显示不再变动
3.当try处代码不隐藏,加不加volatile,结果

 m start 
 m end 

Process finished with exit code 0
*/

上述原因:线程有自己独立空间,存储需要用的变量副本。线程对共享变量操作,会在自己工作内存中完成,再同步给主内存只有使用volatile,将会强制所有线程都去堆内存中读取running/共享变量的值

总结:多线程下访问共享变量,使用volatile关键字可以确保可见性和有序性。

  1. 可见性(Visibility):当一个线程修改了一个被volatile修饰的变量的值时,这个新值会立即被写回主内存,并且其他线程在读取该变量时会立即从主内存中获取最新的值。这样可以避免线程之间使用过期的缓存值,保证了对该变量的修改对其他线程是可见的。
    概述:多线程之间看到的最新的值

  2. 有序性(Ordering):volatile关键字可以防止指令重排优化。在没有volatile修饰的情况下,编译器和处理器可能会对指令进行重排,以提高执行效率。然而,这种重排可能会导致多线程程序出现意外的行为。通过使用volatile关键字,可以防止指令重排,确保指令按照程序顺序执行。

适用:标志位或计数器。符合操作类似递增,volatile无法提供原子性,考虑加锁或原子类。

        3.原子性:原子性指的是一个操作是不可被中断的整体操作,要么完成,要么不执行。在多线程环境下,如果多个线程同时对共享变量进行修改,可能会导致数据竞争和不一致的结果。为了保证原子性,可以使用锁(如synchronized关键字、ReentrantLock等)或原子类(如AtomicInteger、AtomicLong等)来控制对共享变量的访问,从而保证操作的原子性。

synchronized关键字在多线程编程中可以保证【同一时间只有一个线程访问共享资源,从而保证了线程之间的可见性和原子性。】

10.不要以字符串常量作为锁定对象

/**
字符串常量是全局唯一的,因此多个线程如果都使用相同的字符串常量作为锁定对象,就会导致所有这些线程都竞争同一个锁
1.其他代码的干扰:字符串常量作为锁定对象时,在整个应用程序中的任何地方,其他代码都可以使用相同的字符串常量作为锁定对象,这样就可能产生不可预期的同步问题。

2.可能引发死锁:如果多个线程都在不同的位置使用相同的字符串常量作为锁定对象,并且这些线程按照不同的顺序尝试获取这些锁,就有可能引发死锁。
*/

public class MyThread2 {
//建议使用的对象锁:private final Object lock = new Object();
    String s1 = "s1";
    String s2 = "s2";
    
    void m1() {
        synchronized (s1) {
            
        }
    }
 
    void m2() {
        synchronized (s2) {
            
        }
    }
}

11.锁的对象发生变化,引用变为另一个对象。锁就释放掉了

/**
 * 锁定某个对象o,如果o属性发生变化,不影响锁的使用
 * 但是如果o编程另一个对象,则锁定的对象发生变化,
 * 所以锁对象通常要设置为 final类型,保证引用不可以变
 */
public class T {
 
    Object o = new Object();
    
    void m() {
        synchronized (o) {
            while (true) {
                System.out.println(Thread.currentThread().getName());
                try {
                    TimeUnit.SECONDS.sleep(1);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }
 
    public static void main(String[] args) {
        T t = new T();
        new Thread(t::m, "线程1").start();
 
        try {
            TimeUnit.SECONDS.sleep(3);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
 
        Thread thread2 = new Thread(t::m, "线程2");
        t.o = new Object(); // 改变锁引用, 线程2也有机会运行,否则一直都是线程1 运行
        thread2.start();
    }
 
}

Java高并发_timeunit.seconds.sleep(1);-CSDN博客  之前的模糊概念,重新理了一下遍。例子也好啊 推荐!

11.java 中sleep yield join wait的区别

1、sleep:让出CPU调度Thread类的方法,必须带一个时间参数。会让当前线程休眠进入阻塞状态并释放CPU(cpu资源宝贵!只有在线程running的时候,才会获取cpu片段

ep:main方法中Thread.sleep(3000)此时主线程休眠。 run方法中则是子线程休眠

谁执行Thread.sleep(arg)代码 谁休眠。  也可用

TimeUnit.MILLISECONDS.sleep(10)   不释放锁

2、yield:让出CPU调度,执行yield()的线程有可能在进入到可执行状态后马上又被执行,另外yield()方法只能使同优先级或者高优先级的线程得到执行机会,这也和sleep()方法不同。yield()只是使当前线程重新回到可执行状态,所以执行yield()的线程有可能在进入到可执行状态后马上又被执行。调用yield方法只是一个建议,告诉线程调度器我的工作已经做的差不多了,可以让别的相同优先级的线程使用CPU了,没有任何机制保证采纳。 Thread类提供的一个静态方法
ep:thread1.yield() thread1暂让CPU,不释放锁  

3、join:让出CPU调度,join()方法会使当前线程等待调用join()方法的线程结束后才能继续执行   Thread类提供的一个非静态方法  不释放锁

ep:main方法中 thread1.join()        主线程等待thread1先执行完毕

4、wait:让出CPU调度,并且释放掉锁,wait()方法与sleep()方法的不同之处在于,wait()方法会释放对象的“锁标志”。当调用某一对象的wait()方法后,会使当前线程暂停执行,并将当前线程放入对象等待池中,直到调用了notify()方法后,将从对象等待池中移出任意一个线程并放入锁标志等待池中,只有锁标志等待池中的线程可以获取锁标志,它们随时准备争夺锁的拥有权。当调用了某个对象的notifyAll()方法,会将对象等待池中的所有线程都移动到该对象的锁标志等待池。  释放锁

ep:wait用于锁机制,sleep不是,sleep是线程的方法,跟锁没半毛钱关系。wait释放锁的原因,wait,notify,notifyall 都是Object对象的方法,是一起使用的,用于锁机制)

简单概况:Object对象方法随意组合,sleep睡了无其他方法唤醒,不具可操控性。

总结:1.CPU资源宝贵,上述方法会让出使用权。2.除了wait、notify等一组的是Object方法,其余都是Thread方法。3.sleep()方法在任何地方调用,wait()方法在同步方法、同步代码块中调用。 4.除了wait()其他不释放锁[sleep、yield、join]。 5.sleep调用停止后扔仍持有锁,到时间后接着执行。wait()调用放弃锁,等待其他线程notify()唤醒,去竞争同步资源锁。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值