Java 多线程的方法 实现 休眠 加入 礼让 守护线程 线程中断 线程安全 Lock锁

22.01 多线程的实现方式1

  A:多线程的实现方式1:
  		a:继承Thread类
  		b:步骤及代码演示
  		c:几个小问题:
  			启动线程使用的是哪个方法
  			线程能不能多次启动
  			run()和start()方法的区别

		我们启动线程不是使用run方法,而应该是start方法,使该线程开始执行;
		Java虚拟机调用该线程的 run 方法

		为什么要重写run方法?
			这个类是一个线程类,那么在这个类中我们能不能写一些其他方法呢?
				可以写其他方法
			其他方法中封装的代码都是需要被我们线程执行的吗?
				不一定,也就是run方法中封装应该是必须被线程执行的代码
			run方法中的代码的书写原则:一般是比较耗时的代码
/*
    我们要学习线程,先要知道一下进程,因为线程要依赖于进程。
    什么是进程?正在执行的应用程序。
    什么是线程?正在执行的应用程序,可能要执行很多的任务,那每一个任务我
    们就叫做线程。

    ***进程是拥有资源的基本单位,线程是CPU调度的基本单位***

    比如,打开一个word软件,一个任务是在进行文字的录入,一个任务是在进行
    自动保存。这两个任务,你可以看作是两条线程。线程要依赖于进程,一个进
    程里面至少包含一个线程。
    我们现在的计算机,是可以执行多个进程的,也就是说你可以打开多个应用程
    序。那么在某一个时刻,我们单核CPU是同时在执行多个进程吗?
    我们的单核CPU在某一个时间点上,只能执行一个进程。因为CPU会在多个进程
    间,进行高速的切换,所以我们感觉是同时进行的

    多进程的意义:提供CPU的利用率
    我们的一个进程里面可以包含多个线程,多个线程是同时执行的吗? ***Java
    中多个线程是并发执行。***

    并发:多个任务高速的交替执行,在某一个时间点上只能执行一个任务,但是
    	 由于多个任务交替执行的切换的比较快,感觉是同时执行,其实就类似于
    	 Java中的多线程,多个任务高速的交替执行
    并行:是真正意义上的多个任务同时执行
*/

