多线程

多线程

1.多线程是指从软件或者硬件上实现多个线程并发执行的技术.
具有多线程能力的计算机因由硬件支持而能够在同一时间执行多个线程,提升性能.
2.并行: 在同一时刻,有多个指令在多个CPU上 同时执行;
并发:在同一时刻,有多个指令在单个CPU 上交替执行.
3.进程: 指正在运行的程序;
独立性:进程是一个能独立运行的基本单位,同时也是系统分配资源和调度的独立单位;
动态性:进程的实质是程序的一次执行过程,进程是动态产生,动态消亡的;
并发性:任何进程都可以同其他进程一起并发执行.
4.线程:是进程中的单个顺序控制流,是一条执行路径;
单线程:一个进程如果只有一条执行路径,则称为单线程程序;
多线程:一个进程如果有多条执行路径,则称为多线程程序.
5.实现多线程方式:继承Tread类;实现Runnable接口;实现Callable接口;
一、继承Tread类
方法介绍
方法名 说明
void run() 在线程开启后,此方法将被调用执行
void start() 使此线程开始执行,Java虚拟机会调用run方法()
实现步骤:
定义一个类MyThread继承Thread类;
在MyThread类中重写run()方法;
创建MyThread类的对象;
启动线程。
代码演示:
public class MyThread extends Thread {
@Override
public void run() {
for (int i = 0; i < 100; i++) {
System.out.println(i);
}
}
}
----------------------------------------------------------------
public static void main(String[] args) {
MyThread my1 = new MyThread();
MyThread my2 = new MyThread();

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

    my1.start();
    my2.start();

}          

注意: 重写run()方法 是因为、 run()是用来封装被线程执行的代码;
run()方法和start()方法的区别:
run():封装线程执行的代码,直接调用,相当于普通方法的调用;
start():启动线程,然后由JVM调用此线程的run()方法。
二、实现Runnable接口
Thread构造方法
方法名 说明
Thread(Runnable target) 分配一个新的Thread对象
Thread(Runnable target, String name) 分配一个新的Thread对象
实现步骤:
定义一个类MyRunnable实现Runnable接口;
在MyRunnable类中重写run()方法;
创建MyRunnable类的对象;
创建Thread类的对象,把MyRunnable对象作为构造方法的参数;
启动线程。
代码演示:
public class MyRunnable implements Runnable {
@Override
public void run() {
for (int i = 0; i < 100; i++) {
System.out.println(Thread.currentThread().getName() + “执行了” + i +“次”);
}
}
}

 public static void main(String[] args) {
    //创建MyRunnable类的对象
 MyRunnable my = new MyRunnable();
    //创建Thread类的对象,把MyRunnable对象作为构造方法的参数
    //Thread(Runnable target)
   // Thread t1 = new Thread(my);
  //  Thread t2 = new Thread(my);
  //  Thread(Runnable target,String name)
      Thread t1 = new Thread(my,"舒克");
      Thread t2 = new Thread(my,"贝塔");

    t1.start();
    t2.start();
}

三、实现Callable接口
方法介绍:
方法名 说明
V call() 计算结果,如果无法计算结果,则抛出一个异常
FutureTask(Callable callable) 创建一个 FutureTask,一旦运行就执行给定的 Callable
V get() 如有必要,等待计算完成,然后获取其结果
实现步骤:
定义一个类MyCallable实现Callable接口;
在MyCallable类中重写call()方法;
创建MyCallable类的对象;
创建Future的实现类FutureTask对象,把MyCallable对象作为构造方法的参数;
创建Thread类的对象,把FutureTask对象作为构造方法的参数;
启动线程;
再调用get方法,就可以获取线程结束之后的结果。
代码演示:
public class MyCallable1 implements Callable {
@Override
public String call() throws Exception {
for (int i = 0; i < 100; i++) {
System.out.println(Thread.currentThread().getName() + “执行了” + i + “次”);
}
return “执行完毕”;
}
}
-----------------------------------------------------------------------------------------------------------
public class Test002 {
public static void main(String[] args) throws ExecutionException, InterruptedException {
MyCallable mc1 = new MyCallable();
FutureTask ft1 = new FutureTask<>(mc1);
Thread t1 = new Thread(ft1);
t1.setName(“天籁”);
System.out.println(t1.getPriority());
t1.start();
System.out.println(ft1.get());

    MyCallable mc2 = new MyCallable();
    FutureTask<String> ft2 = new FutureTask<>(mc2);
    Thread t2 = new Thread(ft2);
    t2.setName("云库");
    System.out.println(t2.getPriority());
    t2.start();
    System.out.println(ft2.get());
}

}

