Java并行程序基础。

本文深入讲解Java并发编程的基础知识,包括线程的创建、中断、等待、通知等基本操作,以及synchronized关键字、volatile变量、守护线程和线程优先级的概念与应用。通过实例演示了如何使用不同方式创建线程,以及线程间如何进行协作。

并发,就是用多个执行器(线程)来完成一个任务(大任务)来处理业务(提高效率)的方法。而在这个过程中,会涉及到一些问题,所以学的就是解决这些问题的方法。

线程的基本操作:

  1、创建线程:只需要new一个线程对象即可:Thread myThread=new Thread()。然后 myThread.start() 即可开启线程。那么问题来了,这个线程到底要做些什么呢,所以,Thread有一个run() 方法,方法体则是线程要做的事情。而默认情况下,run()方法什么不做,所以我们需要重写run(),把你要做的事情填进去。

 public static void main(String[] args) {
        Thread t1=new Thread(){
            @Override
            public void run() {
                System.out.println("hello i am t1");
            }
        };
        t1.start();
    }

  上面使用匿名内部类的方式重写了run方法。

  因为Java是单继承的,有时候继承也是一种稀缺的资源,所以可以用Runable接口来实现:

public interface Runnable{
        public  abstract void  run();
    }

  Runnable接口就只有一个run方法。

  Thread类还有一个重要的构造方法:public Thread (Runnable target) ,所以,可以通过实现了Runnable接口的类的实力来创建线程,同时,Runnable是一个函数式接口,所以还可以使用Lambda表达式:

public static void main(String[] args) {
        Thread t1=new Thread(()-> System.out.println("hello i am t1"));
        t1.start();
    }

  2、终止线程:Thread.stop() 可以终止线程,但是它是一个废弃的方法,因为它太过暴力,即使线程正在执行中,也会直接终止,并释放锁。

  3、线程中断:线程中断不会使线程立即退出,而是给线程发送一个通知,告诉目标线程:有人希望你退出啦!至于目标线程如何处理,还是又目标线程自己决定。

        有关线程中断的三个方法:Thread.interrupt() 中断线程。Thread.isInterruputed() 判断是否被中断了。 Thread.interrupted() 判读是否被中断,并且清除中断状态。

 public static void main(String[] args) throws InterruptedException {
        Thread t=new Thread(){
            @Override
            public void run() {
                while (true)
                    System.out.println("t1 is running.");
            }
        };
        t.start();
        Thread.sleep(1000);
        t.interrupt();
    }

    上面代码中,对线程t进行了中断,但并没有作出中断后的操作,所以是没有效果的。改进:

public static void main(String[] args) throws InterruptedException {
        Thread t=new Thread(){
            @Override
            public void run() {
                while (true){
                    if (Thread.currentThread().isInterrupted())
                        break;
                    System.out.println("t1 is running.");
                }
            }
        };
        t.start();
        Thread.sleep(1000);
        t.interrupt();
    }

  4、等待(wait)和通知(notify):为了支持多线程之间的协作,JDK提供了wait()和notify(),这两个方法并不在Thread类中,而是在Object中,也就是说,任何对象都可以调用这两个方法。

    当线程A 调用了obj.wait()方法后,线程A就会停止执行,转为等待状态。等待的状态要等到其他线程调用obj.notify()方法为止。

    当某个线程调用了object.wait()方法后,该线程就会进去object对象的等待队列。这个等待队列中,可能有多个线程。当调用 object.notify()时,会随机在队列中唤醒一个线程。而调用object.notifyAll()时,会唤醒所有线程。

    注意:wait()和notify()都必须在synchronized语句中,必须先获得目标对象的监听器,才能调用这两个方法。调用wait后,会释放监听器。

  5、挂起(suspend)和继续执行(resume):suspend可以停止线程执行,直到resume。但suspend停止时,不会释放资源,所以不推荐使用。

  6、等待线程结束(join)和谦让(yeild):线程之间协作时,有时会有先后关系,也就是B线程等到A线程执行完了,再执行。

volatile:

  当用volatile关键字申明变量时,就等于告诉虚拟机,这个变量极有可能会被某些线程修改,虚拟机就会做一些操作,来确保这个变量背修改后,应用程序范围内的所有线程都能看到这个改动。用volatile申明的变量可以保证有序性和可见性,但无法保证原子性。

守护线程:

  守护线程是一种特殊的线程,它是系统的守护者,在后台完成一些系统性的服务,比如垃圾回收线程。与之对应的是用户线程。当一个Java应用只有守护线程时,Java虚拟机就会自然退出。setDaemon()可以将线程设为守护线程。

public class test {
  public static class DaemonThread extends Thread{
      @Override
      public void run() {
          while (true){
              System.out.println("i am alive");
          }
      }
  }

    public static void main(String[] args) throws InterruptedException {
        Thread thread=new DaemonThread();
        thread.setDaemon(true);
        thread.start();
        Thread.sleep(2000);
    }
}

  将thread设置为守护线程,所以在主线程休眠2s后,用户线程全部结束,整个程序也就结束了。

