我是靠谱客的博主 感动故事,最近开发中收集的这篇文章主要介绍1.Java多线程,觉得挺不错的,现在分享给大家,希望可以做个参考。

概述

  1. 进程
    一个进程包括由操作系统分配的内存空间,包含一个或多个线程。一个线程不能独立的存在,它必须是进程的一部分。一个进程一直运行,直到所有的非守护线程都结束运行后才能结束。
  2. 多线程
    用多线程只有一个目的,那就是更好的利用cpu的资源,多线程指指的是这个程序(一个进程)运行时产生了不止一个线程(线程是cpu调度的最小单位)。
    在java中,每次程序运行至少启动2个线程。一个是main线程,一个是垃圾收集线程。因为每当使用java命令执行一个类的时候,实际上都会启动一个JVM,每一个jVM实习在就是在操作系统中启动了一个进程。
  3. 并行与并发
    并行:多个cpu实例或者多台机器同时执行一段处理逻辑,是真正的同时。
    并发:通过cpu调度算法,让用户看上去同时执行,实际上从cpu操作层面不是真正的同时。并发往往在场景中有公用的资源,那么针对这个公用的资源往往产生瓶颈,我们会用TPS或者QPS来反应这个系统的处理能力。

QPS:Queries Per Second意思是“每秒查询率”,是一台服务器每秒能够相应的查询次数,是对一个特定的查询服务器在规定时间内所处理流量多少的衡量标准。
TPS:是TransactionsPerSecond的缩写,也就是事务数/秒(每秒处理事务数)。它是软件测试结果的测量单位。一个事务是指一个客户机向服务器发送请求然后服务器做出反应的过程。客户机在发送请求时开始计时,收到服务器响应后结束计时,以此来计算使用的时间完成的事务个数。

  1. 一个线程生命周期
    在这里插入图片描述
  • 新建状态(New):
    使用 new 关键字和 Thread 类或其子类建立一个线程对象后,该线程对象就处于新建状态。它保持这个状态直到程 序start() 这个线程。
  • 就绪状态(Runnable):
    当线程对象调用了start()方法之后,该线程就进入就绪状态。就绪状态的线程处于就绪队列中,要等待JVM里线程调度器的调度。
  • 运行状态(Running):
    如果就绪状态的线程获取 CPU 资源,就可以执行 run(),此时线程便处于运行状态。处于运行状态的线程最为复杂,它可以变为阻塞状态、就绪状态和死亡状态。
  • 阻塞状态(Blocked):
    如果一个线程执行了sleep(睡眠)、suspend(挂起)等方法,失去所占用资源之后,该线程就从运行状态进入阻塞状态。在睡眠时间已到或获得设备资源后可以重新进入就绪状态。
    可以分为三种:
    1).等待阻塞:运行状态中的线程执行 wait() 方法,使线程进入到等待阻塞状态。
    2).同步阻塞:线程在获取 synchronized 同步锁失败(因为同步锁被其他线程占用)。
    3).其他阻塞:通过调用线程的 sleep() 或 join() 发出了 I/O请求时,。当sleep() 状态超时,join()等待线程终止或超时,或者I/O处理完毕,线程重新转入就绪状态。
  • 死亡状态(Dead):
    一个运行状态的线程完成任务或者其他终止条件发生时,该线程就切换到终止状态。
  1. Runnable与Thread对比优势
    1).适合多个相同的程序代码的线程去处理同一个资源。
    2).可以避免java中的单继承的限制。
    3).增加程序的健壮性,代码可以被多个线程共享,代码和数据独立。
  2. 同步(Synchronized)和锁(Lock)机制
    当多个执行线程共享一个资源时,多个线程读或者写相同的数据等情况时可能会导致数据不一致。 为了解决这些问题,引入了临界区概念。临界区是一个用以访问共享资源的代码块,这个代码块在同一时间内只允许一个线程执行。
    Java提供了同步机制。当一个线程试图访问一个临界区时,它将使用一种同步机制来查看是不是已有其他线程进入临界区。如果没有其他线程进入临界区,它就可以进入临界区;如果已有线程进入了临界区,它就被同步机制挂起,直到进入的线程离开这个临界区。如果在等待进入临界区的线程不止一个,JVM会随机选择其中的一个,其余的将继续等待。

