文章目录
一:概念
程序:为完成特定的任务,用某种语言编写的一组指令的集合,即一段静态的代码
进程:正在运行的一个程序
线程:一个程序内部的一条执行路径
进程可以细化为多个线程
每个线程都拥有自己独立的:栈和程序计数器
多个线程共享同一个进程的结构:方法区和堆
一个进程中的多个线程共享一份方法区和堆
每个线程都有独立的程序计数器和虚拟机栈
并行:多个cpu同时执行多个任务
并发:一个CPU同时执行多个任务
二:多线程的创建
方法一:使用Thread
①:创建类来继承Thread
②:重写Thread类中的run()方法—>此线程执行的方法放在run()方法中
③:创建子类对象
④:子类调用start()方法,start()作用:调用此线程;执行此线程的run()方法
方法二:实现Runnable接口
- 1.创建实现了Runnable接口的类
- 2.实现Runnable中的唯一一个抽象方法:run()
- 3.创建实现类的对象
- 4.将实现类的的对象放到Thread类的构造器中,创建Thread类对象
- 5.通过Thread类的对象调用strat()方法
方法三:Lock锁 —JDK5.0新增
- 1.面试题:synchronized与lock的异同?
- 相同:都是去解决线程安全问题
- 不同点:synchronized机制在执行完相应的同步代码以后,自动的释放同步监视器
- Lock需要手动的启动同步(lock()),同时手动结束同步(unlock())
*2.优先使用顺序: - lock ->同步代码块 ->同步方法
- 面试题:如何解决线程的安全问题?有几种方式?
- 三种:lock和两种synchronized
方法三:Lock锁举例
//方法三::Lock锁
class Window implements Runnable{
private int ticket = 100;
//1.实例化ReentrantLock
private ReentrantLock lock = new ReentrantLock(false);//默认值为false,true的意思为公平,谁先到谁先有使用权
@Override
public void run() {
while(true) {
//2.调用lock()
lock.lock();
try {
try {
Thread.sleep(100);
} catch (Exception e) {
e.printStackTrace();
}
if (ticket > 0) {
System.out.println(Thread.currentThread().getName() + "\t" + ticket);
ticket--;
}
}finally {
//3.调用解锁方法:unlock()
lock.unlock();
}
}
}
}
public class LockTest {
public static void main(String[] args) {
Window w = new Window();
Thread t1 = new Thread(w);
Thread t2 = new Thread(w);
Thread t3 = new Thread(w);
t1.setName("窗口1");
t2.setName("窗口2");
t3.setName("窗口3");
t1.start();
t2.start();
t3.start();
}
}
三:测试Thread中的常用方法
1.start():启用当前线程;调用当前线程的run()方法
2.run():通常需要重写Thread中的run()方法,将创建的线程要执行的
操作申明在此方法中
3.currentThread():静态方法,返回执行当前代码的线程
4.getName():获取当前线程的名称
5.setName():设置当前线程的名称
6.yield():释放当前CPU的执行权
7.join():在线程A中调用线程B的join(),此时线程A就进入阻塞状态,直到线程B都执行完
线程A才结束阻塞状态。
8.stop():已过时,当执行此方法时,强制结束当前线程。
9.sleep(long millitime):让当前线程睡眠指定的millitime毫秒,在这个睡眠时间里线程是阻塞状态
10:isAlive():判断当前线程是否存活
四:线程的优先级
一:
MAX_PRIORITY: 10
MIN_PRIORITY: 1
NORM_PRIORITY: 5 ----->默认优先级
二:如何获取和设置当前的优先级
getPriority():获取
setPriority():设置
高优先级的线程抢占低优先级的CPU执行权,只是从概率上讲,高优先级的线程
高概率的情况下被执行,并不意味着高优先级线程全执行完执行低优先级
*/
五:比较线程的两种创建方式:
-
开发中:优先选择 Runnable接口的方式
-
原因:
1.实现的方式没有类的单继承的局限性
2.实现的方式更适合来处理多个线程有共享数据的情况 -
联系:public class Thread implements Runnable
-
相同点:两种方式都重写run(),将线程的执行写在run()中,目前两种方式,要想启动线程,都是调用的Thread类中的start()
线程通信:wait(),notify(),notifyAll():此三个方法定义在Object类中。
补充:线程的分类:一种是守护线程,一种是用户线程。
1.它们在几乎每个方面都是相同的,唯一的区别是判断JVM何时离开。
2.守护线程是用来服务用户线程的,通过在start()方法前调用。thread.setDaemon(true)可以把一个用户线程变成一个守护线程。
3.Java垃圾回收就是一个典型的守护线程。
4. 若JVM中都是守护线程,当前JVM将退出。
六:线程的生命周期
七:线程同步
- 例子:创建三个窗口卖票,总票数为100张
- 存在线程安全问题,待解决
- 1.问题:卖票过程中,出现了重票、错票–>出现了线程的安全问题
- 2.问题出现的原因:当某个线程操作车票的过程中,尚未操作完成时,其他线程参与进来,也操作车票
- 3.如何解决:当一个线程在操作ticket的时候,其他线程不能参与进来,直到线程a操作完ticket时,其他线程才可以操作ticket,这种即使线程a出现了阻塞,也不能被改变。
方式一:同步代码块:
- synchronized(同步监视器){
-
//需要被同步的代码
- }
- 说明:
- 1.操作共享数据的代码,即为需要被同步的代码
- 2.共享数据:多个线程共同操作的变量,比如:ticket就是共享数据
- 3.同步监视器,俗称:锁。任何一个类的对象,都可以充当锁。
要求:多个线程必须共用同一把锁
补充:在实现Runnable接口创建多线程的方法中,我们可以考虑使用this充当同步监视器
方式二:同步方法
- 5.同步方式,解决了线程的安全问题。—>好处
操作同步代码时,只能有一个线程参与,其他线程等待。相当于是一个单线程的过程,效率低。—>效率低
方式三:Lock锁
- 解决线程安全问题的方式三:Lock锁 —JDK5.0新增
- 1.面试题:synchronized与lock的异同?
- 相同:都是去解决线程安全问题
- 不同点:synchronized机制在执行完相应的同步代码以后,自动的释放同步监视器
Lock需要手动的启动同步(lock()),同时手动结束同步(unlock()) - 2.优先使用顺序: lock ->同步代码块 ->同步方法
- 面试题:如何解决线程的安全问题?有几种方式?
三种:lock和两种synchronized
同步代码块的方法举例
①实现Runnable接口
//例子:创建三个窗口卖票,总票数为100张
//实现Runnable接口
class Window1 implements Runnable{
private int ticket = 100;//不需要加static,因为只生成一个Window1的对象
Object obj = new Object();
@Override
public void run() {
while(true) {
synchronized (obj) {//或者synchronized(this)
try {
sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
if (ticket > 0) {
System.out.println(Thread.currentThread().getName() + ":" + ticket);
ticket--;
} else {
break;
}
}
}
}
}
public class WindowTest1 {
public static void main(String[] args) {
Window1 w1 =new Window1();
Thread t1 = new Thread(w1);
Thread t2 = new Thread(w1);
Thread t3 = new Thread(w1);
t1.setName("窗口一:");
t2.setName("窗口二:");
t3.setName("窗口三:");
t1.start();
t2.start();
t3.start();
}
}
②继承Thread
/*
例子:创建三个窗口卖票,总票数为100张
说明:再继承Thread类创建多线程的方式中,慎用this充当同步监视器
考虑使用当前类充当同比监视器
*/
public class WindowTest2 {
public static void main(String[] args) {
Window w1 = new Window();
Window w2 = new Window();
Window w3 = new Window();
w1.setName("线程1");
w2.setName("线程2");
w3.setName("线程3");
w1.start();
w2.start();
w3.start();
}
}
class Window extends Thread{
private static int ticket = 100;//为什么写成静态,因为三个对象去共享100张票
static Object obj = new Object();
@Override
public void run() {
while(true) {
//正确的
synchronized(obj){
//或者synchronized(Window.class){//class clazz = Window.class只会加载一次
//错误的
// synchronized(this) 因为创建了三个对象,this指代不明
if (ticket > 0) {
System.out.println(Thread.currentThread().getName()+":"+ticket);
ticket--;
} else {
break;
}
}
}
}
}
同步方法举例
①实现Runnable接口
/**
* 使用同步方法实现Runnable接口的线程安全问题.
*
* 关于同步方法的总结:
* 1.同步方法任然涉及到监视器,只是不需要显式的申明private synchronized void show()
* 2.非静态的同步方法:同步监视器:this
* 静态的同步方法:同步监视器:当前类本身
*/
class Window3 implements Runnable{
private int ticket = 100;
@Override
public void run() {
while(true){
show();
}
}
private synchronized void show(){//同步监视器:this
//synchronized (this){
if (ticket > 0) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + ":卖票,票号为:" + ticket);
ticket--;
}
//}
}
}
public class WindowTest3 {
public static void main(String[] args) {
Window3 w = new Window3();
Thread t1 = new Thread(w);
Thread t2 = new Thread(w);
Thread t3 = new Thread(w);
t1.setName("窗口1");
t2.setName("窗口2");
t3.setName("窗口3");
t1.start();
t2.start();
t3.start();
}
}
②继承Thread
/*
例子:创建三个窗口卖票,总票数为100张
说明:再继承Thread类创建多线程的方式中,慎用this充当同步监视器
考虑使用当前类充当同比监视器
*/
public class WindowTest4 {
public static void main(String[] args) {
Window w1 = new Window();
Window w2 = new Window();
Window w3 = new Window();
w1.setName("线程1");
w2.setName("线程2");
w3.setName("线程3");
w1.start();
w2.start();
w3.start();
}
}
class Window4 extends Thread{
private static int ticket = 100;//为什么写成静态,因为三个对象去共享100张票
@Override
public void run() {
while(true) {
show();
}
}
public static synchronized void show() {//同步监视器为当前类:window4.class
//错误的 public symchronized voidshow()//同步监视器为t1,t2,t3,此种解决方式是错误的
if (ticket > 0) {
System.out.println(Thread.currentThread().getName() + ticket);
ticket--;
}
}
}
lock锁举例
class Window implements Runnable{
private int ticket = 100;
//1.实例化ReentrantLock
private ReentrantLock lock = new ReentrantLock(false);//默认值为false,true的意思为公平,谁先到谁先有使用权
@Override
public void run() {
while(true) {
//2.调用lock()
lock.lock();
try {
try {
Thread.sleep(100);
} catch (Exception e) {
e.printStackTrace();
}
if (ticket > 0) {
System.out.println(Thread.currentThread().getName() + "\t" + ticket);
ticket--;
}
}finally {
//3.调用解锁方法:unlock()
lock.unlock();
}
}
}
}
public class LockTest {
public static void main(String[] args) {
Window w = new Window();
Thread t1 = new Thread(w);
Thread t2 = new Thread(w);
Thread t3 = new Thread(w);
t1.setName("窗口1");
t2.setName("窗口2");
t3.setName("窗口3");
t1.start();
t2.start();
t3.start();
}
}
八:线程的通信
- 线程通信的例子:使用两个线程打印1-100,线程1,线程2交替打印
- 涉及到的三个方法:
- wait():一旦执行此方法,当前线程进入阻塞状态,并释放同步监视器。
- notify():一旦执行此方法,就会唤醒被wait的一个线程,如果有多个线程被wait,就唤醒优先级高的那个。
- notifyAll():一旦执行此方法,就会唤醒所有被wait的线程。
- 说明:
- 1.wait(),notify(),notifyAll()三个方法必须使用在同步代码块或同步方法中。
- 2.wait(),notify(),notifyAll()三个方法的调用者必须是同步代码块或同步方法中的同步监视器。否则,会出现IllegalMonitorStateException异常。
- 3.wait(),notify(),notifyAll()三个方法是定义在java.lang.Object类中。
面试题:sleep() 和 wait()的异同?
- 相同点:一旦执行方法,都可以使得当前线程进入阻塞状态
- 不同点:
1)两个方法声明的位置不同,Thread类声明sleep(),Object类中声明wait()。
2)调用的要求不同:sleep()可以在任何需要的场景下调用,wait()必须声明在同步代码块和同步方法中。
3)关于是否是否同步监视器:如果两个方法都声明在同步代码块和同比监视器中,则sleep()不会释放锁,wait()会。