线程相关知识

程序、进程、线程


创建多线程的方式
  1. 方式一:

    package com.growingwang.Tread;
    
    /**
     * 测试Thread中的常用方法:
     * 1. start():启动当前线程,调用当前线程的run()
     * 2. run():通常需要重写Thread类中的此方法,将创建的线程要执行的操作声明在此方法中
     * 3. currentThread():静态方法,返回执行当前代码的线程
     * 4. getName():获取当前线程的名字
     * 5. setName():设置当前线程的名字
     * 6. yield(): 释放当前CPU的执行权
     * 7. join():在线程a中调用线程b的join(),此时线程a就进入阻塞状态,直到线程b完全执行完成以后,线程a才结束阻塞状态
     * 8. stop(): 已过时。强制结束当前线程
     * 9. sleep(long millitime):让当前线程“睡眠”指定的millitime毫秒。在指定的millitime毫秒时间内,当前线程是阻塞状态
     * 10. isAlive(): 判断当前线程是否存活
     */
    
    class MyThread extends Thread{
        @Override
        public void run() {
            for (int i = 0; i < 100; i++) {
                if(i % 2 == 0){
                    try {
                        sleep(10);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println(Thread.currentThread().getName()+":"+i);
                }
                if(i % 20 == 0){
                    yield();
                }
            }
        }
        public MyThread(){
        }
        public MyThread(String name){
            super(name);
        }
    }
    
    public class ThreadMethodTest {
        public static void main(String[] args) {
            MyThread th1= new MyThread("Thread: 1");
            //th1.setName("th1");
            th1.start();
    
            Thread.currentThread().setName("主线程");
            for (int i = 0; i < 100; i++) {
                if(i % 2 == 0){
                    System.out.println(Thread.currentThread().getName()+":"+i);
                }
                if(i == 20){
                    try {
                        th1.join();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
            System.out.println(th1.isAlive());
        }
    }
    
    
  2. 方式二:

package com.growingwang.Tread;

/*
* 创建多线程方式2:实现Runnable接口
* 1. 创建一个实现Runnable接口的类
* 2. 实现类去实现Runnable的抽象方法:run()
* 3. 创建实现类的对象
* 4. 将此对象作为参数传递到Thread类的构造器中,创建Thread类的对象
* 5. 通过Thread类的对象调用start()
 */

class MThread implements Runnable{
    @Override
    public void run() {
        for (int i = 0; i < 100; i++) {
            if (i % 2 == 0) {
                System.out.println(Thread.currentThread().getName() + ":" + i);
            }
        }
    }
}

public class TestThread2 {
    public static void main(String[] args) {
        MThread mThread = new MThread();
        Thread t1 = new Thread(mThread);
        //通过Thread类的对象调用start():1.启动线程。2.调用当前线程的run()-->调用了Runnable类型的target的run()
        t1.setName("线程1");
        t1.start();
        Thread t2 = new Thread(mThread);
        t2.setName("线程2");
        t2.start();
    }
}

比较创建线程的两种方式

​ 开发中:优先选择实现Runnable接口的类

​ 原因:1. 实现的方式没有类的单继承的局限性

​ 2.实现的额方式更适合来处理多个线程有共享数据的情况

方式三:实现Callable接口 --->JDK5.0新增

线程的生命周期


线程的同步


同步代码块
package com.growingwang.Tread;

/**
* 创建3个窗口卖票,总票数100张,使用Runnable接口的方式
*
* 1.问题:卖票过程中出现了重票、错票--->出现了线程安全问题
* 2.原因:当某个线程操作车票的过程中,尚未完成操作是,其它线程参与进来也操作车票
* 3.如何解决:当一个线程a在操作ticket的时候,其它线程不能参与进来,知道线程a操作完成
*   ticket时,其它线程才可以开始操作ticket。这种情况即使a出现了阻塞,也不能被改变
* 4.在Java中,通过同步机制来解决线程的安全问题
*
* 方式一:同步代码块
*
* synchronized(同步监视器){
*     //需要被同步的代码
* }
* 说明:1.操作共享数据的代码,即为需要被同步的代码
*      2.共享数据:多个线程共同操作的变量。比如:ticket就是共享数据
*      3.同步监视器:俗称:锁,任何一个类的对象,都可以充当锁
*
* 方式二:同步方法
*      如果操作共享数据的代码完整的声明在一个方法中,我们不妨将此方法声明为同步的
*/

class Window implements Runnable{
   private int ticket = 100;
   //Object obj = new Object();
   @Override
   public void run() {
       while(true){
           synchronized (this){//此时的this:唯一的Window的对象 //方式二:synchronized (obj){
               if(ticket > 0){
                   //方式3:synchronized(Window.class){//Window只会加载一次
               try {
                   Thread.sleep(10);
               } catch (InterruptedException e) {
                   e.printStackTrace();
               }
                   System.out.println(Thread.currentThread().getName()+":售票订单为:" + ticket);
                   ticket--;
               }else{
                   break;
               }
           }
       }
   }
}

public class WindowTest {
   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();
   }
}
同步方法
package com.growingwang.Tread;

class Window1 implements Runnable {
    private int ticket = 100;

    //Object obj = new Object();
    @Override
    public void run() {
        while (true) {
            show();
        }
    }

    //方式二
    private synchronized void show() {//同步监视器this
        if (ticket > 0) {
            try {
                Thread.sleep(10);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName() + ":售票订单为:" + ticket);
            ticket--;
        }
    }
}

public class Window1Test {
    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("窗口1");
        t2.setName("窗口2");
        t3.setName("窗口3");

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

总结:

  1. 同步方法仍然涉及到同步监视器,只是不需要我们显示的声明
  2. 非静态的同步方法(实现Runnable),同步监视器是:this
  3. 静态的同步方法(继承Thread),同步监视器是:当前类本身
Lock锁
package com.growingwang.Tread;

import java.util.concurrent.locks.ReentrantLock;

class Window2 implements Runnable{
    private ReentrantLock lock = new ReentrantLock();
    private int ticket = 100;
    public void run(){
        while(true){
            try{
                //调用lock()锁定方法
                lock.lock();
                if(ticket > 0){
                    try {
                        Thread.sleep(100);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println(Thread.currentThread().getName()+":售票订单为:" + ticket);
                    ticket--;
                }else{
                    break;
                }
            }finally {
                //调用解锁方法:unlock()
                lock.unlock();
            }
            }
        }
    }


public class ThreadLock {
    public static void main(String[] args) {
        Window2 w = new Window2();

        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();
    }
}

Lock与Synchronized的异同

相同:二者都可以解决线程的安全问题

不同:Synchronized机制在执行完相应的同步代码以后,自动的释放同步监视器

​ Lock需要手动的气筒同步(lock()),同时结束同步也需要手动的实现(unlock())

优先使用顺序

Lock --->同步代码块(已经进入了方法体,分配了相应资源)---->同步方法(在方法体外)

停止线程

可以通过设置一个标志位flag来操作

class TestStop implements Runnable{
    private boolean flag = true;
    public void run(){
        int i= 0;
        while(flag){
            System.out.println("run....Thread"+i++);
        }
    }
    
    public void stop(){
        this.flag = false;
    }
}

public class StopThread{
    public static void main(String[] args){
        TestStop testStop = new TestStop();
        new Thread(testStop).start();
        
        for(int i=0;i<1000;i++){
            if(i==900){
                testStop.stop();
                System.out.println("线程停止了");
            }
        }
    }
}

线程的死锁


  1. 死锁的理解:不同的线程分别占用对方需要的同步资源不放弃,都在等待对方放弃自己需要的同步资源,形成了线程死锁

  2. 说明:

    1) 出现死锁后,不会出现异常,不会出现提示,只是所有的线程都处于死锁状态,无法继续

​ 2) 使用同步时避免死锁

package com.growingwang.Tread;

public class ThreadLockTest {
    public static void main(String[] args) {

        StringBuffer s1 = new StringBuffer();
        StringBuffer s2 = new StringBuffer();

        new Thread(){
            @Override
            public void run() {
                synchronized (s1){
                    s1.append("a");
                    s2.append("1");

                    try {
                        Thread.sleep(100);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }

                synchronized (s2){
                    s1.append("b");
                    s2.append("2");

                    System.out.println(s1);
                    System.out.println(s2);
                }
            }
        }.start();

        new Thread(){
            public void run() {
                synchronized (s2){
                    s1.append("c");
                    s2.append("3");

                    try {
                        Thread.sleep(100);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }

                synchronized (s1){
                    s1.append("d");
                    s2.append("4");

                    System.out.println(s1);
                    System.out.println(s2);
                }
            }
        }.start();
    }
}

  1. 解决方法
    • 专门的算法、原则
    • 尽量减少同步资源的定义
    • 尽量避免嵌套同步

线程通信


使用两个线程交替打印1-100

package com.growingwang.Tread;

class Communicate implements Runnable{
    private int number = 1;
    public void run(){
        while(true){
            synchronized (this){
                notify();//唤醒

                if (number <= 100){
                    try {
                        Thread.sleep(100);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }

                    System.out.println(Thread.currentThread().getName() + ":" + number);
                    number++;

                    try {
                        //使用调用如下wait()方法的线程进入阻塞状态
                        wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }

            }
        }
    }
}

public class CommunicationTest {
    public static void main(String[] args) {
        Communicate c = new Communicate();
        Thread t1 = new Thread(c);
        Thread t2 = new Thread(c);

        t1.setName("线程1");
        t2.setName("线程2");

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

涉及到的方法:

  • wait():一旦执行此方法,当前线程进入阻塞状态,并释放同步监视器
  • notify():一旦执行此方法,就会唤醒被wait的一个线程,如果有多个线程被wait,就唤醒优先级高的
  • notifyAll():旦执行此方法,就会唤醒被所有被wait的线程

注意:

  • wait(), notify(), notifyAll()三个方法必须使用在同步代码块或同步方法中
  • wait(), notify(), notifyAll()三个方法的调用者必须是同步代码块或同步方法中的同步监视器,否则,会出现IllegalMonitorStateException异常
private Object obj = new Object();   
synchronized (obj){
                this.notify();
    			...
                    
               try {
                        this.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
}
  • wait(), notify(), notifyAll()三个方法都定义在Object类中
sleep()与wait()的区别
  1. 相同点:一旦执行方法,都可以使得当前的线程进入阻塞状态

  2. 不同点:1)两个方法声明的位置不同:Thread类中声明sleep(),Object类中声明wait()

    ​ 2)调用的要求不同:sleep()可以在任何需要的场景下调用,wait()必须使用在同步 代码块或同步方法中

    ​ 3)关于是否释放同步监视器:如果两个方法都使用在同步代码块或同步方法中, sleep()不会释放锁,wait()会释放锁

线程池


背景:经常创建和销毁、使用量特别大的资源,比如并发情况下的线程,对性能的影响很大

思路:提前创建好多线程,放入线程池中,使用时直接获取,使用完放回池中,可以避免频繁的创建、销毁,实现重复利用。

  • ExecutorService: 真正的线程池接口。

    ​ void execute(Runnable command):执行任务/命令,没有返回值

​ void shutdown(): 关闭连接池

  • Excutors: 工具类、线程池的工厂类,用于创建并返回不同类型的线程池。
package com.growingwang.Tread;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class TestPool {
    public static void main(String[] args) {
        //1.开启服务,创建线程池
        ExecutorService service = Executors.newFixedThreadPool(10);

        //执行
        service.execute(new MThread());
        service.execute(new TestThread1());
        service.execute(new MThread());
        service.execute(new MThread());

        //2.关闭链接
    }
}

posted @   growingwang  阅读(43)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 【译】Visual Studio 中新的强大生产力特性
· 【设计模式】告别冗长if-else语句:使用策略模式优化代码结构
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
点击右上角即可分享
微信分享提示