ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

多线程第三章

2021-08-01 19:02:15  阅读:259  来源: 互联网

标签:第三章 synchronized thread Thread 线程 sleep 多线程 public


synchronized和lock锁

1、保证原子性

二者都可以保证在多线程环境下保证原子性

对于synchronized关键字

synchronized(锁对象){
    // 无论有多少行代码
}

对于lock锁:

lock.lock();
// 无论有多少行代码
lock.unlock();

从上面的简单介绍中可以看出来,无论有多少行代码,那么最终的效果就像是在执行一行代码。

2、synchronized特性

对于synchronized来说,线程阻塞在对象监视器的集合中,对于lock锁来说,多线程会集中到CLH队列中去,后面会着重分析这个。

对于synchronized深入理解的话,得深入了解到字节码环节中去了。

下面首先介绍几个概念:

可重入锁:也就说一个线程可以多次持有这个锁。这个最好的说明就是利用lock锁的源码来进行说明。每个线程获取得到的时候,都会有这个锁的使用次数,对于同一个线程来说,使用一个state来进行记录,获取得到几次就加多少个一;也就是说一个计数器来进行累加;

不可中断:多线程环境下,一个线程持有了锁,那么其他的线程只能进入到阻塞状态,等待获取得到锁。获取得到锁的线程不释放,那么没有获取得到锁的只能够等着。

可中断状态:由于线程发生了中断,导致了获取锁的线程和没有获取得到锁的线程只能够进行中断,而无法获取得到锁或者是获取得到锁的线程只能退出本次任务的执行

使用synchronized具备了可重入锁和不可中断的特性:

可重入性:两个锁对象都是同一个,多个线程来进行操作的时候,只要有一个线程操作的时候,那么其他的线程不会获取得到;

得到了锁之后,再一次获取得到锁,然后执行;执行完成之后,其他的线程再次操作得到锁对象;

