第十章:线程

我们从今天开始学习线程。

关于线程的基本概念

程序,进程和线程。

程序:为完成特定的功能,使用计算机语言编写的一系列的指令集合,即静态代码

进程: 运行中的程序,被加载到内存中,是操作系统分配资源的最小单位

线程:是进程中的最小的执行单元(任务),是操作系统进行任务调度的最小单位,隶属于进程

关于线程:

1.一个线程只能属于一个进程,线程不能脱离进程 2.一个进程中至少有一个线程(主线程) java中main方法就是用来启动主线程

3.在主线程中可以创建并启动其他的线程。

4.一个进程内的所有线程都共享该进程的内存资源。

创建线程

在java中,创建线程有两种方式,继承Thread类和实现Runnable接口。

继承Thread类

继承Thread类,重写其中的run方法。

注意!:Thread类中的run方法本身并不执行任何操作,但如果我们重写了run方法,当线程启动时,它将执行run方法。

 //继承Thread类,并重写其中的run方法。
 public class mythread_demo1 extends Thread {
     @Override
     public void run() {
     /*
     在run方法体内写程序员想要在这个线程内执行的东西。
     */
         for (int i = 0; i < 1000; i++) {
             System.out.println("myThread_demo1"+i);
         }
     }
 }

 //
 public class dxc_demo1 {
     public static void main(String[] args) {
         mythread_demo1 d1 =new mythread_demo1();
         d1.start();//注意,调用的是start方法!,如果调用的是run方法,那就不构成创建线的行为了。
     }
 }
 ​

实现Runnable接口

java.lang.Runnable接口仅仅只有一个抽象方法run(),可以通过实现Runnable接口的方式来实现线程,只需要实现其中的run()方法。

 //mythread类实现Runnable接口,并实现接口中的run方法。
 public class mythread implements Runnable{
     @Override
     public void run() {
      /*
     在run方法体内写程序员想要在这个线程内执行的东西。
     */
         for (int i = 0; i < 1000; i++) {
             System.out.println("Runnable"+i);
         }
     }
 }
 ​

 ​
 public class dxc_demo1 {
     public static void main(String[] args) {
    /*
    创建一个线程作为外壳,将m1包起来,
    然后,用创建的Thread类thread对象调用statr()方法。
    注意,调用的是start方法!,如果调用的是run方法,那就不构成创建线的行为了。
    */
         mythread m1 =new mythread();
         Thread thread =new Thread(m1);
         thread.start();
 ​
     }
 }

关于创建线程中继承方式和实现方式的联系与区别。

区别:

继承Thread: 线程代码存放Thread子类的run方法中。

实现Runnable接口: 线程代码存在接口的子类的run方法中

使用Runnable接口创建线程的好处:

1.避免了单继承的局限性。(一个类只能有一个父类(只能继承一个类))

2.多个线程可以共享同一个接口实现类的对象,非常适合多个相同线程来处理同一个资源。

Thread类中的常用方法

 void start() 启动线程 
 final String getName() 返回线程的名称 
 final void setPriority(int newPriority) 设置线程的优先级 
 final int getPriority() 返回线程的优先级 
 final void join() 等待线程终止 
 static Thread currentThread() 返回对当前正在执行的线程对象的引用 
 static void sleep(long millis) 让当前正在执行的线程休眠(暂停执行), 休眠时间由milli s(毫秒)指定 
 yield() 线程让步

 System.out.println(Thread.currentThread().getName());

关于线程的优先级:

关于优先级:事实上,计算机只有一个CPU,各个线程轮流获得CPU的使用权,才能执行任务,优先级较高的线程有更多获得CPU的机会,反之亦然。

优先级用整数表示,取值范围是1~10,一般情况下,线程的默认优先级都是5,但是也可以通过setPriority和getPriority方法来设置或返回优先级。

线程状态

新建:当一个Thread类或者其子类的对象被声明并创建时,新生的线程对象处于新建状态。

就绪: 处于新建状态的线程被start()后,将进入线程排列等待CPU时间片,此时它具有了运行的条件,只是没分配到CPU资源。

运行:当就绪的线程被调度并获得CPU资源时,便进入运行状态,run()方法定义了线程的操作和功能。

阻塞:在这种特殊情况下,被人为挂起或执行输入输出操作时,让出CPU并临时中指自己的操作,进入阻塞状态。

死亡:线程完成了它的全部工作或线程被提前强制性地中止或出现异常导致结束

守护线程

Java中的线程分为两类:用户线程和守护线程

任何一个守护线程都是整个JVM中所有非守护线程的保姆.只要当前JVM实例中尚存在任何一个非守护线程没有结束,守护线程就全部工作;

只有当最后一个非守护线程结束时,守护线程随着JVM一同结束工作.

