25/365 java 守护线程 线程同步 synchronized

文章讨论了Java中的守护线程与用户线程的概念,解释了虚拟机对两者的不同处理方式,并通过垃圾回收线程作为守护线程的例子进行说明。接着,文章阐述了线程同步的必要性,描述了锁机制的工作原理以及其带来的上下文切换等开销。然后,文章通过示例展示了如何使用`synchronized`关键字解决线程并发安全问题,包括同步方法和同步块的应用,以防止多个线程同时访问共享资源导致的数据不一致。
摘要由CSDN通过智能技术生成

1.守护线程

线程分为守护线程和用户线程  : daemon thread and non-daemon thread

虚拟机必须等待用户线程执行完毕,但无需等待守护线程执行完毕。

守护线程举例:垃圾回收线程,监控内存线程。

setDaemon(true):默认为false,默认为用户线程

public class D19 {
    public static void main(String[] args) {
        Thread thread = new Thread(new A1());
        thread.setDaemon(true);

        thread.start();

        new Thread(new A2()).start();
    }
}

class A1 implements Runnable{
    @Override
    public void run() {
        while(true){
            System.out.println("This is a daemon thread.");
        }
    }
}

class A2 implements Runnable{
    @Override
    public void run() {
        for (int i = 0; i < 100; i++) {
            System.out.println("This is a non-daemon thread.");
        }
        System.out.println("The non-daemon thread has finished.");
    }
}

A1并没有设置停止条件,但因为将它设置为了守护线程,所以程序可以正常结束。

2.线程同步

并发:多个线程操作一个对象

当多个线程操作同一个对象,并且有线程想要修改该对象时,就会出现线程同步的问题。

线程同步是一种等待机制,多个线程进入该对象的等待池形成队列,等待前一个线程使用完毕,下一个线程再使用。

使用线程和锁来保证线程同步的安全性。

锁机制:当一个线程获得锁,独占资源,其他线程必须等待,直到该线程使用完毕,释放锁。

问题:

  • 等待该资源的对象挂起
  • 加锁,释放锁导致更多的开销:上下文切换,调度延时
  • 优先级倒置

3.运用synchronized关键字解决线程并发造成的不安全问题

1)synchronized关键字修饰方法,默认锁的是该方法的对象

使用前:

import org.omg.CORBA.TIMEOUT;

public class D20 {

    public static void main(String[] args) {
        Ticket ticket = new Ticket();
        new Thread(ticket).start();
        new Thread(ticket).start();
        new Thread(ticket).start();
        new Thread(ticket).start();


    }
}

class Ticket implements Runnable{
    int ticketNum = 20;
    boolean flag = true;
    @Override
    public void run() {
        while(flag){
            try {
                buy();
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }
    }

    public  void buy() throws InterruptedException {
        if(ticketNum<=0){
            flag = false;
            return;
        }

        Thread.sleep(100);

        System.out.println(Thread.currentThread().getName()+" buy ticket: " + ticketNum--);
    }
}

不安全结果:多个对象取到相同的票

 加入synchronized关键字

import org.omg.CORBA.TIMEOUT;

public class D20 {

    public static void main(String[] args) {
        Ticket ticket = new Ticket();
        new Thread(ticket).start();
        new Thread(ticket).start();
        new Thread(ticket).start();
        new Thread(ticket).start();


    }
}

class Ticket implements Runnable{
    int ticketNum = 20;
    boolean flag = true;
    @Override
    public void run() {
        while(flag){
            try {
                buy();
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }
    }

    public synchronized void buy() throws InterruptedException {
        if(ticketNum<=0){
            flag = false;
            return;
        }

        Thread.sleep(100);

        System.out.println(Thread.currentThread().getName()+" buy ticket: " + ticketNum--);
    }
}

结果:不再出现取到同一张票的问题

2)用synchronized同步块

synchronized(Obj){ }

Obj为同步监视器,锁定代码块,同一时刻下只有一个线程访问该代码块

当synchronized修饰方法时,同步监视器默认为该方法的对象,也就是this

使用前:

import java.util.ArrayList;
import java.util.List;

public class D21 {
    public static void main(String[] args) throws InterruptedException {
        List<String> list = new ArrayList<String>();
        for(int i=0;i<10000;i++){
            new Thread(()->{

                    list.add(Thread.currentThread().getName());

            } ).start();
        }
        Thread.sleep(100);
        System.out.println(list.size());
    }
}

不安全结果:多个线程向同一个地址写入数据,导致list的size不足10000

加入synchronized修饰代码块

import java.util.ArrayList;
import java.util.List;

public class D21 {
    public static void main(String[] args) throws InterruptedException {
        List<String> list = new ArrayList<String>();
        for(int i=0;i<10000;i++){
            new Thread(()->{
                    synchronized (list) {
                        list.add(Thread.currentThread().getName());
                    }
            } ).start();
        }
        Thread.sleep(100);
        System.out.println(list.size());
    }
}

 结果:

java中有线程安全的ArrayList: CopyOnWriteArrayList

import java.util.concurrent.CopyOnWriteArrayList;

public class D22 {
    public static void main(String[] args) {
        CopyOnWriteArrayList<String> list = new CopyOnWriteArrayList<>();
        for (int i = 0; i < 10000; i++) {
            new Thread(
                    ()->{
                        list.add(Thread.currentThread().getName());
                    }
            ).start();
        }

        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }

        System.out.println(list.size());
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值