线程优先级:

  优先级高的线程在竞争资源时,会更有优势。但是这只是概率问题,运气不好的话,高优先级的线程也可能抢占资源失败。setPriority()来设置线程优先级。

public class test {
    final static Object obj=new Object();
    public static class Low extends Thread{
        static int i=0;
        @Override
        public void run() {
            while (true){
                synchronized (obj){
                    i++;
                    if (i>100000){
                        System.out.println("low complete。");
                        break;
                    }
                }
            }
        }
    }
    public static class High extends Thread{
        static int i;
        @Override
        public void run() {
            while (true){
                synchronized (obj){
                    i++;
                    if (i>150000){
                        System.out.println("high complete.");
                        break;
                    }
                }
            }
        }
    }

    public static void main(String[] args) {
        Thread low=new Low();
        Thread high=new High();
        low.setPriority(Thread.MIN_PRIORITY);
        high.setPriority(Thread.MAX_PRIORITY);
        low.start();
        high.start();
    }

}

  上述代码中,即使high要多加50000次,但是因为优先级高,会更先执行完。

Synchronized

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

  用法:可以给指定对象加锁(上面事例中),也可以作用于实例方法:

public class test implements Runnable {
    static test t=new test();
    static int i=0;
        public synchronized void add(){
            i++;
        }

        @Override
        public void run() {
            for (int k=0;k<100000;k++)
                add();
        }

    public static void main(String[] args) throws InterruptedException {
        Thread t1=new Thread(t);
        Thread t2=new Thread(t);
        t1.start();
        t2.start();
        t1.join();t2.join();
        System.out.println(i);
    }
}

 

转载于:https://www.cnblogs.com/zhangyuhao/p/11552159.html

### Java 并行编程与多线程并发的设计模式及最佳实践 #### 1. 并发与并行的区别 在设计高效并发系统时,需明确“并发”与“并行”的差异。前者表示任务在同一时间段内交替执行,后者则依赖于多核处理器支持真正的同时运行[^3]。 #### 2. 创建线程的方式 Java 提供多种创建线程的方法,其中继承 `Thread` 类是最基础的一种。然而,在实际开发中推荐使用实现 `Runnable` 接口或 `Callable` 接口的方式来创建线程,因为这种方式更具灵活性且不会受到单一继承的限制。 #### 3. 原子性和可见性的保障 在多线程环境中,变量更新可能引发数据一致性问题。例如,`inc++` 操作并非原子操作,它由读取、修改和写入三个步骤组成[^4]。因此,仅靠 `volatile` 关键字无法保证其原子性,尽管它可以提供内存可见性。为了确保线程安全,可采用以下几种方式: - **锁机制 (`synchronized`):** 使用同步块或方法锁定共享资源访问权限,防止多个线程同时修改同一对象的状态。 - **显式锁 (`Lock` 接口):** 提供更灵活的功能,如尝试获取锁、定时等待锁释放等。 - **无锁算法:** 利用 CAS(Compare-and-Swap)技术实现高吞吐量下的线程安全性,典型代表为 `AtomicInteger` 和其他 `java.util.concurrent.atomic` 包中的类。 #### 4. 阻塞队列的应用场景 阻塞队列是一组重要的工具类,用于协调生产者消费者之间的关系。常见的阻塞队列有: - `ArrayBlockingQueue`: 基于数组结构的固定大小阻塞队列; - `LinkedBlockingQueue`: 基于链表结构的容量可选阻塞队列; - `SynchronousQueue`: 不存储元素的交接队列,生产和消费必须同步发生; - `PriorityBlockingQueue`: 支持优先级排序的无界阻塞队列; - `DelayQueue`: 只允许延迟到期的任务被取出的特殊队列[^5]。 这些队列广泛应用于线程池管理以及异步任务调度等领域。 #### 5. 线程池的最佳实践 合理配置线程池参数能显著提升应用程序性能。建议遵循如下原则: - 控制核心线程数与最大线程数的比例,避免因过度创建线程而导致上下文切换开销过大; - 设置合理的超时时间以便及时回收闲置资源; - 结合具体业务需求选用合适的拒绝策略(AbortPolicy, CallerRunsPolicy 等)。 以下是基于 `Executors` 工厂方法构建固定大小线程池的一个实例: ```java import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; public class ThreadPoolExample { public static void main(String[] args) { ExecutorService executor = Executors.newFixedThreadPool(10); Runnable task = () -> System.out.println("Executing Task"); for(int i=0;i<10;i++) { executor.submit(task); } executor.shutdown(); } } ``` #### 6. 设计模式在并发环境中的应用 一些经典设计模式经过改造后适用于复杂的并发场景,比如: - **生产者-消费者模式:** 解决了生产速度与消费能力不平衡的问题; - **工作窃取模式:** 让空闲的工作线程从繁忙线程的任务队列中偷取部分任务来分担压力。 以上内容涵盖了 Java 并行程序设计的核心知识点及其优化技巧[^1][^2]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值