Java 多线程

互斥:通过竞争,独占资源,各线程彼此之间不需要知道对方的存在,乱序执行。
同步:协调多个相互关联的线程按一定的顺序,合作完成任务,彼此之间知道对方存在。
  1. 创建线程

(1)继承自Thread类

public class MyThread extends Thread{
    @Override
    public void run() {
        //代码
    }
}

class Test{
    public static void main(String[] args){
        MyThread myThread = new MyThread();
        myThread.run();     //普通的方法调用
        myThread.start();   //相当于开启一个新进程,该线程与main线程是平级的
    }
}
每新建一个线程,都会在JVM中新建一个栈空间,且每个线程都会争夺进程的CPU。

(2)实现Runnable接口

public class MyRunnable implements Runnable{
    @Override
    public void run() {
        String name = Thread.currentThread().getName();
        //获取当前进程的名字
    }
}

class TestRunnable{
    public static void main(String[] args){
        MyRunnable myRunnable = new MyRunnable();   //线程的执行体
        Thread thread = new Thread(myRunnable, "窗口1");
        thread.start(); //启动进程
    }
}
优点:
①Runnable更适合处理共享资源。若共享资源定义为static,Thread也能处理。
②避免Java中单继承的局限性。
③实现解耦,代码可以被多个线程共享

(3)Callable接口

class MyCallable implements Callable<Integer>{  //此时泛型要指定具体类型,如Integer、String
    @Override
    public Integer call() throws Exception {
        //代码
        return null;
    }
}

class TestCallable{
    public static void main(String[] args) {
        ExecutorService pool = Executors.newFixedThreadPool(3);
        MyCallable myCallable = new MyCallable();
        Future<Integer> result = pool.submit(myCallable);
        pool.shutdown();
        Integer value = result.get();   //获取线程的结果
    }
}

  1. 线程常用方法

(1)设置线程优先级

MyThread myThread = new MyThread();
myThread.setName("线程1");
//为线程设置名字
myThread.setPriority(Thread.NORM_PRIORITY);  
//为线程设置优先级
//Java优先级1~10,值越大,优先级越高
//Thread.MAX_PRIORITY = 10
//Thread.MIN_PRIORITY = 1
//Thread.NORM_PRIORITY = 5
优先级高的线程会获得较多的运行机会。

(2)线程休眠

MyThread t = new MyThread();
t.start();                 //开启的进程才能休眠   
try {
    Thread.sleep(1000);    //让当前进程休眠n秒,非main线程
} catch (InterruptedException e) {
    e.printStackTrace();
}    

(3)线程让步

class A extends Thread{
    @Override
    public void run() {
        //代码
    }
}

class B extends Thread{
    @Override
    public void run() {
        Thread.yield();
        //yield()无法真正达到线程让步的目的
        //yield()可以使当前进程放弃CPU的执行权,重新回到可执行状态,但马上又会去竞争CPU的执行权
    }
}

class test1{
    public static void main(){
        new A().start();
        new B().start();
    }
}

(4)线程合并

主线程的执行会被打断,直到新加入的线程执行完毕,主线程才继续。

class A extends Thread{
    @Override
    public void run() {
        //代码
    }
}