public class MyTest {
    public static void main(String[] args) {
        /*
            Thread 线程,是程序中的执行线程。Java虚拟机允许应用程序并发
            地运行多个线程
            我们可以使用Java提供的这个Thread类,来创建子线程。

            创建一个线程的常见方式:
            方式1:
                1.定义一个类,继承Thread类
                2.重写Thread类中的run方法,因为run方法里面的代码是让线
                  程来执行的。
                3.创建自己定义的这个类的对象
                4.调用start()方法来开启线程
         */

        MyThread mt = new MyThread();

        //开启线程
        //mt.run();   //这个不是开启线程的方法,就是普通的创建对象,调用
        			  //方法
        //mt.show();

        //正确开启线程的方法是,调用start()方法来开启
        //多次启动一个线程是非法的,特别是当线程已经结束执行后,不能再重
        //新启动
        mt.start();
        //mt.start(); //IllegalThreadStateException 不要重复开启同一个
        			  //线程

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

public class MyThread extends Thread{
    @Override
    public void run() {
        //为什么要重写run方法,是因为子线程开启后,run方法里面的代码是由
        //子线程来执行的
        //一般耗时的操作代码,就可以放到run方法里面,让线程来执行

        for (int i = 0; i < 100; i++) {
            System.out.println(i);
        }
    }

    public void show(){
        System.out.println("show方法");
    }
}

22.02 获取和设置线程对象名称

  A:Thread类的基本获取和设置方法
  		public final String getName()			//获取线程名称
  		public final void setName(String name)	//设置线程名称
  		public static Thread currentThread()	//获取当前执行的线程
  		其实通过构造方法也可以给线程起名字
public static void main(String[] args) {

        //获取主线程名称
        Thread thread = Thread.currentThread(); //获取当前正在执行的线
        										//程对象
        thread.setName("主线程");
        String name = thread.getName();
        System.out.println(name);

        //Java中使用Thread创建线程
        MyThread mt = new MyThread();

        //给线程设置名字
        mt.setName("lcy");
        mt.start(); //开启线程
    }
    
public class MyThread extends Thread{
    @Override
    public void run() {
        //this 代表线程对象
        System.out.println(this);

        //run方法里面写的是需要线程来执行的代码,一般放的就是耗时代码
        for (int i = 0; i < 100; i++) {
            System.out.println(this.getName() + "==" + i);
        }
    }
}

22.03 线程调度及获取和设置线程优先级

  A:如何设置和获取线程优先级
  		public final int getPriority()	//获取线程的优先级
  		public final void setPriority(int newPriority)	//设置线程的优
  														//先级
  		public final int getPriority()	//返回线程的优先级
  		线程的默认优先级是5
  		给线程设置优先级:范围是1-10
  			public final void setPriority(int newPriority)
public class MyTest {
    public static void main(String[] args) {
        MyThread mt1 = new MyThread();
        MyThread mt2 = new MyThread();
        mt1.setName("线程A");
        mt2.setName("线程B");

        //设置线程优先级
        mt1.setPriority(Thread.MIN_PRIORITY);   //最低为1
        mt2.setPriority(Thread.MAX_PRIORITY);   //最高为10

        //线程优先级是1-10,默认是5
        int priority = mt1.getPriority();
        int priority1 = mt2.getPriority();
        System.out.println(priority);
        System.out.println(priority1);

        mt1.start();
        mt2.start();
    }
}

public class MyThread extends Thread{
    @Override
    public void run() {
        for (int i = 0; i < 100; i++) {
            System.out.println(this.getName() + "===" + i);
        }
    }
}

22.04 休眠线程

  A:线程休眠:
  		public static void sleep(long millis)	//线程休眠
public class MyTest {
    public static void main(String[] args) throws InterruptedException {

        //展示广告页面 60秒
        System.out.println("广告页面");
        //让当前线程休眠
        Thread.sleep(1000*60);

        //跳到主页面
        System.out.println("跳到主页面");
        MyThread mt1 = new MyThread();
        mt1.setName("线程A");
        mt1.start();
    }
}

public class MyThread extends Thread{
    @Override
    public void run() {
        //this.sleep();
        try {
            Thread.sleep(1000*3);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        for (int i = 0; i < 100; i++) {
            System.out.println(this.getName() + "===" + i);
        }
    }
}

22.05 加入线程

  A:加入线程
  		public final void join()
  		等待该线程执行完毕了以后,其他线程才能在此执行
  		注意事项:在线程启动之后,在调用方法
public class MyTest {
    public static void main(String[] args) throws InterruptedException {

        //三个线程并发执行,现在想让三个线程按顺序执行(串行)
        MyThread mt0 = new MyThread();
        MyThread mt1 = new MyThread();
        MyThread mt2 = new MyThread();
        mt0.setName("刘备");
        mt1.setName("关羽");
        mt2.setName("张飞");

        //设置线程的加入,可以让多个线程从并发执行,变为顺序执行
        mt0.start();
        mt0.join();
        mt1.start();
        mt1.join();
        mt2.start();
        mt2.join();
    }
}

public class MyThread extends Thread{
    @Override
    public void run() {
        for (int i = 0; i < 100; i++) {
            System.out.println(this.getName() + "===" + i);
        }
    }
}

22.06 礼让线程

  A:礼让线程:
  		public static void yield()	//暂停当前正在执行的线程对象,并执
  									//行其他线程
public class MyTest {
    public static void main(String[] args) throws InterruptedException {

        MyThread mt1 = new MyThread();
        MyThread mt2 = new MyThread();

        mt1.setName("关羽");
        mt2.setName("张飞");

        mt1.start();
        mt2.start();
    }
}

public class MyThread extends Thread{
    @Override
    public void run() {
        for (int i = 0; i < 1000; i++) {
            Thread.yield(); //线程礼让
            System.out.println(this.getName() + "===" + i);
        }
    }
}

22.07 守护线程

  A:守护线程
  		public final void setDaemon(boolean on)
  		将该线程标记为守护线程或用户线程。当正在运行的线程都是守护线程时,
  		Java虚拟机退出。
  		该方法必须在启动线程前调用。

  B:Java用户线程和守护线程
  		1.用户线程和守护线程的区别
  			用户线程和守护线程都是线程,区别是Java虚拟机在所有用户线程
  			dead后,程序就会结束。而不管是否还有守护线程还在运行,若守护
  			线程还在运行,则会马上结束。

  		2.用户线程和守护线程的适用场景
  			由两者的区别及dead时间点可知,守护线程不适合用于输入输出或计
  			算等操作,因为用户线程执行完毕,程序就dead了,适用于辅助用户
  			线程的场景,如JVM的垃圾回收,内存管理都是守护线程。

  		3.创建守护线程
  			调用线程对象的方法setDaemon(true),设置线程为守护线程
  				1)thread.setDaemon(true)必须在thread.start()之前设置
  				2)在Daemon线程中产生的新线程也是Daemon的
  				3)不是所有的应用都可以分配给Daemon线程来进行服务,比如
  				   读写操作或者计算逻辑。因为Daemon Thread还没来得及进行
  				   操作,虚拟机可能已经退出了。
