java线程

多线程

单任务和多任务

单任务处理:在一个任务没有完成时,不会执行别的任务。

多任务处理:在一个任务没有完成时,同时进行别的任务,多个任务同时执行。

多任务处理分类

基于进程:进程是自包容的应用程序,由操作系统直接管理,直接运行。每个进程一开启,都会消耗内存空间。

基于线程:线程是应用程序中的顺序控制流,一个进程中可以包含很多线程,多个线程共享一个进程的内存空间。

线程

主线程

main方法一个运行,就开启一个主线程,每个进程都有一个主线程

**特点:**1.最先开始

2.最后结束

3.产生其他的子线程

4.子线程结束后,回收子线程占用的资源。

创建子线程

1、继承Thread类,重写run方法
class Thread1 extends Thread{
    @Override
    public void run() {

    }

启动线程

        Thread1 thread1=new Thread1();
        //启动线程
        thread1.start();

调用线程类start()和run()方法的区别:

  • 调用start(),会在主线程之外,开启子线程,多个任务同时执行。
  • 调用run(),是普通方法调用,不会在主线程之外,开启子线程,是单任务处理。
2、实现Runnable接口,实现run方法
class Thread2 implements Runnable {
    @Override
    public void run() {

}

启动线程

       //创建线程对象,传入Runnable实现类对象,线程启动后,会执行Runnable实现类的run()
        Thread thread2 = new Thread(new Thread2());
        thread2.start();
3、实现Callable接口,实现calll方法

继承Thead类,重写run,实现Runnable接口,实现run方法。在线程结束后,都不能那个得到返回值。

实现Callable接口,重写call方法,在线程结束后可以得到返回值。

        //创建FutureTask对象,该类为runnable实现类。
        //传入callable实现类对象,线程启动时,会执行callable实现类的call方法
        FutureTask<Integer> task = new FutureTask<Integer>(new Callable<Integer>() {
            @Override
            public Integer call() throws Exception {
                int sum = 0;
                for (int i = 1; i <= 50; i++) {
                    sum += i;
                }
                return sum;
            }
        });
        //启动线程
        new Thread(task).start();
        try {
            //得到线程执行完毕后的返回值
            Integer sum = task.get();
            System.out.println(sum);
        } catch (Exception e) {
            e.printStackTrace();
        }
4、采用线程池创建线程

应用大量通过new Thead()方法创建执行时间短的线程,较大消耗系统资源并且系统的响应速度变慢。

线程池首先创建一些线程,他们的集合称为线程池。线程池在系统启动时即创建大量空闲的线程,程序将一个任务传给线程池,线程池就会启动一条线程来执行这个任务。执行任务结束以后,该线程并不会死亡,而是再次返回线程池中成为空闲状态,等待执行下一个任务。

线程池的工作机制

1、在线程池的编程模式下,任务是提交給整个线程池,而不是直接提交给某个线程,线程池在拿到任务后,就是内部寻找是否有空闲的线程,如果有,则将任务交给某个空闲的线程。

2、一个线程同时只能执行一个任务,但可以同时向一个线程池提交多个任务。

线程池的种类

1、创建一个可以无限扩大的线程池,适用于负载较轻的场景,执行短期异步任务。

