多线程笔记

多线程笔记

1、创建多线程的两种方法

(1)通过创建Thread的子类实现多线程

创建多线程步骤:

① 将一个类MyThread声明为Thread的子类。 这个子类应该重写Thread类的方法run()

② 在run()方法中编写多线程想要执行的方法

③ 创建MyThread的实例对象,使用实例对象的start()方法执行线程

void start(): 导致此线程开始执行; Java虚拟机调用此线程的run方法。

代码如下:

package com.java.myThread;

/**
 * @Description 该类实现Thread类,为线程类
 * @Author yc
 * @Version V1.0.0
 * @Date 2021/8/1
 */
public class MyThread extends Thread{

    @Override
    public void run() {
        //run()方法中编写多线程想要执行的代码
        for (int i = 0;i < 100; i++){
            System.out.println(i);
        }
    }
}
package com.java.myThread;

/**
 * @Description 该类展示通过实现Thread来实现多线程
 * @Author yc
 * @Version V1.0.0
 * @Date 2021/8/1
 */
public class MyThreadTest01 {
    public static void main(String[] args) {
        //创建线程1
        MyThread myThread01 = new MyThread();
        //创建线程2
        MyThread myThread02 = new MyThread();

        //使用start()方法启动线程
        //void start() :导致此线程开始执行; Java虚拟机调用此线程的run方法。
        myThread01.start();
        myThread02.start();

    }
}
(2)通过继承Runnable接口实现多线程

创建多线程步骤:

① 创建一个类MyRunnable继承Runnable接口。 这个类需要run()

② 在run()方法中编写多线程想要执行的方法

③ 通过Thread的构造函数创建MyRunnable的实例对象,使用实例对象的start()方法执行线程

package com.java.myThread;

/**
 * @Description 该类继承了Runnable接口
 * @Author yc
 * @Version V1.0.0
 * @Date 2021/8/1
 */
public class MyRunnable implements Runnable{
    @Override
    public void run() {
        for (int i=0;i<100;i++){
            System.out.println(Thread.currentThread().getName()+":"+i);
        }
    }
}
package com.java.myThread;

/**
 * @Description
 * @Author yc
 * @Version V1.0.0
 * @Date 2021/8/1
 */
public class MyRunnableTest {
    public static void main(String[] args) {
        //创建MyRunnable对象实例
        MyRunnable myRunnable = new MyRunnable();

        //创建线程
        Thread thread01 = new Thread(myRunnable,"线程1");
        Thread thread02 = new Thread(myRunnable,"线程2");

        //启动线程
        thread01.start();
        thread02.start();
    }
}

相比继承Thread类,实现Runnable接口的好处:

① 避免了JAVA单继承的局限性

② 适合多个相同程序的代码去处理同一个资源的情况,把线程和程序的代码、数据有效分离,较好的体现了面对对象的设计思想

2、设置与获取线程名称

(1)设置线程名称

①通过setName()方法对线程名称进行设置

public final void setName(String name):将此线程的名称更改为等于参数name 。

//创建线程1
MyThread myThread01 = new MyThread();
//将myThread01线程名称设置为线程1
myThread01.setName("线程1");
//创建线程2
MyThread myThread02 = new MyThread();
//将myThread02线程名称设置为线程2
myThread02.setName("线程2");

②通过Thread(String name)构造方法对线程名称进行设置

Thread(String name):分配一个新的 Thread对象。

//创建线程1 名称设置为线程1
MyThread myThread01 = new MyThread("线程1");
//创建线程2 名称设置为线程2
MyThread myThread02 = new MyThread("线程2");

使用构造方法设置线程名称时,需要注意,在MyThread类中必须重写Thread类的Thread(String name)方法

//重写构造方法
public MyThread(String name) {
    super(name);
}
(2)获取线程名称

通过getName()方法获取线程名称

String getName() :返回此线程的名称。

public void run() {
    //run()方法中编写多线程想要执行的代码
    for (int i = 0;i < 100; i++){
        //getName()获取线程名称
        System.out.println(getName()+":"+i);
    }
}

如果想要获取main()方法所在的线程名称,可通过currentThread()方法获取线程名称

public static Thread currentThread()返回对当前正在执行的线程对象的引用。

public static void main(String[] args) {
        //输出main()方法对应的线程名称
        System.out.println(Thread.currentThread().getName());   //输出结果为main
    }

3、线程的优先级

(1)线程调度模型

线程有两种调度模型:

① 分时调度模型 : 所有的线程轮流使用CPU的使用权,平均分配每个线程占用CPU的时间片