public class MyTest {
    public static void main(String[] args) throws InterruptedException {
        //主线程:相当于用户线程
        //守护线程:当用户线程执行完了,守护线程就要死亡掉
        Thread.currentThread().setName("刘备");

        MyThread mt1 = new MyThread();
        MyThread mt2 = new MyThread();
        mt1.setName("关羽");
        mt2.setName("张飞");
        mt1.setDaemon(true);
        mt2.setDaemon(true);
        mt1.start();
        mt2.start();

        for (int i = 0; i < 100; i++) {
            System.out.println("主线程" + i);
        }
    }
}

public class MyThread extends Thread{
    @Override
    public void run() {
        for (int i = 0; i < 1000; i++) {
            System.out.println(this.getName() + "===" + i);
        }
    }
}

22.08 中断线程

  A:中断线程
  		public final void stop();	停止线程的运行
  		public void interrupt();	当线程调用wait(),sleep(long time)
  									方法的时候处于阻塞状态,可以通过这
  									个方法清除阻塞
public class MyTest {
    public static void main(String[] args) throws InterruptedException {

        MyThread mt1 = new MyThread();
        mt1.setName("线程A");
        mt1.start();
        Thread.sleep(100);
        //停止线程,现在不推荐使用
        mt1.stop();
    }
}

public class MyThread extends Thread{
    @Override
    public void run() {
        for (int i = 0; i < 100000; i++) {
            System.out.println(this.getName() + "===" + i);
        }
    }
}

public class MyTest {
    public static void main(String[] args) throws InterruptedException {

        MyThread mt1 = new MyThread();
        mt1.setName("线程A");
        mt1.start();
        Thread.sleep(2000);
        //打断子线程的阻塞状态,比如线程正处于休眠状态,那么这个方法就可
        //以让它不要再休眠
        mt1.interrupt();
    }
}

public class MyThread extends Thread{
    @Override
    public void run() {
        //线程休眠,其实让线程处于了一种阻塞的状态
        try {
            Thread.sleep(1000*10);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        for (int i = 0; i < 100; i++) {
            System.out.println(this.getName() + "===" + i);
        }
    }
}

22.09 多线程的实现方式2

  A:实现Runnable接口
  		这种方式拓展性强,实现一个接口,还可以再去继承其他类
public class MyTest {
    public static void main(String[] args) {
        //创建一个任务
        MyRunnable mr = new MyRunnable();
        //创建一个线程对象,把任务传进来
        Thread th = new Thread(mr);
        //开启线程,线程就会执行任务中的run方法
        th.start();
    }
}

public class MyRunnable implements Runnable{
    @Override
    public void run() {
        for (int i = 0; i < 100; i++) {
            System.out.println(i);
        }
    }
}

22.10 多线程的实现方式3

  A:实现Callable接口。
  		相较于实现Runnable接口的方式,方法可以有返回值,并且可以抛出异常。
  B:执行Callable方式,需要FutureTask实现类的支持,用于接收运算结果。
  	 FutureTask是Future接口的实现类
  C:实现步骤
  		1.创建一个类实现Callable接口
  		2.创建一个FutureTask类将Callable接口的子类对象作为参数传进去
  		3.创建Thread类,将FutureTask对象作为参数传进去
  		4.开启线程
public class MyTest {
    public static void main(String[] args) throws ExecutionException, InterruptedException {

        //创建任务
        MyCallable mc = new MyCallable();
        //可取消的异步计算,利用开始和取消计算的方法,查询计算是否完成的
        //方法和获取计算结果的方法,此类提供了对 Future 的基本实现。
        //仅在计算完成时才能获取结果;如果计算尚未完成,则阻塞 get 方法。
        //一旦计算完成,就不能再重新开始或取消计算。
        //可使用 FutureTask 包装 Callable 或 Runnable 对象
        FutureTask<Object> oft = new FutureTask<>(mc);

        Thread th = new Thread(oft);
        th.setName("线程A");
        th.start();

        //获取子线程执行完之后,返回的结果
        Object o = oft.get();
        System.out.println(o);
    }
}

public class MyCallable implements Callable<Object> {
    @Override
    public Object call() throws Exception {
        for (int i = 0; i < 100; i++) {
            System.out.println(Thread.currentThread().getName() + "===" +i);
        }
        return 100;
    }
}

22.11 线程安全问题的产生原因分析