@Slf4j
public class SynchronizedTest {
    public static void main(String[] args) {
        // 当前线程是所有线程的起始点
        MyThread thread = new MyThread();
        // 创建100个线程来执行操作
        for (int i = 0; i < 100; i++) {
            new Thread(thread).start();
        }
        try {
            Thread.sleep(10000);
            log.info("num变量最终的值是:{}",thread.num);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

@Slf4j
class MyThread implements Runnable{
    int num = 100;
    @Override
    public void run() {
        synchronized (MyThread.class){
            try {
                log.info("I can run!!!!!当前线程是:{}",Thread.currentThread().getName());
                hello();
                Thread.sleep(50);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    public void hello(){
        synchronized (MyThread.class){
            log.info("I can run!!!!!当前线程是:{}",Thread.currentThread().getName());
            num--;
        }
    }
}
17:05:22.812 [Thread-2] INFO com.guang.thread.kechongru.MyThread - I can run!!!!!当前线程是:Thread-2
17:05:22.813 [Thread-2] INFO com.guang.thread.kechongru.MyThread - I can run!!!!!当前线程是:Thread-2
17:05:22.875 [Thread-1] INFO com.guang.thread.kechongru.MyThread - I can run!!!!!当前线程是:Thread-1
17:05:22.875 [Thread-1] INFO com.guang.thread.kechongru.MyThread - I can run!!!!!当前线程是:Thread-1
17:05:26.666 [main] INFO com.guang.thread.kechongru.SynchronizedTest - num变量最终的值是:0

从控制台输出可以看出,代码每两个都是一样的。

不可中断性:

@Slf4j
public class NoInterruptSyncronizedTest {
    public static void main(String[] args) {

        Thread t = new Thread(() -> {
            synchronized ("lock") {
                while (true){
                    log.info("hello,world");
                }
            }
        });
        try {
            t.start();
            Thread.sleep(100);
            // 打上中断标记
            t.interrupt();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

观察控制台,即使设置了中断标记,但是在synchronized还是一直在进行运行。不抛出来异常!

但是将代码修改一下:

@Slf4j
public class NoInterruptSyncronizedTest {
    public static void main(String[] args) {

        Thread t = new Thread(() -> {
            synchronized ("lock") {
                while (true){
                    log.info("hello,world");
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    break;
                }
            }
        });
        try {
            t.start();
            Thread.sleep(100);
            // 打上中断标记
            t.interrupt();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

可以看到控制台报错了,但是线程依然运行!显示如下:

17:17:06.756 [Thread-0] INFO com.guang.thread.kechongru.NoInterruptSyncronizedTest - hello,world
java.lang.InterruptedException: sleep interrupted
	at java.lang.Thread.sleep(Native Method)
	at com.guang.thread.kechongru.NoInterruptSyncronizedTest.lambda$main$0(NoInterruptSyncronizedTest.java:16)
	at java.lang.Thread.run(Thread.java:748)
17:17:06.863 [Thread-0] INFO com.guang.thread.kechongru.NoInterruptSyncronizedTest - hello,world
17:17:07.871 [Thread-0] INFO com.guang.thread.kechongru.NoInterruptSyncronizedTest - hello,world
17:17:08.875 [Thread-0] INFO com.guang.thread.kechongru.NoInterruptSyncronizedTest - hello,world

报出来异常在Thread.sleep这个地方,那么跟进去看看:

    /**
     * Causes the currently executing thread to sleep (temporarily cease
     * execution) for the specified number of milliseconds, subject to
     * the precision and accuracy of system timers and schedulers. The thread
     * does not lose ownership of any monitors.
     * 导致当前执行的线程睡眠指定的时间,取决于系统计时器和系统调度器的精确性。当前线程并不会失去对象监视器
     * 通过这段注释可以看到,sleep在睡眠的时候,也会持有锁,不是释放锁!
     * @param  millis
     *         the length of time to sleep in milliseconds
     *
     * @throws  IllegalArgumentException
     *          if the value of {@code millis} is negative
     *
     * @throws  InterruptedException:抛出来的异常介绍,当这个异常抛出来的时候,当前的线程的中断会被擦除掉!中断没用!
     *          if any thread has interrupted the current thread. The
     *          <i>interrupted status</i> of the current thread is
     *          cleared when this exception is thrown.
     */
    public static native void sleep(long millis) throws InterruptedException;

即使是给其设置了中断标识,但是被擦掉了,会一直执行。也就是说,synchronized中的线程获取得到了锁之后,不管锁的状态是否中断,都将会执行。

Lock锁之后专门开一个专题介绍!

3、synchronized使用

下面接着介绍synchronized的使用,可以用在方法上,也可以用在同步代码块上:

用在方法上和用在同步代码块上

@Slf4j
public class SynchronizedMethodTest {
    public synchronized void test1(){
        log.info("当前的锁对象是:{}",Thread.currentThread().getName());
        try {
            Thread.sleep(10000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
    public  void test2(){
        synchronized (this){
            log.info("当前的锁对象是:{}",Thread.currentThread().getName());
            try {
                Thread.sleep(10000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    public synchronized static void test3(){
        log.info("当前的锁对象是:{}",Thread.currentThread().getName());
        try {
            Thread.sleep(100);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }


    public  static void test4(){
        synchronized (SynchronizedMethodTest.class){
            log.info("当前的锁对象是:{}",Thread.currentThread().getName());
            try {
                Thread.sleep(50);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

    }

    public static void main(String[] args) {
        SynchronizedMethodTest t = new SynchronizedMethodTest();
        new Thread(()->{
            t.test1();
        }).start();
        new Thread(()->{
            t.test2();
        }).start();
        new Thread(()->{
            t.test3();
        }).start();
        new Thread(()->{
            t.test4();
        }).start();
        try {
            Thread.sleep(5000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

test3和test4都是使用了类的字节码对象作为锁,test1和test2都是使用了当前对象作为了锁对象。

但是synchronized的使用不止如此,还有其他用法。比如线程类中提供的方法。

标签:第三章,synchronized,thread,Thread,线程,sleep,多线程,public
来源: https://www.cnblogs.com/likeguang/p/15087366.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有