关于线程理解的笔记

1:关于线程的理解

1.1:什么是线程?

在了解线程之前,我们不得不提到一个词"进程"。那什么是进程呢?进程是系统进行资源分配的基本单位,是操作系统结构的基础。而线程是进程的一条执行路径,也是cpu的基本单位。简单来说,当我们启动一个程序就会在系统中开展一个进程,而通过任务管理器我们就可以看到这个系统所运行的进程。

 1.2:什么是线程

线程又称轻量级进程,线程是进程中的一条执行路径,也是cpu的基本调度单位。

1.3:什么是多线程

一个进程由一个或多个线程组成,并且在同一时间执行完成不同的工作

1.4:线程与进程的区别

线程就相当于进程中中独立的运行的子任务,一个线程可以包含多个进程,多个线程可以同时运行。而多线程的出现使我们更加可以更加充分的利用cpu的资源,大大提高了系统的执行能力。如果是一个单线程,那么系统的的执行能力就会特别的低下,运行效率大大降低。

2:java如何创建线程?

java中线程的的实现方式,大致有四种实现方式,目前只学会两种。

1.继续Thread;

//继承Thread
public class MyThread extends Thread{
private int takbov=100;
//重写run方法
@Override
public void run(){
for(int i=0;i<20;i++){
System.out.println("线程"+i);
}
}
}

测试类

public class Text {
public static void main(String[] args) {
// 创建一个线程
MyThread m1 =new MyThread();
//开启线程,当前线程和main主线程同时争夺CPU时间片
m1.start();
for (int i = 0; i <20 ; i++) {
System.out.println("main"+i);
}
}

}

2.实现Runnable

 public class MyRunable implements Runnable {
public void run() {
for (int i = 0; i <20 ; i++) {
System.out.println(Thread.currentThread().getName()+"线程:"+i);
}
}
}

测试类:

public class MyRunableTest {
public static void main(String[] args) {

MyRunable m1=new MyRunable(); //线程任务

//线程对象
Thread t1=new Thread(m1,"线程A");
t1.start();

//线程对象
Thread t2=new Thread(m1,"线程B");
t2.start();

for (int i = 0; i <20 ; i++) {
System.out.println("main线程:"+i);
}
}

}

2.1:为线程起名获取线程名称

【1】获取线程名称

(1)this.getName()必须是Thread的子类

(2)Thread.currentThread.getName();在任意位置获取线程名称

【2】为线程取名:

(1)通过调用线程对象的setName方法。

(2)通过线程子类的构造方法赋值。

package Thread;

public class Threads extends Thread{
private int takbov=100;
@Override
public void run(){
while (takbov>0){
takbov--;
//该方法可以在任何位置获取线程名:默认系统会为每个线程-- Thread-n
System.out.println(Thread.currentThread().getName()+"剩余"+takbov+"张票");
}
}
}

测试类:

package Thread;

public class Text {
public static void main(String[] args) {
Threads mo1 =new Threads();
// 通过调用对象名.setName()
mo1.setName("线程A");
  //2.开启线程.start()---底层会自动调用线程的run方法,来执行该线程的任务
mo1.start();
for (int i = 0; i <20 ; i++) {
System.out.println(Thread.currentThread().getName()+"==="+i);
}
}

}

3:实现Callable


public class Text {
public static void main(String[] args) throws ExecutionException, InterruptedException {
// MyCallable myCallable=new MyCallable();
// FutureTask<Integer> task=new FutureTask<Integer>(myCallable);//把线程任务封装到该类中,该类可以获取线程任务执行后的结果.
// Thread t=new Thread(task);
// t.start();
//
// System.out.println(task.get());
//自己创建的线程执行非常麻烦。都使用线程池来执行任务。---所以不要自己创建线程对象,而是使用线程池中的对象
ExecutorService executorService = Executors.newSingleThreadExecutor();
Future<Integer> submit = executorService.submit(new MyCallable());
System.out.println(submit.get());
}
}
class MyCallable implements Callable<Integer> {
//线程任务:
public Integer call() throws Exception {
int sum=0;
for(int i=0;i<=10;i++){
sum+=i;
}
return sum;
}
}
class MyRunnable implements Runnable{

public void run() {

}
}

Callable与Runnable的区别

1:重写的方法不同:Callable重写Call方法Runnable重写run方法。

2:返回值不同:Callable有返回值Runnable无返回值。

3:线程中常用的方法

1.:休眠

public static void sleep(long)

作用:是当前主动休眠millis毫秒

2:放弃

public static void yield();