  A:判断一个多线程应用程序是否有问题的标准
  		a:是否是多线程环境
  		b:是否存在共享数据
  		c:是否存在多条语句同时操作共享数据

  B:解决方法
  		需要使用同步代码块
  		格式:
  			synchronized(对象){	//不能在括号里直接new对象
				要被同步的代码;
			}

  		这个同步代码块保证数据的安全性的一个主要因素就是这个对象,这个对
  		象要定义为 静态成员变量 才能被所有线程共享

	 	同步代码块的锁对象:任意一个对象
	 	同步方法的锁对象:this
	 	静态同步方法的锁对象:当前类对应的字节码文件对象
public static void main(String[] args) {

        /*
            出现线程安全问题,要符合几个条件。
              1.是不是多线程环境
              2.多个线程有没有去操作共享资源
              3.是不是有多条语句在操作这个共享资源,也就是对共享资源是不
                是一个原子性的操作
         */

        /*
            我们模拟了一下,网络售票延迟,就出现了一些不合理的数据,也就
            是说,出现了线程安全问题

            1.出现了0票、负数票,这是由于线程的随机性导致,并发导致的。

            2.出现了重复票,原因是由于,原子性所导致的,原子性:不可再分
              割性
                piao-- 不是一个原子性的操作

                piao-- 要经过三个步骤 读  改  写
         */

        CellRunnable cr = new CellRunnable();
        Thread th1 = new Thread(cr, "窗口1");
        Thread th2 = new Thread(cr, "窗口2");
        Thread th3 = new Thread(cr, "窗口3");

        th1.start();
        th2.start();
        th3.start();
    }

public class CellRunnable implements Runnable{

    int piao = 100; //这里可以不用加静态,因为三个线程传的是同一个任务

    //锁对象
    static Object obj = new Object();

    int i = 0;

    @Override
    public void run() {

        while (true){
            if (i%2 == 0){
                synchronized (obj){
                    if (piao > 0){
                        try {
                            Thread.sleep(50);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                        System.out.println(Thread.currentThread().getName() + "正在售卖第" + piao-- + "张票");
                    }
                }
            }else {
                maiPiao();
            }
            i++;
        }
    }

    //同步代码块使用的锁对象是任意的Java对象
    //方法加上synchronized就成为了一个同步方法,同步方法使用的锁对象this
    public synchronized void maiPiao(){
        System.out.println(this.getClass().getName());
        if (piao > 0){
            try {
                Thread.sleep(50);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName() + "正在售卖第" + piao-- + "张票");
        }
    }
}
public static void main(String[] args) {

        /*
            出现线程安全问题,要符合几个条件。
              1.是不是多线程环境
              2.多个线程有没有去操作共享资源
              3.是不是有多条语句在操作这个共享资源,也就是对共享资源是不
                是一个原子性的操作
         */

        /*
            我们模拟了一下,网络售票延迟,就出现了一些不合理的数据,也就
            是说,出现了线程安全问题

            1.出现了0票、负数票,这是由于线程的随机性导致,并发导致的。

            2.出现了重复票,原因是由于,原子性所导致的,原子性:不可再分
              割性
                piao-- 不是一个原子性的操作

                piao-- 要经过三个步骤 读  改  写
         */

        CellRunnable cr = new CellRunnable();
        Thread th1 = new Thread(cr, "窗口1");
        Thread th2 = new Thread(cr, "窗口2");
        Thread th3 = new Thread(cr, "窗口3");

        th1.start();
        th2.start();
        th3.start();
    }
public class CellRunnable implements Runnable{

    static int piao = 100; //这里可以不用加静态,因为三个线程传的是同一
    					   //个任务

    //锁对象
    static Object obj = new Object();

    int i = 0;