    public static void main(String[] args) {
        //创建无限扩展的线程池
        ExecutorService service = Executors.newCachedThreadPool();
        //向线程池提交任务
        service.execute(new Mythead());
        service.execute(new Mythead());
        //关闭线程池
        service.shutdown();

    }

class Mythead implements Runnable {
    @Override
    public void run() {
        for (int i = 0; i < 10; i++) {
            //Thread.currentThread()得到当前线程
            System.out.println(Thread.currentThread() + "   " + i);
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

2、创建一个固定大小的线程池,因为采用无界的阻塞队列,所以实际线程数量永远不会变化,适用于负载较重的场景,对当前线程数量进行限制。

        //创建大小为2的线程池
        ExecutorService service = Executors.newFixedThreadPool(2);
        //向线程池提交任务
        service.execute(new Mythead());
        service.execute(new Mythead());
        service.execute(new Mythead());
        //关闭线程池
        service.shutdown();

开始时,线程池中两个线程交替执行两个任务。第三个任务进行等待。当线程池中其中一个线程任务执行完毕,就执行第三个任务。

3、创建一个单线程的线程池,适用于需要保证顺序执行各种任务。

        //创建单线程线程池
        ExecutorService service = Executors.newSingleThreadExecutor();
        service.execute(new Mythead());
        service.execute(new Mythead());
        service.shutdown();

任务一个一个按顺序执行

线程状态

  • 新建 创建Thread对象
  • 就绪 调用start()方法,启动线程
  • 运行 执行run()方法
  • 死亡 run()执行完毕
  • 睡眠 调用Thread.sleep(30)
  • 等待 调用Object类中的wait()
  • 挂起 调用yield()方法,当前线程让出CPU使用权
  • 阻塞 等待IO事件输入

线程优先级

线程优先级是指,当两个或两个以上的线程都处于就绪状态。优先级高的线程,会优先得到执行。

优先级分为10级,1-10,默认为5,数字越大,优先级越高。

优先级的设置和获取

        Thread thread = new Thread();
        //设置线程的优先级
        thread.setPriority(10);
        //得到线程的优先级
        System.out.println(thread.getPriority());

线程同步

当两个或两个以上的线程,同时访问同一个资源时,为了保证数据的安全,只允许同一事件一个线程进行访问。

线程同步的后果

1、数据安全

2、效率低

死锁

死锁是指在多线程情况下,多个线程同步竞争相互依赖的资源,从而造成多线程无法继续执行的情况。

public class DieLok {
    public static void main(String[] args) {
        LockObj o1 = new LockObj("乔丹");
        LockObj o2 = new LockObj("皮蓬");
        new Thread(new Runnable() {
            @Override
            public void run() {
                synchronized (o1) {
                    try {
                        Thread.sleep(200);
                        o2.speak();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        }).start();
        new Thread(new Runnable() {
            @Override
            public void run() {
                synchronized (o2) {
                    try {
                        Thread.sleep(200);
                        o1.speak();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        }).start();
    }
}

class LockObj {
    private String name;

    public LockObj(String name) {
        this.name = name;
    }

    public synchronized void speak() {
        System.out.println("我叫:" + this.name);
    }
}
线程同步的实现
使用synchronized
  • 同步方法

    public synchronized void getMoney() {}
    
  • 同步块

        public void run() {
            synchronized(one){
                one.display();
            }
        }
    
使用Lock接口
  • Lock是一个接口,而synchronized是Java内置的语言实现
  • synchronized在发生异常时,会自动释放线程占有的锁,因此不会导致死锁现象发生,而Lock在发生异常时,如果没有主动通过unlock()去释放锁,则很可能造成死锁现象,因此使用lock时需要在finally块中释放锁。
  • Lock可以让等待锁的线程响应中断,而synchronized却不行,使用synchronized时,等待的线程会一直等待下去,不能够相应中断
  • 通过Lock可以知道有没有成功获取锁,而synchronized无法办到
  • Lock可以提高多个线程进行读操作的效率
  1. Lock接口常用方法:lock()方法获取锁时,如果锁已经被其它线程获取,则等待
public class LockTest {
    public static void main(String[] args) {
        LockObject lockObject = new LockObject();
        new Thread(new Runnable() {
            @Override
            public void run() {
                lockObject.speak();
            }
        }).start();

        new Thread(new Runnable() {
            @Override
            public void run() {
                lockObject.speak();
            }
        }).start();
    }
}

class LockObject {
    private Lock lockObject = new ReentrantLock();
    public void speak() {
        //加锁
        lockObject.lock();
        try {
            for (int i = 0; i < 10; i++) {
                System.out.print(i);
                    Thread.sleep(200);
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            lockObject.unlock();
        }
    }
}

2.trylock()方法是有返回值的,它表示用来尝试获取锁,如果获取成功,则返回ture,如果获取失败(即锁已被其它线程获取),则返回false,也就是说这个方法无论如何都会立即返回,在拿不到锁时不会一直在那等待。

class LockObject {
    private Lock lockObject = new ReentrantLock();
    public void speak() {
        if (lockObject.tryLock()) {
            try {
                for (int i = 0; i < 10; i++) {
                    System.out.print(i);
                    Thread.sleep(200);
                }
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                lockObject.unlock();
            }
        }else {
            System.out.println("获取锁失败");
        }
    }
}


3.读写锁

  • 当有多个线程读写文件时,读操作和写操作会发生冲突现象,写操作和写操作会发生冲突现象,但是读操作和读操作不会发生冲突现象。
  • 读写锁将对一个资源(比如文件)的访问分成了2个锁,一个读锁和一个写锁
  • 如果多个线程都只是进行读操作,所以当一个线程在进行读操作时,其它线程可以同时进行读操作。申请写操作的线程只能等待。
  • 如果有一个线程已经占用了写锁,则此时其它线程如果申请写锁或者读锁,则申请的线程会一直等待释放写锁。
线程通讯

java使用wait()、notify()和notifyAll()方法,完成线程间的通信。

wait()、notify()和notifyAll()都在Object中定义。都只能在同步方法或同步块中使用。

public class WaitTest {
    public static void main(String[] args) {
        TestObj obj = new TestObj();
        new ObjA(obj).start();
        new ObjB(obj).start();
    }
}

class TestObj {
    public boolean isRun = true;
}


class ObjA extends Thread {
    private TestObj obj;

    public ObjA(TestObj obj) {
        this.obj = obj;

    }

    public void run() {
        while (true) {
            synchronized (obj) {
                if (obj.isRun == true) {
                    System.out.print("A");
                    obj.isRun = false;
                    try {
                        Thread.sleep(200);
                        //唤醒等待线程访问obj对象
                        obj.notify();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                } else {
                    try {
                        //当前线程对obj对象等待
                        obj.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        }
    }
}

class ObjB extends Thread {
    private TestObj obj;

    public ObjB(TestObj obj) {
        this.obj = obj;

    }

    public void run() {
        while (true) {
            synchronized (obj) {
                if (obj.isRun == false) {
                    System.out.print("B");
                    obj.isRun = true;
                    try {
                        Thread.sleep(200);
                        obj.notify();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }else {
                    try {
                        obj.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        }
    }
wait和sleep的区别

wait和sleep都可以让线程暂时停止运行,但是使用时机是不同的。

  1. sleep在Thread类中定义,而wait在Object中定义
  2. wait只能放在同步方法或同步块中,表示针对某个对象进行等待。而sleep可以放在方法中任何位置,表示当前线程休眠。
  3. wait会释放对象锁,而sleep不会
  4. sleep在休眠结束后,自动恢复线程运行。而wait需唤醒才能恢复线程运行。
Volatile

变量定义为volatile之后将具备两种特性:

  • 保证此变量对所有的线程的可见性,当一个线程修改了这个变量的值,volatile保证了新值能立即同步到主内存
  • 禁止指令重排序优化
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值