作用:放弃本次时间片,回到就绪状态,参与下次时间片的争夺

3:加入

public void  join()

作用:允许其他线程加入当前线程,直到其他线程运行结束后。当前线程才可以运行

4:优先级

线程对象.setPriority()

作用:设置线程优先级 值1-10值越大获取cpu的概率越大。默认为5

5:守护线程

线程对象.setDaemon(true);设置守护线程。

线程有两类:守护线程(后台线程)和用户线程(前台线程)。

如果程序中所有前台线程都执行完毕,后台线程也会自动结束。

垃圾回收线程属于守护线程下

4:线程安全

如果多个程序在同时运行,而这些线程可能会同时运行这段代码。程序每次与运行这段代码的结果和单线程运行的结果是一样的,而且其他变量的值和预想的也一样,就是线程安全的。

实例:

package Thread;
public class Threads2 implements Runnable {
private int ticket = 100;
@Override
public void run() {
// 永远开启
while (true) {
if (ticket > 0) {// 有票 可以卖
// 使sleep模拟下出票时间
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
// 获取当前线程对象的名字
String name = Thread.currentThread().getName();
System.out.println(name + "正在卖:" + ticket--);
}
}
}
}

测试:

public class Text2 {
public static void main(String[] args) {
// 创建线程任务对象
Threads2 ticket = new Threads2();
// 创建三个窗对象
Thread t1 = new Thread(ticket, "1");
Thread t2 = new Thread(ticket, "2");
Thread t3 = new Thread(ticket, "3");

// 同时卖票
t1.start();
t2.start();
t3.start();

}
}

结果出现的现象:

 从中可以发现程序中出现的两个问题:

1:相同的票数

2:不存在的票数

这种几个窗口票数不同步,这种问题被称为线程安全性问题。

5:如何解决线程安全问题、

当多个线程访问同一资源时,其多个线程中对资源有写的操作,就容易出现线程安全问题。

要解决这种情况我们就需要使用锁,锁定的代码都是原子操作。

锁分为两种:

1:自动锁

public class Threads2 implements Runnable {
private int ticket = 100;
@Override
public void run() {
// 永远开启
while (true) {
// 自动上锁和释放锁
synchronized (this){
if (ticket > 0) {// 有票 可以卖
// 使sleep模拟下出票时间
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
// 获取当前线程对象的名字
System.out.println(Thread.currentThread().getName()+ "正在卖:" + ticket--);
}else {
break;
}
}
}
}
}

2:手动锁:Lock

package Thread;

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class Threads2 implements Runnable {
private int ticket = 100;
// 创建手动锁
private Lock l=new ReentrantLock();
@Override
public void run() {
// 窗口开启
while (true){
// 手动上锁
l.lock();
if (ticket>0){
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"正在卖"+ticket--);
}else {
break;
}
// 手动释放
l.unlock();
}
}
}

6:什么是死锁?

死锁:当第一个线程拥有A对象锁标记,并等待B对象锁标记,同时第二个线程拥有B对象标记,并等待A对象锁标记,并等待A对象锁标,产生死锁。

一个线程可以同时拥有多个对象的锁标记,当线程阻塞时,不会释放已经标记的锁标记,由此可能造成死锁。

死锁案例:

1:创建两个锁。

public class LBreak extends  Thread{
public static Object la= new Object();// A
public static Object lb=new Object();//B
}

2:设置两个线程。

public class TextLA extends Thread{

@Override
public void run() {
synchronized (LBreak.la){
System.out.println("拿到锁A");
synchronized (LBreak.lb){
System.out.println("拿到suoB");
System.out.println("开始执行程序");
}
}
}
}
public class TextLB extends Thread{
@Override
public void run(){
synchronized (LBreak.lb){
System.out.println("拿到锁B");
synchronized (LBreak.la){
System.out.println("拿到锁A");
System.out.println("开始执行另一道程序");
}
}
}
}

测试类:

public class Text{
public static void main(String[] args) {
TextLA textLA = new TextLA();
textLA.start();
TextLB textLB = new TextLB();
textLB.start();
}
}

运行结果:

如何解决死锁?有以下几种方式?

1:减少同步代码块的嵌套。

2:设置锁的超时时间。

3:可以使用安全类-jdk提高安全类

7:线程通信

 

 

1:如果存储处没有值,那么线程1可以往存储处有值,如果存储处有值,则线程1无法向其中存值。

2:如果存储处有值,则线程2可以取值。反之存储处无值则不可取值

3:线程1存一次线程2取一次

线程之间就可以通讯了

等待:

 

 

