线程
1、基本概念:程序 线程 进程
程序:
进程:
线程:
进程可以进一步化为多个线程,是一个程序内的一条执行路径
若一个进程同一时间并行多个线程,就是支持多线程
一个Java程序至少有三个线程 main() 主线程 gc 垃圾回收线程 异常处理线程
1、并行和并发
- 并行:多个cpu同事执行多个任务
-
2、多线程的优点
提高应用程序的响应
- 提高计算cpu的使用效率
-
2、线程的创建和使用
jvm允许程序运行多个线程,它通过java.lang.Thread类来体现
Thread类的特点
每个线程都是通过特定的Thread类的对象的run()方法来完成操作,经常把run()方法的主体叫线程体
-
1、线程的创建
方式一:继承Thread类
步骤
- 继承Thread类
- 重写Thread类的run()方法
- 创建Thread的子类对象
- 调用子类对象的start()方法
```java
public class ThreadTest {
public static void main(String[] args) {
MyThread t1 = new MyThread();
t1.start();
//匿名子类对象
new Thread(){
@Override
public void run() {
} }.start();for (int i = 0; i < 100; i++) {if (i % 2 != 0) {System.out.println(Thread.currentThread().getName() + ":" + i);}}
} }
class MyThread extends Thread { @Override public void run() { for (int i = 0; i < 100; i++) { if (i % 2 == 0) { System.out.println(Thread.currentThread().getName() + “:” + i); } } } }
线程的常用方法1. start() 启动当前线程,调用线程的run()方法1. run() 通常需要重写Thread类中的run方法 、将创建线程要执行的操作申明在此方法中1. getName() 获取线程名称1. setName() 设置线程名称1. yield() 释放当前cpu的执行权1. join() 在线程a中调用线程b的join()方法,线程a进入阻塞状态,知道线程b执行完毕,线程a才结束阻塞状态1. stop() 已过时 强制结束当前线程1. sleep() 让当前线程睡眠XXX毫秒,指定时间内线程是阻塞状态1. isAlive() 判断当前线程是否存活<a name="ytn8Q"></a>#### 方式二:实现 Runnable 接口1. 创建实现Runnable接口的类1. 实现类去实现Runnable中的抽象方法run()1. 创建实现类的对象1. 将此对象作为参数传递到Thread类的构造器中,创建Thread类的对象1. 调用Thread类对象的run()方法```javapublic class ThreadTest1 {public static void main(String[] args) {MyThread1 m1 = new MyThread1();Thread t1 = new Thread(m1);t1.start();Thread t2 = new Thread(m1);t2.start();}}class MyThread1 implements Runnable {@Overridepublic void run() {for (int i = 0; i < 100; i++) {if (i % 2 == 0) {System.out.println(Thread.currentThread().getName()+":"+i);}}}}
方式三:实现Callable接口
与Runnable 相比 Callable更强大
- 相比run方法 可以有返回值
- 方法可以抛出异常
- 支持泛行的返回值
- 需要借助FutureTask类,比如获取返回结果
import java.util.concurrent.Callable;import java.util.concurrent.ExecutionException;import java.util.concurrent.FutureTask;/**1.创建Callable的实现类2.实现call方法,并将线程需要执行的操作放在call方法内3.创建Callable接口实现类的对象4.将Callable接口实现类的对象作为参数传递到 FutureTask 的构造器当中,创建FutureTask的对象5.将FutureTask的对象作为参数传递到Thread的构造器当中创建Thread对象并掉用start()方法6.获取Callable call 方法的返回你*/public class CallableTest {public static void main(String[] args) {ThreadNew tn = new ThreadNew();FutureTask task = new FutureTask(tn);new Thread(task).start();try {Object o = task.get();System.out.println(o);} catch (InterruptedException e) {e.printStackTrace();} catch (ExecutionException e) {e.printStackTrace();}}}class ThreadNew implements Callable {@Overridepublic Object call() throws Exception {int sum = 0;for (int i = 0; i < 100; i++) {if (i % 2 == 0) {sum += i;}}return sum;}}
方式四:线程池
- 提高响应速度
- 降低资源消耗
- 便于线程管理 ```java import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.ThreadPoolExecutor;
public class ThreadPool { public static void main(String[] args) { //1.提供执行数量的线程池 ExecutorService eService = Executors.newFixedThreadPool(10); ThreadPoolExecutor service = (ThreadPoolExecutor) eService; service.setCorePoolSize(15); //2.执行指定线程的操作,需要实现Runnable或者Callable接口 service.execute(new MyThreadNew());//适用于 Runnable //service.submit();//适用于 Callable //关闭线程池 service.shutdown(); } }
class MyThreadNew implements Runnable {
@Overridepublic void run() {for (int i = 0; i < 100; i++) {if (i % 2 == 0) {System.out.println(i);}}}
}
<a name="cVyJG"></a>#### 如何理解实现Callable接口创建多线程比实现Runnable接口创建多线程功能强大1. call() 有返回值1. call() 可以抛出异常1. Callable支持泛型<a name="NQTva"></a>#### 线程优先级MIN_PRIORITY 1<br />MAX_PRIORITY 10<br />NORM_PRIORITY 5> 高优先级线程要抢占低优先级线程cpu的执行权,高优先级的线程高概率优先执行,并不意味着高优先级执行完以后才执行低优先级<br /><a name="8f6dede4"></a>#### 比较线程创建的两种方式1. 开发中优先使用 实现 Runnable 接口 没有单继承的限制1. 实现方式更适合来处理多个线程有共享数据的情况<a name="h2p6M"></a>#### 二者之间的联系1. Thread 类也实现了Runnable 接口1. 都需要重写 run()方法,将线程要执行的逻辑写在run()方法中<a name="128fl"></a>## 3、线程的生命周期<a name="Fjs2r"></a>### 新建<a name="HCvui"></a>### 就绪<a name="dXf6x"></a>### 运行<a name="jiENc"></a>### 阻塞<a name="iXCtM"></a>### 死亡<a name="rIK9g"></a>## 4、线程的同步> 好处:解决线程安全的问题。> 局限性:操作同步代码时只能有一个线程参与,相当于但线程,效率低<a name="PPTsN"></a>### 方式一:同步代码块synchronized (同步监视器){<br />//需要被同步的代码<br />}<br />说明:需要操作共享数据的代码,极为需要被同步的代码块<br />同步监视器:俗称锁,任何一个类的对象,都可以充当锁<br />要求所有线程都使用同一把锁。```javapublic class WindowTest {public static void main(String[] args) {Window w = new Window();Thread t1 = new Thread(w);t1.setName("窗口一");t1.start();Thread t2 = new Thread(w);t2.setName("窗口二");t2.start();Thread t3 = new Thread(w);t3.setName("窗口三");t3.start();}}class Window implements Runnable {private int ticket = 100;@Overridepublic void run() {while (true) {//这里可以是用this或者当前类的对象 Window.classsynchronized (this) {if (ticket > 0) {System.out.println(Thread.currentThread().getName() + ":" + ticket);ticket--;} else {break;}}}}}
方式二:同步方法
如果操作共享数据的代码完整的声明在一个方法中,我们不妨将此方法声明为同步方法
public class SingletonTest {public static void main(String[] args) {System.out.println(MySingle.getInstance());System.out.println(MySingle.getInstance());}}class MySingle{private static MySingle instance = null;private MySingle() {}public static synchronized MySingle getInstance() {if (instance == null ){instance = new MySingle();}return instance;}}
死锁的问题: 不同的线程分别占用对方需要的资源不放弃,都在等待对方放弃自己所的资源,就形成了死锁。 出现死锁后不会出现异常,不会出现提示,只是所有的线程都处在阻塞状态,无法继续。
解决方法:
public class WindowTest3 { public static void main(String[] args) { Window3 w = new Window3(); Thread t1 = new Thread(w); t1.setName(“窗口一”); t1.start(); Thread t2 = new Thread(w); t2.setName(“窗口二”); t2.start(); Thread t3 = new Thread(w); t3.setName(“窗口三”); t3.start(); } }
class Window3 implements Runnable { private int ticket = 100; private ReentrantLock lock = new ReentrantLock(true); @Override public void run() { while (true) { try { lock.lock(); if (ticket > 0) { System.out.println(Thread.currentThread().getName() + “:” + ticket); ticket—; } else { break; } } finally { lock.unlock(); } } } }
<a name="bHTyE"></a>### synchronized 和lock的异同?相同: 都可以解决线程安全问题<br />不同: synchronized在执行响应的同步代码块以后,自动的释放同步监视器,lock需要手动启动同步,同时结束也需要手动实现,<br />lock 只有代码块锁,synchronized既有代码块锁,也有方法锁。<a name="DFYqV"></a>## 5、线程的通信<a name="bR87D"></a>### 练习:交替打印```javapublic class NumberTest {public static void main(String[] args) {Number n = new Number();Thread t1 = new Thread(n);t1.setName("线程一:");Thread t2 = new Thread(n);t2.setName("线程二:");t1.start();t2.start();}}class Number implements Runnable {private int num = 1;@Overridepublic void run() {while (true) {synchronized (this) {notify();if (num <= 100) {System.out.println(Thread.currentThread().getName() + ":" + num);num++;try {wait();} catch (InterruptedException e) {e.printStackTrace();}}}}}}
涉及到三个方法:
- wait() 一旦执行此方法,线程就会进入阻塞状态,同时释放锁
- notify() 执行此方法,就会唤醒一个被wati的线程 ,优先级高的优先被唤醒
- notifyAll() 执行此方法,就会唤醒所有被wait的线程
说明: 1. wait() notify() notifyAll() 三个方法只能使用在同步方法、和同步代码块当中。 2.三个方法的调用者必须是同步代码块或 3.三个方法定义在Object类当中
面试题1:sleep 和wait的异同
相同点:一旦执行方法,都可以是当前线程进入阻塞状态。
不同点:1. 声明的位置不同
2.调用的范围要求不一样,sleep 可以在任何需要的场景调用,wait方法必须使用在同步方法和同步代码块中
3.两个方法都是用在同步方法块和同步方法中,sleep不会释放锁,wait会释放锁。
