Java多线程

简介: Java多线程

进程与线程

进程(Process):进程是程序的一次动态执行过程,它经历了从代码加载、执行、到执行完毕的一个完整过程;同时也是并发执行的程序在执行过程中分配和管理资源的基本单位,竞争计算机系统资源的基本单位。

线程(Thread):线程可以理解为进程中的执行的一段程序片段,是进程的一个执行单元,是进程内可调度实体,是比进程更小的独立运行的基本单位,线程也被称为轻量级进程。

一、多线程的实现

1.继承Thread类

  • 方式1:继承Thread类

    • 定义一个类MyThread继承Thread类
    • 在MyThread类中重写run0方法
    • 创建MyThread类的对象
    • 启动线程

案例分析:

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


package com.Thread;
public class MyThreadTest {
    public static void main(String[] args) {
        MyThread t1=new MyThread();
        MyThread t2=new MyThread();
//        t1.run();
//        t2.run();
        //start()导致此线程开始执行,java虚拟机调用此线程的run方法
        t1.start();
        t2.start();
    }
}

1.设置和获取线程名称

package com.Thread;
public class MyThread extends Thread {
    public MyThread() {
    }

    public MyThread(String name) {
        super(name);
    }

    @Override
    public void run() {
        for (int i = 0; i <100 ; i++) {
            //getName()获取线程名称
            System.out.println(getName()+":"+i);
        }
    }
}



package com.Thread;
public class MyThreadTest {
    public static void main(String[] args) {
//        MyThread t1=new MyThread();
//        MyThread t2=new MyThread();
       /* //设置线程名称(方式一)
        t1.setName("小马哥");
        t2.setName("小飞侠");*/
//        t1.run();
//        t2.run();
        //start()导致此线程开始执行,java虚拟机调用此线程的run方法
        
        //设置线程名称(方式二)
        MyThread t1=new MyThread("小马哥");
        MyThread t2=new MyThread("小飞侠");

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

2.线程优先级

  • Thread类中设置和获取线程优先级的方法

    • public final int getPriority0):返回此线程的优先级
    • public final void setPriority(int newPriority):更改此线程的优先级

案例分析:

package com.priority;

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


package com.priority;

public class MyPriorityTest {
    public static void main(String[] args) {
        MyPriority p1=new MyPriority();
        MyPriority p2=new MyPriority();
        MyPriority p3=new MyPriority();
        p1.setName("线程一");
        p2.setName("线程二");
        p3.setName("线程三");

//        System.out.println(Thread.NORM_PRIORITY);  //5
//        System.out.println(Thread.MAX_PRIORITY);  //10
//        System.out.println(Thread.MIN_PRIORITY);  //1

        //设置线程优先级
        p1.setPriority(1);
        p2.setPriority(7);
        p3.setPriority(10);  //线程等级越高获取cpu时间片的几率高

        p1.start();
        p2.start();
        p3.start();
    }
}

3.线程控制

方法名 说明
static void sleep(long millis) 使当前正在执行的线程停留(暂停执行)指定的毫秒数
void join() 等待这个线程死亡
void setDaemon(boolean on) 将此线程标记为守护线程,当运行的线程都是守护线程时,Java虚拟机将退出

案例分析:

package com.control;

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

        }
    }
}

package com.control;

public class ThreadSleepTest {
    public static void main(String[] args) {
        ThreadSleep s1=new ThreadSleep();
        ThreadSleep s2=new ThreadSleep();
        ThreadSleep s3=new ThreadSleep();
        s1.setName("小马哥");
        s2.setName("小飞侠");
        s3.setName("老六");
        s1.start();
        s2.start();
        s3.start();

    }
}
package com.control;

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

package com.control;

public class ThreadJoinTest {
    public static void main(String[] args) {
        ThreadJoin j1=new ThreadJoin();
        ThreadJoin j2=new ThreadJoin();
        ThreadJoin j3=new ThreadJoin();
        j1.setName("老大");
        j2.setName("老二");
        j3.setName("老三");
        j2.start();
        //当j2执行完之后,j1,j3才开始执行
        try {
            j2.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        j1.start();
        j3.start();
    }
}
package com.control;

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

package com.control;

public class ThreadDaemonTest {
    public static void main(String[] args) {
        ThreadDaemon d1=new ThreadDaemon();
        ThreadDaemon d2=new ThreadDaemon();

        d1.setName("张飞");
        d2.setName("关羽");
        //设置主线程,主线程执行完毕之后,守护线程也会很快的结束
        Thread.currentThread().setName("刘备");
        //设置守护线程
        d1.setDaemon(true);
        d2.setDaemon(true);

        d1.start();
        d2.start();

        for (int i = 0; i <10 ; i++) {
            System.out.println(Thread.currentThread().getName()+":"+i);
        }

    }

}

4.线程的生命周期

在这里插入图片描述

2.实现Runnable接口的方式实现多线程