通知:

 7.2sleep与wait的区别

1:来自不同的类:sleep来自Thread,wait来自Object类。

2:会不会释放锁资源;sleep不会释放锁资源,wait自动释放锁资源。

3:用法:sleep时间到了自然就行了,而wait需要用notify()或者notifyAll()方法唤醒。

7.3:notify与notifyAll的区别

1.notify随机唤醒等待队列中一个线程,而notifyAll会唤醒等待队列中所有的线程。

8:线程的状态

NEW:新建状态
RUNNABLE: start()就绪状态-时间片-运行状态. 统称为RUNNABLE
BLOCKED: 堵塞状态。加锁时就如该状态
WAITING: 无期等待:  调用wait方法时会进入该状态
TIMED_WAITING: 有期等待---当调用sleep方法时就会进入该状态
TERMINATED: 终止状态。线程的任务代码执行完毕或出现异常。

线程的状态之间可以通过调用相应的方法进行转换。

9:线程池

线程池的概念:

问题:

线程是宝贵的内存资源、单个线程约占1MB空间,过多分配容易造成内存溢出。

频繁的创建及销毁线程会增加虚拟机回收的频率·资源开销·造成程序性能下降。

线程池的原理:

线程容器,可定线程分配的数量上限。

将预先创建的线程对象存入池中,并重用线程池中的线程对象。

避免频繁的创建和销毁

 

线程池的创建:

package demo7;
import java.lang.reflect.Executable;
import java.util.ArrayList;
import java.util.concurrent.*;

/*
Executor :它是线程池的跟接口;
void execute(Runnable command);执行Runnable类型的任务。
ExecutorService:它是Executor的子接口。--
void shutdown();关闭线程池,需要等待任务执行完毕
shutdownNow();立即关闭线程池,不接受新的任务
isTerminated();线程池是否终止,表示线程池的任务都执行完毕,并且线程池关闭了
isShutdown();判断是否执行了关闭
submit(Callable<T> task);提交任务,可以提交Callable
submit(Runnable task);提交任务可以提交Runnable
Executors: 它是线程池的工具类,该类提供了创建线程池的一些静态方法

*/
public class Test {
public static void main(String[] args) {
// 创建一个固定长度的线程池
// ExecutorService executorService = Executors.newFixedThreadPool(5);
// executorService.submit(new Runnable() {
// @Override
// public void run() {
// System.out.println(Thread.currentThread().getName()+"-----");
// }
// });
//2. 单一线程池。
// ExecutorService executorService = Executors.newSingleThreadExecutor();
// executorService.submit(new Runnable() {
// @Override
// public void run() {
// System.out.println(Thread.currentThread().getName()+"----");
// }
// });
//3. 可变线程池--缓存线程池
// ExecutorService executorService = Executors.newCachedThreadPool();
// for (int i = 0; i < 10; i++) {
// executorService.submit(new Runnable() {
// public void run() {
//
// System.out.println(Thread.currentThread().getName()+"----");
// }
// });
// }
//4. 延迟线程池。
// ScheduledExecutorService executorService = Executors.newScheduledThreadPool(5);
// for (int i = 0; i < 20; i++) {
// executorService.submit(new Runnable() {
// public void run() {
// System.out.println(Thread.currentThread().getName()+"-----");
// }
// });
// executorService.schedule(new Runnable() {
// public void run(){
// System.out.println(Thread.currentThread().getName()+"----");
// }
// },10,TimeUnit.SECONDS);
// }
// executorService.shutdown();
//上面通过Executors工具类创建线程池,但是阿里巴巴不建议使用。阿里建议使用原生的模式创建线程池。
/*
int corePoolSize,核心线程的个数
int maximumPoolSize,最多的线程个数
long keepAliveTime, 线程空闲时间。
TimeUnit unit, 空闲的单位
BlockingQueue<Runnable> workQueue:等待队列
*/
ArrayBlockingQueue arrayBlockingQueue = new ArrayBlockingQueue(5);//最多等待的任務
ThreadPoolExecutor executor= new ThreadPoolExecutor(5, 10, 10, TimeUnit.SECONDS, arrayBlockingQueue);
//零活
for (int i = 0; i <20 ; i++) {
executor.submit(new Runnable() {
public void run() {
System.out.println(Thread.currentThread().getName()+"-----");
}
});
}
}
}

 

 

 

 

 

 


 
 
 
posted @   白鹿与鱼  阅读(8)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 【自荐】一款简洁、开源的在线白板工具 Drawnix
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
点击右上角即可分享
微信分享提示