《Java 高并发》05 线程的基本操作

本文介绍了Java并发编程的基础概念,包括volatile关键字的作用与使用、线程组的创建与管理、守护线程的概念及创建方法、线程优先级的设定及其影响,以及synchronized关键字的多种使用方式。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

volatile 与 Java 内存模型

Java 内存模型都是围绕着原子性、有序性和可见性展开的。为了在适当的场合,确保线程间的原子性、有序性和可见性。Java 使用了一些特许的操作或者关键字来申明、告诉虚拟机,在这个地方,要尤其注意,不能随意变动优化目标指令。volatile 关键字就是其中之一。

当用 volatile 去申明一个变量是,就等于告诉虚拟机,这个变量极有可能会被某些程序或者线程修改。为了确保这个变量被修改后,应用程序范围内的所有线程都能“看到”这个改动,虚拟机就必须采用一些特殊的手段,保证这个变量的可见性等特点。

特别注意,volatile 并不能代替锁,它也无法保证一些符合操作的原子性。

使用示例:

public class MultiThreadLong {

    private static Long to = 0L;

    public static class ChangI implements Runnable {

        private Long to;

        public ChangI(Long to) {
            this.to = to;
        }

        @Override
        public void run() {
            while (true) {
                MultiThreadLong.to = this.to;
                Thread.yield();
            }
        }
    }

    public static class ReadI implements Runnable {

        @Override
        public void run() {
            while (true) {
                Long to = MultiThreadLong.to;
                if (to != 111 && to != -999 && to != 333 && to != 444) {
                    System.out.println(to);
                    Thread.yield();
                }

            }
        }
    }

    public static void main(String[] args) {
        new Thread(new ChangI(111L)).start();
        new Thread(new ChangI(-999L)).start();
        new Thread(new ChangI(333L)).start();
        new Thread(new ChangI(444L)).start();
        new Thread(new ReadI()).start();
        new Thread().stop();
    }

}

线程组

在一个系统中,如果线程数量过多,而且功能分配比较明确,就可以将相同功能的线程放置在一个线程组中。

public class ThreadGroupName implements Runnable{

