美文网首页
多线程(一)

多线程(一)

作者: 寂静的春天1988 | 来源:发表于2019-01-15 23:25 被阅读0次

进程:正在运行的程序,是系统进行资源分配和调度的独立单位。每一个进程都有它的内存空间和系统资源。
线程:在一个进程内可以执行多个任务。每一个任务就可以看成一个线程。线程是进程的执行单元。是程序使用CPU的最小单位。

一)使用thread类
1、继承thread类
2、重写run方法
3、启动多线程:new对象,调用它的start()方法(不能调用run方法,否则只是当作普通方法调用)
4、start方法只能被调用一次

public class MyThread extends Thread{

    @Override
    public void run() {
        for (int i = 0; i < 10; i++) {
            System.out.println(i);
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}
public static void main(String[] args) throws Exception {

    MyThread thread1=new MyThread();
    thread1.start();
}

二)使用runnable
1、实现Runnable接口
2、重写run方法
3、启动多线程:
3.1创建实现runnable接口的类
3.2创建thread类。把实现runnable接口的类当作形参放入tread类
3.3然后用thread类.start()方法!

public class MyRunable implements Runnable{

    @Override
    public void run() {
        for (int i = 0; i < 10; i++) {
            System.out.println(i+"---"+Thread.currentThread().getName());
        }
    }
    public static void main(String[] args) {
        MyRunable runbable1=new MyRunable();
        Thread thread1=new Thread(runbable1);
        Thread thread2=new Thread(runbable1);
        thread1.start();
        thread2.start();
    }

}

三)使用Callable<V>接口
1、实现Callable接口
Callable的特点:允许有返回值,依赖于依赖与线程池实现(在多线程二的文章中演示)

推荐使用runnable的方式:
1、避免单继承的局限性
2、便于多个线程共享资源!

线程调度模型有两种:
1)分时调度模型,所有线程轮流使用CPU的使用权,平均分配每个线程占用CPU的时间片。
2)抢占式调度模型,优先让优先级高的线程使用CPU,如果线程的优先级相同,那么随机选择一个。优先级高的线程,使用CPU的时间片会多一些。
java中采用的就是抢占式调度模型。

常用方法:
getName():得到线程名称。
setName():设置线程名称。
currentThread():返回当前正在执行的线程引用。
getPriority():获取线程优先级。(默认优先级是5)
setPriority():设置线程优先级。(必须在1-10范围内,包含)
Thread.sleep():线程睡眠
join():等待这个线程死亡。 (只有它执行完了,其他线程才能走。)
yield():线程礼让。(让出cpu的使用权,重新抢占cpu,所有它本身还是可能抢占到cpu的)
setDaemon():后台线程。 将此线程标记为daemon线程或用户线程。 当运行的唯一线程都是守护进程线程时,Java虚拟机将退出。线程启动前必须调用此方法。

stop():线程中止(已过时)
interrupt():线程中止。

stop和interrupt的区别
stop


image.png

interrupt


image.png

interrupt:把线程状态中止,并抛出一个InterruptedException
stop()方法在现在JDK中不推荐使用,原因是stop()方法过于暴力,强行把执行到一半的线程终止,可能会引起一些数据不一致的问题。因此在使用stop()方法时需要自行决定线程何时退出!
总结:stop()方法执行后,该线程就停止了,不再继续执行了,但是interrupt()方法执行后,它会终止线程的状态,还会继续执行run方法里面的代码
通常不要用interrupt或者stop方法来中断线程,一般用条件判断结束线程方法。如下图:


image.png

线程的生命周期:
新建:创建线程对象
就绪:该线程有执行资格,但是没有执行权
运行:有执行资格,有执行权。
阻塞:由于一些操作让线程处于了该状态,没有执行资格,没有执行权。而另一些操作可以把它激活,激活后处于就绪状态。
死亡:线程对象变成垃圾,等待被回收。

image.png

经典线程安全问题
需求1:三个窗口同时卖100张电影票。

public class MyRunable implements Runnable {
    int p = 100;

    @Override
    public void run() {
        while (true) {
            if (p > 0) {
                try {
                    // 模拟延时
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                p--;
                System.out.println("卖了" + (100 - p) + "张票,还剩" + p + "张" + "----" + Thread.currentThread().getName());
            } else {
                break;
            }

        }
    }

    public static void main(String[] args) {
        MyRunable runbable1 = new MyRunable();
        Thread thread1 = new Thread(runbable1);
        Thread thread2 = new Thread(runbable1);
        Thread thread3 = new Thread(runbable1);
        thread1.start();
        thread2.start();
        thread3.start();
    }

}

运行上面的代码会发现,会发现重复卖同一张票和超卖的问题。

导致线程安全问题的原因
A:是否是多线程环境
B:是否有共享数据
C:是否有多条语句操作共享数据

一般而言A,B我们都不能去改变。可以改C。将多条语句操作包装成一个整体,只能同时有一个线程执行,在它运行完之前(释放锁之前)不能被其他线程抢占。(原子性)

1、使用同步代码块