当多个线程共享一个资源的时候需要进行同步,但是过多的同步可能导致死锁。

  • 使用synchronized实现同步方法

方法声明:

public synchronized void addAmount(double amount) {
   .........
}

代码块中使用:

synchronized(obj){
   .........
}
  • 使用锁实现同步
    Java提供了同步代码块的另一种机制,它比synchronized关键字更强大也更加灵活。这种机制基于Lock接口及其实现类(例如:ReentrantLock)
    它比synchronized关键字好的地方:
    1).提供了更多的功能。tryLock()方法的实现,这个方法试图获取锁,如果锁已经被其他线程占用,它将返回false并继续往下执行代码。
    2).Lock接口允许分离读和写操作,允许多个线程读和只有一个写线程。ReentrantReadWriteLock
    3).具有更好的性能

锁示例:

public class PrintQueue {
    /**声明一把锁,其中ReentrantLock(可重入的互斥锁)是Lock接口的一个实现*/
    private final Lock queueLock=new ReentrantLock();

    public void printJob(Object document){
        /**调用lock()方法声明同步代码块(临界区)*/
        queueLock.lock();
        try {
            Long duration=(long)(Math.random()*10000);
            ......
            Thread.sleep(duration);
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            /**释放锁*/
            queueLock.unlock();
        }
    }
}
  • 读写锁(ReentrantReadWriteLock)
    使用读操作锁时可以允许多个线程同时访问,使用写操作锁时只允许一个线程进行。在一个线程执行写操作时,其他线程不能够执行读操作(类似数据库锁机制,实现读写分离)。
class CachedData {
    Object data;
    volatile boolean cacheValid;    //缓存是否有效
    ReentrantReadWriteLock rwl = new ReentrantReadWriteLock();

    void processCachedData() {
        rwl.readLock().lock();    //获取读锁
        //如果缓存无效,更新cache;否则直接使用data
        if (!cacheValid) {
            // Must release read lock before acquiring write lock
            //获取写锁前须释放读锁
            rwl.readLock().unlock();
            rwl.writeLock().lock();    
            // Recheck state because another thread might have acquired
            //   write lock and changed state before we did.
            if (!cacheValid) {
                data = ...
                cacheValid = true;
            }
            // Downgrade by acquiring read lock before releasing write lock
            //锁降级,在释放写锁前获取读锁
            rwl.readLock().lock();
            rwl.writeLock().unlock(); // Unlock write, still hold read
        }

        use(data);
        rwl.readLock().unlock();    //释放读锁
    }
}
  1. 多线程示例
    同步示例:
public class MyThread implements Runnable {
	private int ticket = 100;

	@Override
	public void run() {
		for (int i = 0; i < 20; i++) {
			synchronized (this) {
				if (this.ticket > 0) {
					System.out.println(Thread.currentThread().getName() + "正在买票:" + this.ticket--);
				}
			}
		}
	}

	public static void main(String[] args) {
		MyThread mt = new MyThread();
		new Thread(mt, "A窗口").start();
		new Thread(mt, "B窗口").start();
		new Thread(mt, "C窗口").start();
	}
}

锁示例:

public class MyThread implements Runnable {
	private int ticket = 100;
	// 定义锁
	private Lock lock = new ReentrantLock();

	@Override
	public void run() {
		for (int i = 0; i < 20; i++) {
			lock.lock();
			if (this.ticket > 0) {
				System.out.println(Thread.currentThread().getName() + "正在买票:" + this.ticket--);
			}
			lock.unlock();
		}
	}

	public static void main(String[] args) {
		MyThread mt = new MyThread();
		new Thread(mt, "A窗口").start();
		new Thread(mt, "B窗口").start();
		new Thread(mt, "C窗口").start();
	}
}

参考资料
java并发之线程同步(synchronized和锁机制)

最后

以上就是感动故事为你收集整理的1.Java多线程的全部内容,希望文章能够帮你解决1.Java多线程所遇到的程序开发问题。

如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(47)

评论列表共有 0 条评论

立即
投稿
返回
顶部