    @Override
    public void run() {
        String groupAndName = Thread.currentThread().getThreadGroup().getName() +"-"+Thread.currentThread().getName();
        while (true){
            System.out.println("I am " + groupAndName);
            try {
                Thread.sleep(3000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    public static void main(String[] args) {
        ThreadGroup groupName1 = new ThreadGroup("groupName1");
        Thread thread1 = new Thread(groupName1,new ThreadGroupName(),"t1");
        Thread thread2 = new Thread(groupName1,new ThreadGroupName(),"t2");
        thread1.start();
        thread2.start();
        ThreadGroup groupName2 = new ThreadGroup("groupName2");
        Thread thread3 = new Thread(groupName2,new ThreadGroupName(),"t3");
        Thread thread4 = new Thread(groupName2,new ThreadGroupName(),"t4");
        thread3.start();
        thread4.start();
    }
}

上述创建了两个线程组 groupName1、groupName2,并创建了四个线程命名为 t1、t2、t3、t4,分别将 t1、t2 放入 groupName1中,t3、t4 放入 groupName2 中。日志打印如下:

I am groupName1-t1
I am groupName2-t4
I am groupName2-t3
I am groupName1-t2

注意:ThreadGroup 提供 stop() 方法,它会停止线程组内的所有线程。但它会出现和 Thread.stop() 相同的问题。

守护线程

守护线程是一种特殊的线程,就和它的名字一样,它是系统的守护者,在后台默默地完成一些系统性的服务,比如垃圾回收线程。与之相对应的是用户线程,用户线程可以认为是系统的工作线程,它会完成这个程序应该要完成的业务操作。 如果用户线程全部结束,这也就意味着这个程序实际上无事可做了。守护线程要收的对象已经不存在了,那么整个应用程序就自然应该结束。因此,当一个 Java 应用内,只有守护线程时,Java 虚拟机就会自然退出。

想要创建一个守护线程只需要将它设置为守护线程即可,具体实现:

public class DaemonThread {

    public static class DaemonT implements Runnable{

        @Override
        public void run() {
            while (true){
                System.out.println(System.currentTimeMillis() + "I am alive.");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    public static void main(String[] args) {
        try {
            DaemonT daemonT = new DaemonT();
            Thread thread = new Thread(daemonT);
            // 设置为守护线程
            thread.setDaemon(true);
            thread.start();

            Thread.sleep(2000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

线程优先级

Java 中的线程有自己的优先级。优先级高的线程在竞争资源师会更有有事,更可能抢占资源,当然,这个是一个概率问题。高优先级线程也有可能抢占失败。

在 Java 中,使用 1 到 10 表示线程优先级,数字越大优先级越高,默认优先级为 5。可以使用内置的三个静态变量来表示优先级:

    public final static int MIN_PRIORITY = 1;
    public final static int NORM_PRIORITY = 5;
    public final static int MAX_PRIORITY = 10;

线程优先级的使用:

public class PriorityDemo {

    public static class HightPriority extends Thread {
        static int count = 0;

        @Override
        public void run() {
            while (true) {
                synchronized (HightPriority.class) {
                    count++;
                    if (count > 10000000){
                        System.out.println("HightPriority is complate");
                        break;
                    }
                }
            }
        }
    }

    public static class LowPriority extends Thread {
        static int count = 0;
        @Override
        public void run() {
            while (true) {
                synchronized (HightPriority.class) {
                    count++;
                    if (count > 10000000){
                        System.out.println("LowPriority is complate");
                        break;
                    }
                }
            }
        }
    }

    public static void main(String[] args) {
        HightPriority hightPriority = new HightPriority();
        LowPriority lowPriority = new LowPriority();
        hightPriority.setPriority(Thread.MAX_PRIORITY);
        lowPriority.setPriority(Thread.MIN_PRIORITY);
        lowPriority.start();
        hightPriority.start();
    }

}

上述代码创建了两个线程,分别是 HightPriority 设置为高优先级、LowPriority 设置为低优先级。让他们完成相同的工作,对 count 累加到 10000000,通过打印结果知道谁先完成工作。在对 count 累加前,使用 synchronized 产生一次资源竞争。目的是是的优先级的差异表现更为明显。

public class PriorityDemo {

    public static class HightPriority extends Thread {
        static int count = 0;

        @Override
        public void run() {
            while (true) {
                synchronized (HightPriority.class) {
                    count++;
                    if (count > 10000000){
                        System.out.println(System.currentTimeMillis() + " HightPriority is complate");
                        break;
                    }
                }
            }
        }
    }

    public static class LowPriority extends Thread {
        static int count = 0;
        @Override
        public void run() {
            while (true) {
                synchronized (HightPriority.class) {
                    count++;
                    if (count > 10000000){
                        System.out.println(System.currentTimeMillis() + " LowPriority is complate");
                        break;
                    }
                }
            }
        }
    }

    public static void main(String[] args) {
        HightPriority hightPriority = new HightPriority();
        LowPriority lowPriority = new LowPriority();
        hightPriority.setPriority(Thread.MAX_PRIORITY);
        lowPriority.setPriority(Thread.MIN_PRIORITY);
        lowPriority.start();
        hightPriority.start();
    }
}

synchronized

synchronized 的作用是实现线程间的同步。它的工作是对同步的代码加锁,是的每一次只能有一个线程进入同步块,从而保证线程间的安全性。

关键字 synchronized 使用方法:

  1. 同步代码块:同步代码块可以指定加锁对象,进入同步代码钱获得给定对象的锁。
  2. 作用于普通方法:相当于给当前实例加锁,进入同步代码前要获得当前实例的锁。
  3. 作用于静态方法:相当于对当前类加锁,进入同步代码钱要获得当前类的锁。

释放方式一:同步代码块

public class AccountingSync implements Runnable {
    static AccountingSync instance = new AccountingSync();
    static int num = 0;

    @Override
    public void run() {
        for (int j = 0; j < 100000000; j++) {
            synchronized (instance) {
                num++;
            }
        }
    }

    public static void main(String[] args) {
        try {
            Thread t1 = new Thread(instance, "thread1");
            Thread t2 = new Thread(instance, "thread2");
            t1.start();
            t2.start();
            t1.join();
            t2.join();
            System.out.println(num);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

}

使用方式二:同步方法

public class AccountingSync2 implements Runnable {
    static AccountingSync2 instance = new AccountingSync2();
    static int num = 0;


    public synchronized void increase() {
        num++;
    }

    @Override
    public void run() {
        for (int j = 0; j < 100000000; j++) {
            increase();
        }
    }

    public static void main(String[] args) {
        try {
            Thread t1 = new Thread(instance, "thread1");
            Thread t2 = new Thread(instance, "thread2");
            t1.start();t2.start();
            t1.join();t2.join();
            System.out.println(num);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

基于上面两种凡是的错误加锁实现:

public class AccountingSync2 implements Runnable {
    static AccountingSync2 instance = new AccountingSync2();
    static int num = 0;


    public synchronized void increase() {
        num++;
    }

    @Override
    public void run() {
        for (int j = 0; j < 100000000; j++) {
            increase();
        }
    }

    public static void main(String[] args) {
        try {
            Thread t1 = new Thread(new AccountingSync2(), "thread1");
            Thread t2 = new Thread(new AccountingSync2(), "thread2");
            t1.start();t2.start();
            t1.join();t2.join();
            System.out.println(num);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

}

上述锁对象是当前调用实例对象,但是在创建两个线程时都指向了不同的 AccountingSync2 锁对象。因此线程 t1 在进入同步方法前加锁 自己的 AccountingSunc2 实例,而线程 t2 也加锁自己 AccountingSync2 实例锁。因此,线程安全是无法保证的。

为解决上述请,synchronized 的第三种使用方式,将其作用在静态方法上。

public class AccountingSync3 implements Runnable {
    static AccountingSync3 instance = new AccountingSync3();
    static int num = 0;

    public synchronized static void increase() {
        num++;
    }

    @Override
    public void run() {
        for (int j = 0; j < 100000000; j++) {
            increase();
        }
    }

    public static void main(String[] args) {
        try {
            Thread t1 = new Thread(new AccountingSync3(), "thread1");
            Thread t2 = new Thread(new AccountingSync3(), "thread2");
            t1.start();t2.start();
            t1.join();t2.join();
            System.out.println(num);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

}

静态同步方法的锁对象是当前类.class,尽管上诉两个线程创建了两个实例对象,但是他们使用的锁是同一个。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值