  • 方式2:实现Runnable接口

    • 定义一个类MyRunnable实现Runnable接口
    • 在MyRunnable类中重写run()方法
    • 创建MyRunnable类的对象
    • 创建Thread类的对象,把MyRunnable对象作为构造方法的参数
    • 启动线程
  • 多线程的实现方案有两种

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

    • 避免了Java单继承的局限性
    • 适合多个相同程序的代码去处理同一个资源的情况,把线程和程序的代码、数据有效分离,较好子的体现了面向对象的设计思想。

案例分析:

package com.Runnable;

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.Runnable;

public class MyRunableTest {
    public static void main(String[] args) {
        MyRunnable m=new MyRunnable();
        Thread t1=new Thread(m,"小马哥");
        Thread t2=new Thread(m,"小飞侠");
        t1.start();
        t2.start();
    }
}

二、线程同步

  • 锁多条语句操作共享数据,可以使用同步代码块实现
  • 格式:
synchronized(任意对象){
     多条语句操作共享数据的代码
)
  • synchronized(任意对象):就相当于给代码加锁了,任意对象就可以看成是一把锁

同步的好处和弊端

  • 好处:解决了多线程的数据安全问题
  • 弊端:当线程很多时,因为每个线程都会去判断同步上的锁,这是很耗费资源的,无形中会降低程序的运行效率

案例分析:

package com.SellTicketTest;

public class SellTicket implements Runnable {
    private int ticket=100;
    private Object obj=new Object();
    @Override
    public void run() {
        while (true) {
            synchronized (obj) {
                if (ticket > 0) {
                    try {
                        Thread.sleep(100);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println(Thread.currentThread().getName() + "正在售出第" + ticket + "张票");
                    ticket--;
                }
            }
        }
    }
}


package com.SellTicketTest;

public class SellTicketTest {
    public static void main(String[] args) {
        SellTicket ticket=new SellTicket();
        Thread t1=new Thread(ticket,"窗口一");
        Thread t2=new Thread(ticket,"窗口二");
        Thread t3=new Thread(ticket,"窗口三");
        t1.start();
        t2.start();
        t3.start();

    }
}

三、线程安全的类

/*
线程安全的类;
    StringBuffer
    Vector
    HashtabLe
    */
public c1ass ThreadTest {
    public static void main(string[] args){
         StringBuffer sb = new StringBuffer();
         StringBuilder sb2 = new StringBuilder();
         
         Vector<String> v = new Vector<String>();
         ArrayList<String> array = new ArrayList<string>();
        Hashtable<String,String> ht = new Hashtable<String,string>();
        HashNap<String,string> hm = new HashMap<String,string>();
        List<String> list = Collections.synchronizedList(new ArrayList<String>());
      }
 }

四、Lock锁

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

    • void lock():获得锁
    • void unlock():释放锁
  • Lock是接口不能直接实例化,这里采用它的实现类ReentrantLock来实例化ReentrantLock的构造方法