    @Override
    public void run() {
        while (true) {
            synchronized (this) {
                if (p > 0) {
                    try {
                        // 模拟延时
                        Thread.sleep(100);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    p--;
                    System.out.println("卖了" + (100 - p) + "张票,还剩" + p + "张" + "----" + Thread.currentThread().getName());
                } else {
                    break;
                }
            }

        }
    }

2、使用同步方法

public class MyRunable implements Runnable {
    int p = 100;
    int x = 0;
    private Object obj=new Object();
    
    
    @Override
    public void run() {
        while (true) {
            if(x%2==0) {
                synchronized (obj) {
                    if (p > 0) {
                        try {
                            // 模拟延时
                            Thread.sleep(100);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                        p--;
                        System.out.println("卖了" + (100 - p) + "张票,还剩" + p + "张" + "----" + Thread.currentThread().getName());
                    }
                }
                
            }else {
                sell();
            }
            x++;
        }
    }
    
    private synchronized void sell() {
        if (p > 0) {
            try {
                // 模拟延时
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            p--;
            System.out.println("卖了" + (100 - p) + "张票,还剩" + p + "张" + "----" + Thread.currentThread().getName());
        }
    }

这里我们将代码改造了一下,x%2==0的时候走同步代码块,否则走同步方法。同时将同步代码块的锁对象换成了成员变量obj,
会发现上面的代码出现了线程安全问题。因为这里同步代码块的锁对象和同步方法的锁对象不一致了。这里同步代码块的锁对象应该换成this,就不会出现线程安全问题了。
这也表明了同步方法的锁对象时this。
同步静态方法的锁对象呢?是类的class文件(MyRunable.class)

总结:
同步代码块的锁对象:形参。
同步方法的锁对象:this
静态同步方法的锁对象:类的class文件。
如果锁对象是this可以使用同步方法,一般而言使用同步代码块。

同步可以解决安全问题的原因在那个形参上面。那个形参就相当于一把锁,多个线程要使用同一把锁。(这里使用this因为只new了一个MyRunable对象。)

注意事项:同步代码块的锁对象可以是任意对象,但是多个线程必须是同一个锁对象。
好处:同步解决了多线程的安全问题。
坏处:降低了程序的效率。

提示:Collections中有方法可以将线程不安全的集合类,转换成线程安全类的方法。synchronizedList()、synchronizedMap()...

相关文章

  • 多线程介绍

    一、进程与线程 进程介绍 线程介绍 线程的串行 二、多线程 多线程介绍 多线程原理 多线程的优缺点 多线程优点: ...

  • 多线程编程

    多线程编程之Linux环境下的多线程(一)多线程编程之Linux环境下的多线程(二)多线程编程之Linux环境下的...

  • 多线程

    创建一个多线程 创建多线程-继承线程类 创建多线程-实现Runnable接口 创建多线程-匿名类code

  • 带你搞懂Java多线程(五)

    带你搞懂Java多线程(一)带你搞懂Java多线程(二)带你搞懂Java多线程(三)带你搞懂Java多线程(四) ...

  • iOS多线程 NSOperation

    系列文章: 多线程 多线程 pthread、NSThread 多线程 GCD 多线程 NSOperation 多线...

  • iOS多线程 pthread、NSThread

    系列文章: 多线程 多线程 pthread、NSThread 多线程 GCD 多线程 NSOperation 多线...

  • iOS多线程: GCD

    系列文章: 多线程 多线程 pthread、NSThread 多线程 GCD 多线程 NSOperation 多线...

  • iOS多线程运用

    系列文章: 多线程 多线程 pthread、NSThread 多线程 GCD 多线程 NSOperation 多线...

  • iOS多线程基础

    系列文章: 多线程 多线程 pthread、NSThread 多线程 GCD 多线程 NSOperation 多线...

  • iOS 多线程简介

    一.本文介绍点 1.为什么要学习多线程2.什么是多线程3.多线程的原理4.多线程的优缺点5.多线程的应用6.多线程...

网友评论

      本文标题:多线程(一)

      本文链接:https://www.haomeiwen.com/subject/fdnudqtx.html