② 抢占式调度模型 : 优先让优先级高的线程占用CPU,如果优先级相同,那么会随机选择一个,优先级高的线程获取的CPU时间片相对多一些

在JAVA中使用的是抢占式调度模型

(2)优先级相关方法

①获取线程的优先级

int getPriority() : 返回此线程的优先级。

②设置线程的优先级

void setPriority(int newPriority) :更改此线程的优先级。

package com.java.myThread;

/**
 * @Description
 * @Author yc
 * @Version V1.0.0
 * @Date 2021/8/1
 */
public class MyThreadPriority {
    public static void main(String[] args) {
        //创建三个线程
        MyThread myThread01 = new MyThread("线程1");
        MyThread myThread02 = new MyThread("线程2");
        MyThread myThread03 = new MyThread("线程3");

        //获取三个线程的优先级
        System.out.println(myThread01.getPriority());  //5
        System.out.println(myThread02.getPriority());  //5
        System.out.println(myThread02.getPriority());  //5

        //设置三个线程的优先级
        myThread01.setPriority(5);
        myThread02.setPriority(10);
        myThread03.setPriority(1);

        //启动三个线程
        myThread01.start();
        myThread02.start();
        myThread03.start();
    }
}

在设置线程优先级时,需要注意可以设置优先优先级的范围为1-10

/**
 * 优先级最小值
 */
public static final int MIN_PRIORITY = 1;

/**
 * 优先级默认值
 */
public static final int NORM_PRIORITY = 5;

/**
 * 优先级最大值
 */
public static final int MAX_PRIORITY = 10;

4、线程控制

public static void sleep(long millis) throws InterruptedException

使当前正在执行的线程以指定的 毫秒数 暂停(暂时停止执行),具体取决于系统定时器和调度程序的精度和准确性。 线程不会丢失任何显示器的所有权