class Test{
    public static void main(){
        A a = new A();
        a.start();
        try {
            a.join();    //线程让步
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

  1. 守护线程

当主线程结束时,守护线程也会结束。

class Test{
    public static void main(){
        MyThread t = new MyThread();
        t.setDaemon(true);  //设为守护线程
        t.start();
    }
}

  1. 线程的生命周期

(1)新建状态New

当线程对象创建之后进入新建状态。

(2)就绪状态Runnable

当执行start()时,线程进入就绪状态,此时线程随时等待CPU调度执行。

(3)运行状态Running

当CPU开始调度就绪状态的线程时,该线程进入运行状态。

(4)阻塞状态Blocked

运行状态的线程由于某种原因,暂时放弃CPU的使用权,停止运行,此时线程进入阻塞状态。

  1. 等待阻塞

运行状态的线程执行wait()。

  1. 同步阻塞

线程获取synchronized同步锁失败。此时锁被其他线程占有。

(5)死亡状态Dead

线程执行完毕,或发生异常时,线程进入死亡状态。

  1. 线程安全

CPU在高速切换过程中,线程在任何时刻都有可能被抢占CPU的执行权。

原子操作:不可分割的多步操作被视为一个整体。

(1)同步代码块

synchronized,一种同步锁,可以保证多个线程访问共享数据时,同一时刻只会有一个线程操作,其余线程进入阻塞状态。

class SynThread extends Thread{
    Object lock = new Object();
    //锁对象,任意类型对象都可以充当锁对象
    //多个线程都有使用同一个锁,即同一个对象

    @Override
    public void run() {
        synchronized (lock){
            //CPU保证只有一个线程能执行这段代码
        }
    }
}
//多线程可以使用以下方法创建对象
//.class文件加载进内存的时候,只会创建一个该类对象
synchronized(MyThread.class){
    //代码
}

(2)同步方法

class SynThread extends Thread{
    public synchronized void test(){
        //此时,锁对象为this
        //synchronized也可以修饰static方法
    }

    @Override
    public void run() {
        test();
    }
}

(3)Lock

JDK5以后,新加Lock接口,实现类ReentrantLock和synchronized有一样的功能。

class Test{
    private Lock lock = new ReentrantLock();
    void test(){
        lock.lock();    //加锁
        try {
            //有线程安全问题的代码
        }catch (Exception ex){
            //错误处理
        }finally {
            lock.unlock();  //解锁
        }
    }
}

  1. 线程通信——等待唤醒机制

class Test{
    public static void main(String[] args) {
        Object lock = new Object();
        new Thread(
                new Runnable() {
                    @Override
                    public void run() {
                        synchronized (lock){    //获得锁
                            try {
                                lock.wait();    //释放锁,线程进入等待池
                                //代码
                            } catch (InterruptedException e) {
                                e.printStackTrace();
                            }
                        }
                    }
                }, "线程一").start();

        new Thread(
                new Runnable() {
                    @Override
                    public void run() {
                        synchronized (lock){    //获得锁
                            lock.notify();      //唤醒一个等待最久的线程。唤醒后的线程进入就绪状态,将重新竞争CPU的执行权
                        }
                    }
                }, "线程二").start();
    }
}
wait(1000*n) //等待n秒
sleep(long m) //在时间内不会释放锁
lock.notifyAll() //唤醒所有等待中的线程

  1. 线程池

一个可以容纳多个线程的容器,其中的线程可以反复使用,省去创建和销毁线程的步骤。

顶级接口java.util.concurrent.Executor,是执行线程的工具。
真正的线程池接口java.util.concurrent.Executors。
class Test{
    public static void main(String[] args) {
        //方式一,创建固定长度的线程池对象,
        ExecutorService pool1 = Executors.newFixedThreadPool(3);
        MyRunnable myRunnable = new MyRunnable();   //创建一个Runnable接口子类对象
        pool1.submit(myRunnable);   //提交,提交一次就是启动一个线程
        pool1.shutdown();   //关闭线程池
        
        //方式二,创建一个可缓存,大小按需求变化的线程池
        ExecutorService pool2 = Executors.newCachedThreadPool();
        
        //方式三,创建一个单线程的线程池,程序按顺序进行
        ExecutorService pool3 = Executors.newSingleThreadExecutor();
        
        //方式四,创建一个固定长度的线程池,并以延迟或定时的方式进行
        ScheduledExecutorService s = Executors.newScheduledThreadPool(3);
        s.schedule(myRunnable, 3, TimeUnit.SECONDS);//启动一个线程,并延迟3秒执行
        while(!s.isTerminated()){
            //当线程没有执行完毕,main线程将会进入循环
        }
        s.shutdown();   //关闭线程池,但不会马上终止程序
    }
}

  1. 线程安全集合

(1)CopyOnWriteArrayList

线程安全的ArrayList。写入时,先copy一个容器副本,再添加新元素,最后替换引用到副本。

(2)CopyOnWriteArraySet

线程安全的ArraySet。

(3)ConcurrentHashMap

线程安全的HashMap。多线程的时候可能出现异常。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值