程序:
是为了完成特定的任务,用某种语言编写的一组指令的集合。即一段静态的代码。静态对象(静态的代码)
进程:
是程序的一次执行过程,或是正在运行的一个程序,是一个动态的过程,有它自身的产生,存在,消亡的过程 --> 生命周期(动态执行的程序)
线程:
进程可进一步细化为线程,是一个程序内部的一条执行路径。(每一件事执行的路径)
一个程序可以有多个线程
并行:
多个CPU同时执行多个任务(多个人同时做不同的事)
并发:
一个CPU采用时间片 同时执行多个任务(多个人做同一件事)
线程的相关方法:
Thread.currentThread().getName() :获取当前线程的名字.
创建线程池
JAVA中创建线程池主要有两类方法:
第一类是通过Executors工厂类提供的方法,该类提供了4种不同的线程池可供使用。
第二类是通过ThreadPoolExecutor类进行自定义创建。
通过Executors类提供的四种线程池方法
newCachedThreadPool创建一个可缓存线程池,
newFixedThreadPool创建一个定长线程池,可控制线程最大并发数,超出的线程会在队列中等待。
newScheduledThreadPool创建一个大小无限的线程池。此线程池支持定时以及周期性执行任务的需求。
newSingleThreadExecutor创建一个单线程化的线程池,它只会用唯一的工作线程来执行任务, 保证所有任务按照指定顺序(FIFO, LIFO, 优先级)执行
**第二类方式:创建和使用线程池 ThreadPoolExecutor
**线程池类为:**java.util.concurrent.ThreadPoolExecutor
构造函数:
ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime,TimeUnit unit, BlockingQueue workQueue, RejectedExecutionHandler handler);
参数说明:
corePoolSize: 线程池维护线程的最少数量
maximumPoolSize:线程池维护线程的最大数量
keepAliveTime: 线程池维护线程所允许的空闲时间
unit: 线程池维护线程所允许的空闲时间的单位
workQueue: 线程池所使用的缓冲队列
handler: 线程池对拒绝任务的处理策略
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-m35iRvOG-1689562687507)(E:\预习\图片\QQ截图20210916113358.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-vVee2gdt-1689562687507)(E:\预习\图片\QQ截图20210916113503.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-qZBC4kxi-1689562687508)(E:\预习\图片\QQ截图20210916121900.png)]
创建线程的方法:
方式一 继承Thread
/**
* 方式一:继承Thread,重写run()方法,调用start 开启线程
* 线程开启不一定立即执行,由cpu调度执行
*/
public class testThread1 extends Thread{
//run方法线程体
@Override
public void run() {
for (int i = 0; i < 20; i++) {
System.out.println("我在看代码"+i);
}
}
//主线程
public static void main(String[] args) {
//创建一个线程对象
testThread1 t1 = new testThread1();
//调用start开启线程
t1.start();
for (int i = 0; i < 20; i++) {
System.out.println("我在学习多线程"+i);
}
}
}
方式二 实现Runnable接口
/**
* 方式二:实现Runnable接口,重写run方法,执行线程需要放入runnable接口实现类,调用start方法
*/
public class testThreadRunnable1 implements Runnable {
//run方法线程体
@Override
public void run() {
for (int i = 0; i < 20; i++) {
System.out.println("我在看代码"+i);
}
}
//主线程
public static void main(String[] args) {
//创建runnable接口的实现类对象
testThreadRunnable1 threadRunnable1 = new testThreadRunnable1();
//创建线程对象,通过线程对象来开启线程,代理
new Thread(threadRunnable1).start();
for (int i = 0; i < 20; i++) {
System.out.println("我在学习多线程"+i);
}
}
}
方式三 实现Callable接口
/**
* 方式三:实现Callable接口
*
* 1.可以定义返回值
* 2.可以抛出异常
*/
public class testThreadCallable implements Callable<Boolean> {
private String url; // 图片地址
private String name; //图片名称
public testThreadCallable(String url, String name) {
this.url = url;
this.name = name;
}
@Override
public Boolean call() {
WebDownloader2 webDownloader = new WebDownloader2();
webDownloader.downloader(url,name);
System.out.println("文件下载为:"+name);
return true;
}
public static void main(String[] args) throws ExecutionException, InterruptedException {
testThreadCallable t1 = new testThreadCallable("https://img-blog.csdnimg.cn/20210715084821333.png?x-oss-process=image/resize,m_fixed,h_200","1.jpg");
testThreadCallable t2 = new testThreadCallable("https://img-blog.csdnimg.cn/2021071814595362.png?x-oss-process=image/resize,m_fixed,h_200","2.jpg");
testThreadCallable t3 = new testThreadCallable("https://img-blog.csdnimg.cn/20210715090445786.png?x-oss-process=image/resize,m_fixed,h_200","3.jpg");
//创建执行服务
ExecutorService scr = Executors.newFixedThreadPool(3);
//提交执行
Future<Boolean> r1 = scr.submit(t1);
Future<Boolean> r2 = scr.submit(t2);
Future<Boolean> r3 = scr.submit(t3);
//获取结果
boolean rs1 = r1.get();
boolean rs2 = r2.get();
boolean rs3 = r3.get();
//关闭服务
scr.shutdownNow();
}
}
//下载器
class WebDownloader2{
//下载方法
public void downloader(String url,String name){
try {
FileUtils.copyURLToFile(new URL(url),new File(name));
} catch (IOException e) {
e.printStackTrace();
}
}
}
线程安全问题
线程同步
1. Synchronized
package com.zuxia;
/**
* 线程同步
* 锁机制 Synchronized
* 缺点:一个线程持有锁会导致其他所有需要此锁的线程挂起
* 在多线程竞争下,加锁,释放锁会导致比较多的上下文切换和调度延时,引起性能问题
* 一个优先级高的线程等待一个优先级低的线程释放锁会导致优先级倒置,引起性能问题
* 锁机制 Synchronized:包括两种用法:
* Synchronized方法 和 Synchronized块
* Synchronized方法控制对象的访问,每个对象对应一把锁,每个Synchronized方法都必须·获得调用该方法的对象的锁才能执行
* 若将一个大的方法声明为Synchronized将会影响执行效率
* 方法里需要修改的方法才需要锁,只读的不需要
*
* 同步方法:synchronized默认锁的是 this
* 同步块:synchronized(obj){} ---> obj:同步监视器 锁的是任意对象obj
*
* 买票安全问题解决方法一:synchronized
*/
public class testSynchronized implements Runnable{
private int ticketNums = 50; //票数
boolean flag = true;
@Override
public void run() {
while (flag){
buy();
}
}
//添加锁
private synchronized void buy(){
if(ticketNums<=0){
flag = false;
return;
}
//模拟延时
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"--->买到了第"+ ticketNums-- +"张票");
}
public static void main(String[] args) {
testSynchronized ticket = new testSynchronized();
new Thread(ticket,"小灰灰").start();
new Thread(ticket,"小黑黑").start();
new Thread(ticket,"xiao").start();
}
}
2. Lock锁
package com.zuxia;
import java.util.concurrent.locks.ReentrantLock;
/**
* Lock锁
*Lock锁是显示锁(手动开启和关闭锁),synchronized是隐式锁,出了作用域自动释放
* lock只有代码锁,synchronized有代码块锁和方法锁
* Lock > 同步代码块 > 同步方法
*
* class testLock {
* private final ReentrantLock lock = new ReentrantLock();
* public void m(){
* lock.lock(); //开启锁
* try {
* //线程安全的代码
* }finally {
* lock.unlock(); //关闭锁
* //如果同步代码块有异常,需要将unlock()写入finally块
* }
* }
* }
*
* 买票问题解决方法:
*
*/
public class testLock {
public static void main(String[] args) {
TestLock2 ticket = new TestLock2();
new Thread(ticket,"小灰灰").start();
new Thread(ticket,"小黑黑").start();
new Thread(ticket,"小").start();
}
}
class TestLock2 implements Runnable{
private int ticketNums = 10; //票数
private final ReentrantLock lock = new ReentrantLock();
@Override
public void run() {
while (true){
try {
//开启锁
lock.lock();
//可能存在线程安全问题的代码
if(ticketNums>0){
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "--->买到了第" + ticketNums-- + "张票");
}else {
break;
}
}finally {
//释放锁
lock.unlock();
}
}
}
}