守护线程的作用是为了其他线程的运行提供便利服务,守护线程最典型的应用就是GC(垃圾回收站),它就是一个很称职的守护者.

用户线程和守护线程两者几乎没有什么区别,唯一的不同之处就在于虚拟机的离开:如果用户线程已经全部退出运行了,只剩下守护线程存在了,虚拟机也就退出了.因为没有了被守护者,守护线程也就没有了工作可做了,也就没有继续运行程序的必要了

设置守护线程;setDaemon(Boolean on)

注意:设置线程为守护线程必须在启动线程之前,否则会跑出一个IllegalThreadState Excetion异常

多线程的概念

多线程是指程序中包含多个执行单元,即在一个程序中可以同时运行

多个不同的线程来执行不同的任务,也就是说允许单个程序创建多个并行执行的线程来完成各自的任务

何时需要多线程

程序需要同时执行两个或多个任务

程序需要实现一些需要等待的任务时,如用户输入,文件读写操作,网络操作,搜索等

多线程的优点:

提高程序的响应

提高CPU的利用率

改善程序结构,将复杂任务分为多个线程,独立运行

多线程的缺点:

线程也是程序,所以线程需要占用内存,线程越多占用内存也越多;

多线程需要协调和管理,所以需要CPU时间跟踪线程;

线程之间对共享资源的访问会相互影响;

线程同步

并发与并行

并行:多个CPU同时执行多个任务,比如:多个人同时做不同的事.

并发:在一个时间段内一次执行操作,例如卖票,抢购,.秒杀看似同时进行,实际是一个一个执行

多线程同步:

多个线程同时读写同一份共享资源时,可能会引起冲突,所以引入线程"同步机制",即各线程间要有先来后到:

同步就是排队+锁:

几个线程之间要排队,一个个对共享资源进行操作,而不是同时进行操作;

为了保证数据在方法中被访问时的正确性,在访问时加入锁机制

确保一个时间点只有一个线程访问共享资源,可以给共享资源加一把锁,哪个线程获取了这把锁,才有权利访问该共享资源

在Java代码中实现同步:

使用synchronized(同步锁)关键字同步方法或代码块.

synchronized(同步锁){

//需要被同步的代码;

}

synchronized还可以放在方法声明中,表示整个方法,为同步方法

例如:

public synchronized void show(String name){

//需要被同步的代码;

}

同步锁

同步锁可以是任何对象,必须唯一,保证多个线程获得是同一个对象(用来充当锁标记)_.

同步执行过程

1.第一个线程访问,锁定同步对象,执行其中代码块

2.第二个线程访问,发现同步对象被锁定,无法访问

3.第一个线程访问完毕,解锁同步对象

4.第二个线程访问,发现同步对象没有锁,然后锁定并访问

一个线程持有锁会导致其他所有需要此锁的线程挂起;在多线程竞争下,加锁,释放锁会导致比较多的上下文切换和调度延时,引起性能问题

Lock锁

Lock

从JDK 5.0开始,Java提供了更强大的线程同步机制-通过显示定义同步锁对象来实现同步.同步锁使用Lock对象充当

java.util.concurrent.locks.Lock接口是控制多个线程对共享资源进行访问的工具.锁提供了对共享资源的独占访问.每次只能有一个线程对Lock对象加锁,线程开始访问共享资源之前应先获得Lock对象

ReentrantLock类实现了Lock,它拥有与synchronized相同的并发性和内存语义,在实现线程安全的控制中,比较常用的是ReentrantLock,可以显示加锁,释放锁

线程死锁

死锁

不同的线程分别占用对方需要的同步资源不放弃,都在等待对方放弃自己需要的同步资源,就形成了线程的死锁.

出现死锁后,不会出现异常,不会出现提示,只是所有的线程都处于阻塞状态,无法继续

设计时,考虑清楚锁的顺序,尽量减少嵌套的加锁交互数量

线程通信

线程通信指定是多个线程通过相互牵制,互相调度,即线程间的互相作用

涉及三个方法:

.wait一旦执行此方法,当前线程就进入阻塞状态,并释放同步监视器

.notify一旦执行此方法,就会唤醒被wait的一个线程,如果有多个线程被wait,就唤醒优先级高的那个

.notifyAll一旦执行此方法,就会唤醒所有被wait的线程

注意:

.wait() ,notify(),notifyAll()三个方法必须使用在同步代码块或同步方法中

新增创建线程方式

实现Callable接口与使用Runnable相比,Callable功能更强大些

相比run()方法,可以有返回值

方法可以跑出异常

支持泛型的返回值

需要借助FutureTask类,获取返回结果

接受任务

FutureTask<Integer> futureTask=new FutureTask(任务);

创建线程

Thread t=new Thread(futureTask);

t.start();

Integer val=futureTask.get();获得线程call方法的返回值

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值