@Override
public void run() {
    //run()方法中编写多线程想要执行的代码
    for (int i = 0;i < 100; i++){
        System.out.println(getName()+":"+i);
        try {
            //当线程执行到此处时,会睡眠1000毫秒
            sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

public final void join() throws InterruptedException

等待这个线程死亡。

package com.java.myThread;

/**
 * @Description 该类用与演示join方法
 * @Author yc
 * @Version V1.0.0
 * @Date 2021/8/1
 */
public class MyThreadJoin {
    public static void main(String[] args) throws InterruptedException {
        //创建三个线程
        MyThread myThread01 = new MyThread("线程1");
        MyThread myThread02 = new MyThread("线程2");
        MyThread myThread03 = new MyThread("线程3");

        //启动三个线程
        myThread01.start();
        //调用join()方法,调用后,只有线程1执行完后,才能执行其他线程
        myThread01.join();
        myThread02.start();
        myThread03.start();
    }
}

public final void setDaemon(boolean on)

将此线程标记为daemon线程或用户线程。 当运行的唯一线程都是守护进程线程时,Java虚拟机将退出。
线程启动前必须调用此方法。

package com.java.myThread;

/**
 * @Description
 * @Author yc
 * @Version V1.0.0
 * @Date 2021/8/1
 */
public class MyThreadDaemon {
    public static void main(String[] args) {
        //创建线程
        MyThread myThread01 = new MyThread("线程1");
        MyThread myThread02 = new MyThread("线程2");

        //将线程1和线程2设置为守护线程,当主线程跑完,只剩下守护线程时会退出JAVA虚拟机
        myThread01.setDaemon(true);
        myThread02.setDaemon(true);

        //启动线程
        myThread01.start();
        myThread02.start();
    }
}

5、线程的生命周期

在这里插入图片描述

6、卖票案例

(1)判断多线程程序是否存在数据安全问题的标准

① 是否为多线程环境

② 是否存在共享数据

③ 是否存在多条语句对共享数据进行操作

如何解决多线程数据安全问题 : 破坏以上三个条件中的一个即可

如何实现 : 给操作共享数据的代码锁起来,让任意时刻只能有一个线程执行,java提供了同步代码块的方式

(2)同步代码块

锁多条语句操作共享数据,可以通过使用同步代码块实现

格式:

synchronized(任意对象){
	多条语句操作共享数据的代码
}

synchronized(任意对象) : 就相当于给代码加锁了,任意对象就可以看出一把锁

同步的好处和弊端:

好处 : 解决了多线程的数据安全问题

弊端 :当线程很多时,因为每个线程都会去判断同步上的锁,这是很耗费资源的,无形中会降低程序的运行效率

卖票案例代码:

package com.java.caseStudy;

/**
 * @Description
 * @Author yc
 * @Version V1.0.0
 * @Date 2021/8/1
 */
@SuppressWarnings("all")
public class CellTicket implements Runnable {
    //共享资源  总共100张票
    private int tickets = 100;
    private Object object = new Object();

    @Override
    public void run() {
        while (true) {
            //给操作共享资源的语句加上锁 锁为object变量
            synchronized (object) {
                //如果还有票则进行出售
                if (tickets > 0) {
                    try {
                        //模拟买票操作
                        Thread.sleep(100);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println(Thread.currentThread().getName() + "正在买第" + tickets + "张票");
                    //买了一张票,票需要减一
                    tickets--;
                }
            }
        }
    }
}
package com.java.caseStudy;

/**
 * @Description
 * @Author yc
 * @Version V1.0.0
 * @Date 2021/8/1
 */
public class CellTicketTest {
    public static void main(String[] args) {
        CellTicket cellTicket = new CellTicket();
        //创建三个线程,模拟三个窗口
        Thread thread01 = new Thread(cellTicket,"窗口1");
        Thread thread02 = new Thread(cellTicket,"窗口2");
        Thread thread03 = new Thread(cellTicket,"窗口3");
        //启动线程
        thread01.start();
        thread02.start();
        thread03.start();
    }
}
(3)同步方法

同步方法:就是把synchronized关键字加到方法上

格式: 修饰符 synchronized 返回值类型 方法名(方法参数){ }

同步方法的锁对象: this

package com.java.caseStudy;

/**
 * @Description
 * @Author yc
 * @Version V1.0.0
 * @Date 2021/8/1
 */
@SuppressWarnings("all")
public class CellTicket implements Runnable {
    //共享资源  总共100张票
    private int tickets = 100;
    private Object object = new Object();

    @Override
    public void run() {
        while (true) {
            //同步方法处理数据安全问题
            cellTicket();
        }
    }
    
    //同步方法
    private synchronized void cellTicket() {
        if (tickets > 0) {
            try {
                //模拟买票操作
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName() + "正在买第" + tickets + "张票");
            //买了一张票,票需要减一
            tickets--;
        }
    }


}
(4)同步静态方法

同步静态方法:就是把synchronized关键字加到静态方法上

格式 : 修饰符 static synchronized 返回值类型 方法名(方法参数) { }

同步静态方法的锁对象: 类名.class

package com.java.caseStudy;

/**
 * @Description
 * @Author yc
 * @Version V1.0.0
 * @Date 2021/8/1
 */
@SuppressWarnings("all")
public class CellTicket implements Runnable {
    //共享资源  总共100张票
    private static int tickets = 100;
    private Object object = new Object();

    @Override
    public void run() {
        while (true) {
            //同步静态方法处理数据安全问题
            cellTicket();
        }
    }

    //同步静态方法
    private static synchronized void cellTicket() {
        if (tickets > 0) {
            try {
                //模拟买票操作
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName() + "正在买第" + tickets + "张票");
            //买了一张票,票需要减一
            tickets--;
        }
    }


}
(5)Lock锁

虽然我们可以理解同步代码和同步方法的锁对象问题,但是我们并没有直接看到在哪里加上了锁,在哪里释放了锁,为了更加清晰的表达如何加锁和释放锁,JDK5以后提供了一个新的锁对象Lock

Lock实现提供比使用synchronized方法和语句可以获得更广泛的锁定操作

Lock提供了获得锁和释放锁的方法

void lock(): 获得锁

void unlock(): 释放锁

Lock是接口不能直接实例化,这里采用它的实现类ReentrantLock来实例化

ReentrantLock的构造方法 : ReentrantLock()

7、线程安全的类

StringBuffer

① 线程安全,可变的序列

② 从版本5开始,被StringBuilder替代。通常应该使用StringBuilder类,因为它支持所有相同的操作,但它更快,因为它不执行同步

Vector

从Java2平台v1.2开始,该类改进了List接口,使其成为Java Collections Framework的成员。与新的集合实现不同,Vector被同步,如果不需要线程安全的实现,建议使用ArrayList代替Vector

Hashtable

① 该类实现了一个哈希表,它将键映射到值。任何非null对象都可以用作键或者值

② 从Java2平台v1.2开始,该类进行了改进,实现了Map接口,使其成为Java Collections Framework的成员。与新的集合实现不同,Hashtable被同步,如果不需要线程安全的实现,建议使用HashMap代替Hashtable

如果想使用线程安全的集合,除了VectorHashtable外,还可以使用CollectionssynchronizedCollection(Collection<T> c)synchronizedList(List<T> list)等方法

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值