三种实现方式的对比:
实现Runnable、Callable接口: 好处:扩展性强,实现该接口的同时还可以继承其他的类;
缺点:编程相对复杂,不能直接使用Thread类中的方法。
继承Thread类: 好处: 编程比较简单,可以直接使用Thread类中的方法;
缺点: 可以扩展性较差,不能再继承其他的类。
四、设置和获取线程名称
方法介绍:
方法名 说明
void setName(String name) 将此线程的名称更改为等于参数name
String getName() 返回此线程的名称
Thread currentThread() 返回对当前正在执行的线程对象的引用
代码演示:
public class MyThread extends Thread {
public MyThread(){}
public MyThread(String name){
super(name);
}
public void run(){
for (int i = 0; i < 100; i++) {
System.out.println(Thread.currentThread().getName()+ “:” + i);
}
}
}
-----------------------------------------------------------------------------------------------------------
public static void main(String[] args) {
MyThread my1 = new MyThread();
MyThread my2 = new MyThread();
my1.setName(“高铁”);
my2.setName(“飞机”);
// MyThread my1 = new MyThread(“飞机”);
// MyThread my2 = new MyThread(“高铁”);
// String name = Thread.currentThread().getName();
// System.out.println(name);
// Thread.currentThread().setName(“sdjl”);
// String name1 = Thread.currentThread().getName();
// System.out.println(name1);
my1.start();
my2.start();
}
5.线程休眠
相关方法:
方法名 说明
static void sleep(long millis) 使当前正在执行的线程停留(暂停执行)指定的毫秒数
代码演示:
public class MyRunnable implements Runnable {

@Override
public void run() {
    for (int i = 0; i < 100; i++) {
        try {
            Thread.sleep(100);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(Thread.currentThread().getName() + "---" + i);
    }
}

}

public static void main(String[] args) {
/* System.out.println(“睡觉前”);
Thread.sleep(3000);
System.out.println(“睡醒了”);
*/
MyRunnable mr = new MyRunnable();
Thread t1 = new Thread(mr);
Thread t2 = new Thread(mr);
t1.start();
t2.start();
}
6.线程优先级
线程调度:
分时调度模型:所有线程轮流使用CPU 的使用权,平均分配每个线程占用CPU的时间片;
抢占式调度模型:优先让优先级高的线程使用CPU,如果线程的优先级相同,那么会随机选择一个,优先级高的线程获取
的CPU 时间片相对多一些
Java使用的是抢占式调度模型
随机性:假如计算机只有一个CPU,那么CPU在某一时刻只能执行一条指令,线程只有得到CPU时间片,也就是使用权,才可
以执行指令;所以说多线程程序的执行是有随机性,因为谁抢到CPU的使用权是不一定的。
优先级相关方法:
方法名 说明
final int getPriority() 返回此线程的优先级
final void setPriority(int newPriority) 更改此线程的优先级线程默认优先级是5;线程优先级的范围是:1-10

代码演示:
public class MyCallable implements Callable {
@Override
public String call() throws Exception {
for (int i = 0; i < 100; i++) {
System.out.println(Thread.currentThread().getName() + “执行了” + i + “次” );
}
return “线程执行完毕了”;
}
}
------------------------------------------------------------------------------------------------------------------------------------------
public static void main(String[] args) {
MyCallable mc = new MyCallable();
FutureTask ft = new FutureTask<>(mc);
Thread t1 = new Thread(ft);
t1.setName(“飞机”);
t1.setPriority(10);
//System.out.println(t1.getPriority());
t1.start();
MyCallable mc2 = new MyCallable();
FutureTask ft2 = new FutureTask<>(mc2);
Thread t2 = new Thread(ft2);
t2.setName(“坦克”);
t2.setPriority(1);
// System.out.println(t2.getPriority());
t2.start();
}
7.守护线程
相关方法:
方法名 说明
void setDaemon(boolean on) 将此线程标记为守护线程,当运行的线程都是守护线程时,Java虚拟机将退出
代码演示:
public class MyThread1 extends Thread{
@Override
public void run() {
for (int i = 0; i < 10; i++) {
System.out.println(getName() + “----” + i);
}
}
}

public class MyThread2 extends Thread {
@Override
public void run() {
for (int i = 0; i < 100; i++) {
System.out.println(getName() + “—” + i);
}
}
}

public static void main(String[] args) {
MyThread1 t1 = new MyThread1();
MyThread1 t2 = new MyThread1();
t1.setName(“舒克”);
t2.setName(“贝塔”);
t2.setDaemon(true);
t1.start();
t2.start();
}
8.线程同步
数据安全问题–同步代码块
安全问题出现的条件: 是多线程环境;有共享数据;有多条语句操作共享数据。
解决多线程安全问题的基本思想: 让程序没有安全问题的环境。
实现解决多线程安全问题: 把多条语句操作共享数据的代码给锁起来,让任意时刻只有一个线程执行即可;
Java提供了同步代码块的方式来解决。
同步代码块格式:
synchronized(任意对象) {
多条语句操作共享数据的代码
}
synchronized(任意对象):就相当于给代码加锁了,任意对象就可以看成是一把锁;
同步的好处和弊端:
好处:解决了多线程的数据安全问题;
弊端:当线程很多时,因为每个线程都会去判断同步上的锁,这是很耗费资源的,无形中会降低程序的运行效率。
代码演示:
public class SellTicket implements Runnable {
private int ticket = 100;
private Object obj = new Object();
@Override
public void run() {
while (true){
synchronized (obj){// 对可能有安全问题的代码加锁,多个线程必须使用同一把锁
//t1进来后,就会把这段代码给锁起来
if (ticket > 0){
try {
Thread.sleep(100);
//t1休息100毫秒
} catch (InterruptedException e) {
e.printStackTrace();
}
//窗口1正在出售第100张票
System.out.println(Thread.currentThread().getName() + “正在出售第” + ticket + “张票”);
ticket --;
}
}
//t1出来了,这段代码的锁就被释放了
}
}
}
----------------------------------------------------------------------------------------------------------------------------
public static void main(String[] args) {
SellTicket st = new SellTicket();
Thread t1 = new Thread(st,“窗口1”);
Thread t2 = new Thread(st,“窗口2”);
Thread t3 = new Thread(st,“窗口3”);
t1.start();
t2.start();
t3.start();
}
-------------------------------------------------------------------------------------------------------------------------------
9.数据安全问题—锁对象唯一
锁对象的注意事项: 锁对象可以是任意对象; 多个线程想要保证安全,必须使用同一把锁对象。
代码演示:
public class SellTicket implements Runnable {
private static int ticket = 100;
private static Object lock = new Object();
@Override
public void run() {
while (true){
synchronized (lock){// 对可能有安全问题的代码加锁,多个线程必须使用同一把锁
//t1进来后,就会把这段代码给锁起来
if (ticket > 0){
ticket --;
try {
Thread.sleep(100);
//t1休息100毫秒
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + “正在出售第” + ticket + “张票”);
}
}
}
}
}

public static void main(String[] args) {
MyThread ticket1 = new MyThread();
MyThread ticket2 = new MyThread();
ticket1.setName(“窗口1”);
ticket2.setName(“窗口2”);
ticket1.start();
ticket2.start();
}
10.同步方法解决数据安全问题
同步方法的格式(同步方法就是把synchronized关键字加到方法上):
修饰符 synchronized 返回值类型 方法名(方法参数) {
方法体;
}
同步方法的锁对象是:this。
静态同步方法(同步静态方法就是把synchronized关键字加到静态方法上):
修饰符 static synchronized 返回值类型 方法名(方法参数) {
方法体;
}
同步静态方法的锁对象是:类名.class。
11.Lock锁
能清晰的表达如何加锁和释放锁,JDK5以后提供了一个新的锁对象Lock;
Lock实现提供比使用synchronized方法和语句可以获得更广泛的锁定操作;
Lock是接口不能直接实例化,这里采用它的实现类ReentrantLock来实例化。
ReentrantLock构造方法
方法名 说明
ReentrantLock() 创建一个ReentrantLock的实例
加锁解锁方法
方法名 说明
void lock() 获得锁
void unlock() 释放锁
代码演示:
public class SellTicket implements Runnable {
private int tickets = 100;
Object ob = new Object();
private ReentrantLock lock = new ReentrantLock();

@Override
public void run() {
    while (true) {
        try {
            lock.lock();
            if (tickets <= 0) {
                break;
            }
                Thread.sleep(100);
                tickets--;
                System.out.println(Thread.currentThread().getName() + "正在出售第" + tickets + "张票");
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }
}

}

public class Test06 {
public static void main(String[] args) {
SellTicket st = new SellTicket();

  Thread t1 = new Thread(st);
  Thread t2 = new Thread(st);
  Thread t3 = new Thread(st);

  t1.setName("窗口一");
  t2.setName("窗口二");
  t3.setName("窗口三");

  t1.start();
  t2.start();
  t3.start();

}

}
12.死锁
线程死锁是指由于两个或者多个线程互相持有对方所需要的资源,导致这些线程处于等待状态,无法前往执行。
产生死锁的情况: 资源优先; 同步嵌套。
代码演示:
public static void main(String[] args) {
Object objA = new Object();
Object objB = new Object();

    new Thread(()->{
        while (true){
            synchronized (objA){
                synchronized (objB){
                    System.out.println("123");
                }
            }
        }
    }).start();
    new Thread(()->{
        while (true){
            synchronized (objB){
                synchronized (objA){
                    System.out.println("456");
                }
            }
        }
    }).start();
}

13.阻塞队列基本应用
常见BlockingQueue:
ArrayBlockingQueue: 底层是数组,有界
LinkedBlockingQueue: 底层是链表,无界.但不是真正的无界,最大为int的最大值
BlockingQueue的核心方法:
put(anObject): 将参数放入队列,如果放不进去会阻塞
take(): 取出第一个数据,取不到会阻塞

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值