前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >多线程二 synchronized的使用

多线程二 synchronized的使用

作者头像
用针戳左手中指指头
发布2021-01-29 11:06:17
2600
发布2021-01-29 11:06:17
举报
文章被收录于专栏:学习计划

1.synchronized 锁有两种:

1.类的实例

2.类对象

第一种类对象

代码语言:javascript
复制
    public static void test(){
        System.out.println(Thread.currentThread().getName()+" start ");
        try {
            TimeUnit.SECONDS.sleep(2);
        } catch (Exception e) {
            e.printStackTrace();
        }
        System.out.println(Thread.currentThread().getName()+" end ");
    }

    public void test2(){
        synchronized (getClass()) {
            try {
                TimeUnit.SECONDS.sleep(2);
            } catch (Exception e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName()+" run ");
        }
    }

    public static void main(String[] args) {
        TestThread10 t = new TestThread10();
        new Thread(()->TestThread10.test(),"线程 1 ").start();
        new Thread(()->t.test2(),"线程 2 ").start();
    }

其结果如下:

在线程1,也就是先启动线程1且等线程1走完,才执行线程2

第二种类的实例

那就是两个不同的锁,不会干扰

代码语言:javascript
复制
public void test2(){
    synchronized (this) {
        try {
            TimeUnit.SECONDS.sleep(2);
        } catch (Exception e) {
            e.printStackTrace();
        }
        System.out.println(Thread.currentThread().getName()+" run ");
    }
}

2.使用synchronized的时候,出现异常一定要处理,不然他会自动释放锁

它的机制是手动加锁,自动释放锁。下面看一个例子,在异常的地方一定要处理异常,不然就会想下面代码中的线程1,会被释放掉。

代码语言:javascript
复制
private Integer c = 0;

@Override
public void run() {
  count();
}

private synchronized void count(){
    System.out.println(Thread.currentThread().getName()+" start。。。");
    while (true) {
        System.out.println(Thread.currentThread().getName()+" count="+c++);
        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        if (c == 5) {
            int i = 1/0;
        }
    }

}

public static void main(String[] args) {
    TestThread3 t = new TestThread3();
    Thread t1 = new Thread(t, "线程 1");
    Thread t2 = new Thread(t, "线程 2");
    t1.start();
    t2.start();
}

3.主线程和子线程

线程分用户线程和守护线程,main方法其实是一个主线程,在操作系统启动java.exe后,是开启了一个进程,然后进程启动main线程,main线程有启动其他线程。

但是子线程不受主线程影响,当主线程结束后,其子线程任然在运行,就像上面代码所执行的结果一样,主线程启动完子线程后就结束了,但启动的两个子线程都是非守护线程,即不受主线程影响;所以当子线程1异常结束,线程2任然继续

设置守护线程,记住,设置守护线程要在start方法之前设置

代码语言:javascript
复制
t2.setDaemon(true);
t2.start();

这里有两条原则:

  • 主线程结束,用户线程继续执行,
  • 所有子线程都是守护线程,那么主线程结束,所有子线程都会结束,如果存在用户线程,那么在用户线程结束后结束

4.volatile的作用

  • 防止计算机指令的重排序
  • 保证线程间变量的可见性

它不保证原子性,是针对java而实现的功能

看下面代码,对同一个对象的变量进行自增,结果是100000,貌似很正常

代码语言:javascript
复制
public class TestThread4{
    private int c = 0;

    private void count(){
        for (int i = 0; i < 100000; i++) {
            System.out.println("count = "+ ++c);;
        }
    }

    public static void main(String[] args) {
        TestThread4 t = new TestThread4();
        List<Thread> list = new ArrayList<>();
        for (int i = 0; i < 10; i++) {
            list.add(new Thread(t::count,"thread-"+i));
        }

        list.forEach(a->a.start());
        list.forEach(a-> {
            try {
                a.join();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        });
        System.out.println("main end");
    }
}

我们在c上增加volatile修饰:

代码语言:javascript
复制
private volatile int c = 0;

比如在第一个线程在拿到c后进行自增,同时另一个线程也去拿了c,都同时自增,然后都写入同样的值,导致的这样的结果。

5.notify是随机启动等待线程中的一个,并且跟线程优先级无关

notify是随机启动等待线程中的一个,并且跟线程优先级无关,且 wait和notify方法要在同一把lock的情况下使用;还有一点是lock.wait 阻塞还后会把锁让出给需要的线程,然而,在其他线程执行完后,调用lock.notify(),唤醒等待的线程,但是在当前锁里的代码没执行完,不会释放掉锁。

简单场景模拟:

一个固定容量同步容器,拥有put和get方法,以及getCount方法,能够支持两个生产者线程以及10个消费者线程的阻塞调用。

代码语言:javascript
复制
public class TestThread8 {

    private final LinkedList list = new LinkedList();

    private final int MAX = 10;

    private int count = 0;

    public synchronized void put(Object o) {
        while (list.size() == MAX) {
            try {
                // 在这里等待;的那个调用notify时会从这里继续执行
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        list.add(o);
        count++;
        // 启动所有线程,包括生产者,随机的
        this.notifyAll();
    }

    public synchronized void get() {
        while (list.size() == 0) {
            try {
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        list.removeLast();
        count--;
        this.notifyAll();
    }

    public int getCount() {
        return list.size();
    }

    public static void main(String[] args) {
        TestThread8 t = new TestThread8();

        for (int i = 0; i < 2; i++) {
            new Thread(() -> {
                int j = 0;
                while (true) {
                    t.put(Thread.currentThread().getName() + " put " + t.getCount());
                    System.out.println(Thread.currentThread().getName() + " put " + t.getCount());
                }
            }).start();
        }

        for (int i = 0; i < 10; i++) {
            new Thread(() -> {
                while (true) {
                    t.get();
                    System.out.println(Thread.currentThread().getName() + " get " + t.getCount());
                }
            }).start();
        }
    }
}
本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2020/04/06 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 1.synchronized 锁有两种:
  • 2.使用synchronized的时候,出现异常一定要处理,不然他会自动释放锁
  • 3.主线程和子线程
  • 4.volatile的作用
  • 5.notify是随机启动等待线程中的一个,并且跟线程优先级无关
相关产品与服务
容器服务
腾讯云容器服务(Tencent Kubernetes Engine, TKE)基于原生 kubernetes 提供以容器为核心的、高度可扩展的高性能容器管理服务,覆盖 Serverless、边缘计算、分布式云等多种业务部署场景,业内首创单个集群兼容多种计算节点的容器资源管理模式。同时产品作为云原生 Finops 领先布道者,主导开源项目Crane,全面助力客户实现资源优化、成本控制。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档