Java笔记-多线程中同步加锁相关

Java程序入口就是由JVM启动的main线程:

main线程又可以启动其他线程。当所有线程都运行结束时JVM退出,进程结束。

 

守护线程(Daemon):守护线程是为其他线程服务的线程,所有的非守护线程都执行完毕后,虚拟机才会退出。

守护线程的特点:不能持有资源(如打开文件等)

 

创建守护线程:

setDaemon(true);

下面来演示下,子线程中有死循环,而主线程退出了,子线程还没退出。

程序运行截图如下:

主线程已经退出,而子线程没有退出。

 

源码如下:

import java.text.SimpleDateFormat;
import java.util.Date;

class MyThread1 extends Thread{

    @Override
    public void run() {

        while (true) {

            Date date = new Date(System.currentTimeMillis());
            SimpleDateFormat formatter = new SimpleDateFormat("dd-MM-yyyy HH:mm:ss");
            System.out.println(formatter.format(date));

            try {

                Thread.sleep(1000);
            } catch (InterruptedException e) {

                e.printStackTrace();
            }
        }
    }
}

public class Main1 {

    public static void main(String[] args) throws InterruptedException {

        System.out.println("BEGIN");
        MyThread1 myThread1 = new MyThread1();
        myThread1.start();
        Thread.sleep(1000 * 3);
        System.out.println("OVER");
    }
}

加上守护线程后,即可在main函数退出后,页退出!

程序运行截图如下:

源码如下:

import java.text.SimpleDateFormat;
import java.util.Date;

class MyThread1 extends Thread{

    @Override
    public void run() {

        while (true) {

            Date date = new Date(System.currentTimeMillis());
            SimpleDateFormat formatter = new SimpleDateFormat("dd-MM-yyyy HH:mm:ss");
            System.out.println(formatter.format(date));

            try {

                Thread.sleep(1000);
            } catch (InterruptedException e) {

                e.printStackTrace();
            }
        }
    }
}

public class Main1 {

    public static void main(String[] args) throws InterruptedException {

        System.out.println("BEGIN");
        MyThread1 myThread1 = new MyThread1();
        myThread1.setDaemon(true);
        myThread1.start();
        Thread.sleep(1000 * 3);
        System.out.println("OVER");
    }
}

下面是线程同步相关的设置:

多个线程同时运行,线程调度由操作系统决定,程序本身无法决定。

如下多个线程同时读取共享数据时:

会出现问题,程序运行截图如下:

代码如下:

class AddThread extends Thread{

    @Override
    public void run() {

        for(int i = 0; i < Main2.LOOP; i++){

            Main2.count += 1;
        }
    }
}

class DecThread extends Thread{

    @Override
    public void run() {

        for(int i = 0; i < Main2.LOOP; i++){

            Main2.count -= 1;
        }
    }
}

public class Main2 {

    final static int LOOP = 10000;
    public static int count = 0;

    public static void main(String[] arg) throws InterruptedException {

        AddThread addThread = new AddThread();
        DecThread decThread = new DecThread();
        addThread.start();
        decThread.start();
        addThread.join();
        decThread.join();
        System.out.println(count);
        System.out.println("OVER");
    }
}

对共享数据进行写入时,必须要保证是原子操作

Java使用synchronized对一个对象进行加锁!

synchronized的缺陷:性能会下降;

synchronized的使用:

找出修改共享变量线程代码块;选择一个实例作为锁;使用synchronized(lockObject){...}

运行截图如下:

代码如下:

class AddThread3 extends Thread{

    @Override
    public void run() {

        for(int i = 0; i < Main2.LOOP; i++){

            synchronized (Main3.LOCK) {

                Main2.count += 1;
            }
        }
    }
}

class DecThread3 extends Thread{

    @Override
    public void run() {

        for(int i = 0; i < Main2.LOOP; i++){

            synchronized (Main3.LOCK) {

                Main2.count -= 1;
            }
        }
    }
}

public class Main3 {

    final static int LOOP = 10000;
    public static int count = 0;
    public static final Object LOCK = new Object();

    public static void main(String[] arg) throws InterruptedException {

        AddThread3 addThread = new AddThread3();
        DecThread3 decThread = new DecThread3();
        addThread.start();
        decThread.start();
        addThread.join();
        decThread.join();
        System.out.println(count);
        System.out.println("OVER");
    }
}

使用synchronized:不用担心异常!

public void add(int m){
    
    synchronized (obj){
        
        if(m < 0){
            
            throw new RuntimeException();
        }
        this.value += m;
    }//无论有无异常,都会在此释放
}

JVM规范定义了几种原子操作:

基本类型(long和double除外)赋值:int n = 100;

引用类型的赋值:List<String> list = anotherList;

 

下面是关于synchronized其他的写法

public synchronized void add(int n){
    
    count += n;
    total += n;
}

public void add(int n){

    synchronized(this){
        
        count += n;
        total += n;
    }
}

上面这两个写法是等价的。

 

如果一个类被设计为允许多线程正确访问:这个类就是“线程安全”的(thread-safe)

如java.lang.StringBuffer就是线程安全的。

 

synchronized是可以重复使用的。如下:

public void add(int m){
    
    synchronized (lockA){
        
        this.value += m;
        synchronized (lockB){
            
            this.another += m;
        }//释放lockB
    }//释放lockA
}

这里要注意:不同线程获取多个不同对象的锁可能会导致死锁。

死锁形成的条件(注意了,不管是哪个编程语言都差不多):

两个线程各自持有不同的锁;

两个线程各自试图获取对方已持有的锁;

双方无限等待下去:导致死锁。

死锁发送后:没有任何机制能解除死锁;只能强制结束JVM进程。

如何避免死锁:线程获取锁的顺序要一致!

如下实例!

源码如下:

class SharedObject{

    final Object lockA = new Object();
    final Object lockB = new Object();

    int accountA = 1000;
    int accountB = 2000;

    public void a2b(int balance){

        synchronized (lockA){

            accountA -= balance;
            synchronized (lockB){

                accountB += balance;
            }
        }
    }

    public void b2a(int balance){

        synchronized (lockB){

            accountB -= balance;
            synchronized (lockA){

                accountA += balance;
            }
        }
    }
}

class AThread extends Thread{

    @Override
    public void run() {

        for(int i = 0; i < Main4.LOOP; i++){

            Main4.shared.a2b(1);
            System.out.println("ing...");
        }
    }
}

class BThread extends Thread{

    @Override
    public void run() {

        Main4.shared.b2a(1);
        for(int i = 0; i < Main4.LOOP; i++){

            System.out.println("ing...");
        }
    }
}

public class Main4 {

    final static int LOOP = 100000;
    static SharedObject shared = new SharedObject();

    public static void main(String[] args) throws InterruptedException {

        Thread t1 = new AThread();
        Thread t2 = new AThread();
        t1.start();
        t2.start();
        t1.join();
        t2.join();
        System.out.println("OVER");
    }
}

这个代码是有死锁的但不一定发送:

原因是这样的:

此处只要把锁的先后改成一样的就可以了!

  • 43
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

IT1995

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值