本文共 5526 字,大约阅读时间需要 18 分钟。
并发是Java学习的重点内容,没有学好多线程不能算是会Java
首先来了解一些进程与线程的概念:
进程是具有一定独立功能的程序关于某个数据集合上的一次运行活动,进程是系统进行资源分配和调度的一个独立单位。讲人话:启动一个QQ.exe就是启动一个进程。
线程是进程的一个实体,是CPU调度和分派的基本单位,它是比进程更小的能独立运行的基本单位.线程自己基本上不拥有系统资源,只拥有一点在运行中必不可少的资源(如程序计数器,一组寄存器和栈),但是它可与同属一个进程的其他的线程共享进程所拥有的全部资源。讲人话:在QQ里面发一条消息就是启动了一个线程。
一个进程里面可以有多个线程。
一个线程可以创建和撤销另一个线程;同一个进程中的多个线程之间可以并发执行。线程的几种状态之间的相互转换:
//测试类public class test { public static void main(String[] args) { ThreadTest t1 = new ThreadTest("t1"); t1.start(); ThreadTest t2 = new ThreadTest("t2"); t2.start(); }}//创建一个线程类class ThreadTest extends Thread{ private String name; public ThreadTest(String name){ this.name = name; } //重写Thread的run方法 @Override public void run() { System.out.println(name+"启动了一个线程"); }}
这是常见的用法
//测试类public class test { public static void main(String[] args) { Runnabletest r1 = new Runnabletest(); Runnabletest r2 = new Runnabletest(); Thread t1 = new Thread(r1); Thread t2 = new Thread(r2); t1.setName("Thread-t1"); t2.setName("Thread-t2"); t1.start(); t2.start(); }}//用实现Runnable的方式创建一个线程类class Runnabletest implements Runnable { //必须重写run方法 @Override public void run() { int sum = 0; for (int i = 10; i < 20; i++) { sum += i; } System.out.println("线程" + Thread.currentThread().getName() + " 输出:" + sum); }}
这种用法多用与线程池相关的操作
//测试类public class test { public static void main(String[] args) { Callablec1 = new CallableTest<>(); Callable c2 = new CallableTest<>(); FutureTask task1 = new FutureTask (c1); FutureTask task2 = new FutureTask (c2); Thread t1 = new Thread(task1); Thread t2 = new Thread(task2); t1.setName("Thread-t1"); t2.setName("Thread-t2"); t1.start(); t2.start(); Integer i1 = task1.get(); Integer i2 = task2.get(); System.out.println(t1.getName()+"得到的值为:"+i1); System.out.println(t2.getName()+"得到的值为:"+i2); }}//用实现Callable接口的方式创建一个线程类class CallableTest implements Callable { //必须重写call方法 @Override public Integer call() throws Exception { int sum = 0; for (int i = 30; i < 40; i++) { sum += i; } System.out.println(Thread.currentThread().getName()+"运行了"); return sum; }}
方法 | 解读 |
---|---|
public String getName() | 返回此线程的名称 |
public int getPriority() | 返回此线程的优先级 |
public void interrupt() | 中断这个线程。 |
public static boolean interrupted() | 测试当前线程是否中断。 |
public boolean isInterrupted() | 测试这个线程是否被中断。 |
public void join() | 等待这个线程死亡。 |
public void join(long millis) | 等待这个线程死亡最多 millis毫秒。 |
public void run() | 如果这个线程使用单独的Runnable运行对象构造,则调用该Runnable对象的run方法; 否则,此方法不执行任何操作并返回。 |
public void setDaemon(boolean on) | 将此线程标记为 守护线程或用户线程。 |
public void setName(String name) | 将此线程的名称更改为等于参数 name 。 |
public void setPriority(int newPriority) | 更改此线程的优先级。 |
public static void sleep(long millis) | 使当前正在执行的线程以指定的毫秒数暂停(暂时停止执行),具体取决于系统定时器和调度程序的精度和准确性。 |
public static void sleep(long millis, int nanos) | 导致正在执行的线程以指定的毫秒数加上指定的纳秒数来暂停(临时停止执行),这取决于系统定时器和调度器的精度和准确性。 |
public void start() | 导致此线程开始执行; Java虚拟机调用此线程的run方法。 |
public static void yield() | 对调度程序的一个暗示,即当前线程愿意产生当前使用的处理器。 |
*同步:使用多个线程访问同一资源
当我们使用多个线程访问同一资源的时候,且多个线程中对资源有写的操作,就容易出现线程安全问题。要解决多线程并发访问一个资源的安全性问题,Java中提供了同步机制(synchronized)来解决。格式:
synchronized(同步锁){ 需要同步操作的代码}
格式:
public synchronized void method(){ 可能会产生线程安全问题的代码}
与synchronized类似的,lock也能够达到同步的效果
用法:
public static void main(String[] args) { Lock lock = new ReentrantLock(); Condition condition = lock.newCondition(); Thread t1 = new Thread() { public void run() { try { //线程启动,试图占有对象 lock.lock(); Thread.sleep(5000); //临时释放对象,并等待 condition.await(); } catch (InterruptedException e) { e.printStackTrace(); } finally { //释放锁 lock.unlock(); } } }; t1.setName("t1"); t1.start();
每一个线程的启动和结束都是比较消耗时间和占用资源的。
但如果准备一个任务容器,一次性启动10个 消费者线程; 刚开始任务容器是空的,所以线程都wait在上面;直到一个外部线程往这个任务容器中扔了一个“任务”,就会有一个消费者线程被唤醒notify; 这个消费者线程取出“任务”,并且执行这个任务,执行完毕后,继续等待下一次任务的到来; 如果短时间内,有较多的任务加入,那么就会有多个线程被唤醒,去执行这些任务。
在整个过程中,都不需要创建新的线程,而是循环使用这些已经存在的线程。
线程池类ThreadPoolExecutor在包java.util.concurrent下
ThreadPoolExecutor threadPool= new ThreadPoolExecutor(10, 15, 60, TimeUnit.SECONDS, new LinkedBlockingQueue());
execute方法用于添加新的任务
4天
https://blog.csdn.net/qq_44876636/article/details/115382363