    @Override
    public void run() {

        while (true){
            if (i%2 == 0){

                //CellRunnable.class当前类的字节码对象
                synchronized (CellRunnable.class){
                    if (piao > 0){
                        try {
                            Thread.sleep(50);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                        System.out.println(Thread.currentThread().getName() + "正在售卖第" + piao-- + "张票");
                    }
                }
            }else {
                maiPiao();
            }
            i++;
        }
    }

    //同步代码块使用的锁对象是任意的Java对象
    //方法加上synchronized就成为了一个同步方法,同步方法使用的锁对象this
    //静态同步方法使用的锁对象是,当前类的字节码文件对象
    public static synchronized void maiPiao(){
        //System.out.println(this.getClass().getName());
        if (piao > 0){
            try {
                Thread.sleep(50);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName() + "正在售卖第" + piao-- + "张票");
        }
    }
}
public static void main(String[] args) {

        /*
            出现线程安全问题,要符合几个条件。
              1.是不是多线程环境
              2.多个线程有没有去操作共享资源
              3.是不是有多条语句在操作这个共享资源,也就是对共享资源是不
                是一个原子性的操作
         */

        /*
            我们模拟了一下,网络售票延迟,就出现了一些不合理的数据,也就
            是说,出现了线程安全问题

            1.出现了0票、负数票,这是由于线程的随机性导致,并发导致的。

            2.出现了重复票,原因是由于,原子性所导致的,原子性:不可再分
              割性
                piao-- 不是一个原子性的操作

                piao-- 要经过三个步骤 读  改  写
         */

        CellRunnable cr = new CellRunnable();
        Thread th1 = new Thread(cr, "窗口1");
        Thread th2 = new Thread(cr, "窗口2");
        Thread th3 = new Thread(cr, "窗口3");

        th1.start();
        th2.start();
        th3.start();
    }
public class CellRunnable implements Runnable{

    int piao = 100; //这里可以不用加静态,因为三个线程传的是同一个任务

    //锁对象
    static Object obj = new Object();

    @Override
    public void run() {
        while (true){
            maiPiao();
        }
    }

    public synchronized void  maiPiao(){
        if (piao > 0){
            try {
                Thread.sleep(50);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName() + "正在售卖第" + piao-- + "张票");
        }
    }
}

22.12 Lock锁

  A:Lock和ReentrantLock
  		void lock() 加锁
  		void unlock() 释放锁
  		最典型的代码:
  		class X {
  			private final ReentrantLock lock = new ReentrantLock();
  			//...
  			public void m() {
  				lock.lock();	//block until condition holds
  				try {
  					//... method body
  				} finally {
  					lock.unlock();
  				}
  			}
  		}
public class Demo02Ticket {
    public static void main(String[] args) {

        CellRunnable cr = new CellRunnable();
        Thread th1 = new Thread(cr, "窗口1");
        Thread th2 = new Thread(cr, "窗口2");
        Thread th3 = new Thread(cr, "窗口3");

        th1.start();
        th2.start();
        th3.start();
    }
}

public class CellRunnable implements Runnable{

    static int piao = 100; //这里可以不用加静态,因为三个线程传的是同一
    					   //个任务

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

    static Lock lock = new ReentrantLock();

    @Override
    public void run() {
        while (true){
            //加锁
            lock.lock();

            try {
                if (piao > 0){
                    try {
                        Thread.sleep(50);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println(Thread.currentThread().getName() + "正在售卖第" + piao-- + "张票");
                }
            }catch (Exception e){
                e.printStackTrace();
            }finally {
                //释放锁
                lock.unlock();
            }
        }
    }
}
  B:死锁问题
  		如果出现同步嵌套,就容易产生死锁问题
  			指两个或者两个以上的线程在执行的过程中,因争夺资源产生的一种
  			互相等待现象
public static void main(String[] args) {
        MyThread th1 = new MyThread(true);
        MyThread th2 = new MyThread(false);
        th1.start();
        th2.start();
    }

public interface LockUtils {
    //定义两把锁对象
    public static final Object objA =  new Object();
    public static final Object objB =  new Object();
}

public class MyThread extends Thread{

    private boolean flag;

    public MyThread(boolean flag) {
        this.flag = flag;
    }

    @Override
    public void run() {
        if (flag){
            synchronized (LockUtils.objA){
                System.out.println("true线程,持有了objA锁,进来了");
                synchronized (LockUtils.objB){
                    System.out.println("true线程,持有了objB锁,进来了");
                }
            }
        }else {
            synchronized (LockUtils.objB){
                System.out.println("false线程,持有了objB锁,进来了");
                synchronized (LockUtils.objA){
                    System.out.println("false线程,持有了objA 锁,进来了");
                }
            }
        }
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值