    • ReentrantLock():创建一个ReentrantLock的实例

案例分析:

package com.lock;

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class MyLock implements Runnable {
    private int ticket=100;
    private Lock lock=new ReentrantLock();
    @Override
    public void run() {
        while (true) {

            try {
                lock.lock();
                if (ticket > 0) {
                    try {
                        Thread.sleep(100);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println(Thread.currentThread().getName() + "正在售出第" + ticket + "张票");
                    ticket--;
                }
            } catch (Exception e) {
                e.printStackTrace();
            }finally {
                lock.unlock();
            }
        }
    }
}

package com.lock;
import com.SellTicketTest.SellTicket;
public class MyLockTest {
    public static void main(String[] args) {
        MyLock ticket=new MyLock();
        Thread t1=new Thread(ticket,"窗口一");
        Thread t2=new Thread(ticket,"窗口二");
        Thread t3=new Thread(ticket,"窗口三");
        t1.start();
        t2.start();
        t3.start();
    }
}

五、生产者消费者模式

  • ava就提供了几个方法供我们使用,这几个方法在Object类中Object类的等待和唤醒方法:
方法名 说明
void wait() 导致当前线程等待,直到另一个线程调用该对象的notify)方法或notifyAll)方法
void notify() 唤醒正在等待对象监视器的单个线程
void notifyAll() 唤醒正在等待对象监视器的所有线程

案例分析:

 
class Box{
    private int milk;
    private boolean state=false;
    public synchronized void put(int milk)  {//同步代码块:执行这块代码后,所在线程加锁,不会被抢占使用权。
                                             //这时其他线程要执行,需要wait()该线程,notify()其他线程
        if(state) {  //有奶,不再继续放,put的线程暂停,等待get线程拿出奶
            try {
                wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        //state=false,没有奶,生产者放入奶,这是第i瓶
        this.milk=milk;
        System.out.println("生产者放入第"+this.milk+"瓶奶");
        state=true;   //有了奶,奶箱不为空,修改奶箱状态
        notifyAll();  //唤醒其他所有线程(唤醒get线程,取出牛奶)
    }
    public synchronized void get()  {
        if(!state) {  //state=false,没有奶,消费者没法拿出奶,只能等待
            try {
                wait();  //消费者的get行为/线程开始等待
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        //state=true,有奶,可以拿出,这是第i瓶奶
        System.out.println("消费者拿出第"+this.milk+"瓶奶");
        state=false; //拿出以后,box空,修改box状态
        notifyAll();  //唤醒其他所有线程(唤醒put线程,开始放入)
    }
}
 
class Producer implements Runnable{
    private Box b;
    public Producer(Box b) {
        this.b=b;
    }
    @Override
    public void run() {
        for(int i=1;i<11;i++) {
            b.put(i);
        }
    }
   
}
 
class Customer implements Runnable{
    private Box b;
    public Customer(Box b) {
        this.b=b;
    }
    @Override
    public void run() {
        while(true) {
            b.get();
        }
    }
}
 
public class Milk {
    public static void main(String[] args) {
        Box b=new Box();    //创建一个奶箱
        Producer p=new Producer(b);  //都用这个奶箱
        Customer c=new Customer(b);
        Thread t1=new Thread(p);    //producer在线程1中
        Thread t2=new Thread(c);    //customer在线程2中
        t1.start();
        t2.start();
        
    }
}
相关文章
|
19天前
|
数据采集 存储 弹性计算
高并发Java爬虫的瓶颈分析与动态线程优化方案
高并发Java爬虫的瓶颈分析与动态线程优化方案
Java 数据库 Spring
58 0
|
1月前
|
算法 Java
Java多线程编程:实现线程间数据共享机制
以上就是Java中几种主要处理多线程序列化资源以及协调各自独立运行但需相互配合以完成任务threads 的技术手段与策略。正确应用上述技术将大大增强你程序稳定性与效率同时也降低bug出现率因此深刻理解每项技术背后理论至关重要.
84 16
|
2月前
|
缓存 并行计算 安全
关于Java多线程详解
本文深入讲解Java多线程编程,涵盖基础概念、线程创建与管理、同步机制、并发工具类、线程池、线程安全集合、实战案例及常见问题解决方案,助你掌握高性能并发编程技巧,应对多线程开发中的挑战。
|
2月前
|
数据采集 存储 前端开发
Java爬虫性能优化:多线程抓取JSP动态数据实践
Java爬虫性能优化:多线程抓取JSP动态数据实践
|
3月前
|
Java API 调度
从阻塞到畅通:Java虚拟线程开启并发新纪元
从阻塞到畅通:Java虚拟线程开启并发新纪元
307 83
|
3月前
|
安全 算法 Java
Java 多线程:线程安全与同步控制的深度解析
本文介绍了 Java 多线程开发的关键技术,涵盖线程的创建与启动、线程安全问题及其解决方案,包括 synchronized 关键字、原子类和线程间通信机制。通过示例代码讲解了多线程编程中的常见问题与优化方法,帮助开发者提升程序性能与稳定性。
144 0
|
3月前
|
存储 Java 调度
Java虚拟线程:轻量级并发的革命性突破
Java虚拟线程:轻量级并发的革命性突破
259 83
|
4月前
|
移动开发 Java
说一说 Java 是如何实现线程间通信
我是小假 期待与你的下一次相遇 ~