Java基础-多线程总结

Java基础-多线程总结

线程常用方法
方法说明
public static void sleep(long millis)当前线程主动休眠millis毫秒。
public static void yield()当前线程主动放弃时间片,回到就绪状态,竞争下一次时间片。
public final void join()允许其他线程加入到当前线程中
Thread.setDaemon(true)守护进程,Thread(线程名),必须在线程启动之前调用
Thread.setPriority(int a)线程优先级,a为1-10

方法就不和大家演示了

如何使用多线程
  • 继承Thread类

    class MyThread extends Thread{
    
        @Override
        public void run() {
            //编写线程中的操作
    
        }
    }
    
  • 实现Runnable接口

    class MyRunnable implements Runnable{
    	public void run(){
    		//多线程处理的逻辑代码
    	}
    }
    

推荐使用Runnable

1.因为java中单继承,继承了Thread类就无法在继承其他的类了,使用Runnable接口更为灵活一点;

2.另外继承代表强关联,实现稍微弱一点;

执行线程的是start方法

案例

使用子线程打印 0-9

  • 继承Thread
//继承Thread
public class ThreadDemo {
    public static void main(String[] args) {

        MyThread myThread = new MyThread();
        myThread.start();//开启子线程
        System.out.println("主线程结束");

    }

}
class MyThread extends Thread{

    @Override
    public void run() {
        for (int i=0;i<10;i++){
            //Thread.currentThread().getName(),当前线程的名字
            System.out.println(Thread.currentThread().getName()+"---"+i);
        }
        System.out.println("子线程结束");
    }
}
  • 实现Runnable
//实现Runnable
public class RunnableDemo {
    public static void main(String[] args) {
       MyRunnable myRunnable = new MyRunnable();
       Thread thread = new Thread(myRunnable);
       thread.start();
        System.out.println("主线程结束");

    }

}
class MyRunnable implements Runnable {

    @Override
    public void run() {
        for (int i=0;i<10;i++){
            //Thread.currentThread().getName(),当前线程的名字
            System.out.println(Thread.currentThread().getName()+"---"+i);
        }
        System.out.println("子线程结束");
    }
}
  • 其他
public class RunnableDemo1 {
    public static void main(String[] args) {
		//使用匿名内部类创建线程
        new Thread(new Runnable() {
            @Override
            public void run() {
                //执行代码
            }
        }).start();

    }

}

两者运行结果

在这里插入图片描述

线程安全问题
  • 什么是线程不安全

    • 当多线程并发访问临界资源时,如果破坏原子操作,可能会造成数据不一致
    • 临界资源:共享资源(同一对象),一次仅允许一个线程,才可以保证其正确性。
    • 原子操作:不可分割的多步操作,被视作一个整体其顺序不可打乱或缺省
  • 案例:车站有四个窗口共卖100张票

public class Sell2 {

    public static void main(String[] args) {
        /*车站四个窗口共卖100张票*/
        MyRunnable2 runnable = new MyRunnable2();
        Thread thread1 = new Thread(runnable,"窗口一");//第二个参数,是线程的名字
        Thread thread2 = new Thread(runnable,"窗口二");//第二个参数,是线程的名字
        Thread thread3 = new Thread(runnable,"窗口三");//第二个参数,是线程的名字
        Thread thread4 = new Thread(runnable,"窗口四");//第二个参数,是线程的名字

        //开启线程
        thread1.start();
        thread2.start();
        thread3.start();
        thread4.start();

    }

}

class MyRunnable2 implements Runnable {
    int size = 100;

    @Override
    public void run() {


        while (size>0){
            try {
                Thread.sleep(100);//让线程睡眠100毫秒
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            size--;
            System.out.println(Thread.currentThread().getName() + "已售出一张票,剩余票数为" + size);
        }

    }

}

运行结果

在这里插入图片描述

  • 案例分析

假设窗口一拿到了最后一张票时,还没来得及size–,其他窗口已经进入了while(size>0)循环,所以导致上述问题

解决线程安全问题
  • 使用同步代码块

    • synchronized(临界资源对象){//原子操作}//(给临界资源加锁)
    public class Sell2 {
    
        public static void main(String[] args) {
            /*车站四个窗口共卖100张票*/
            MyRunnable2 runnable = new MyRunnable2();
            Thread thread1 = new Thread(runnable,"窗口一");//第二个参数,是线程的名字
            Thread thread2 = new Thread(runnable,"窗口二");//第二个参数,是线程的名字
            Thread thread3 = new Thread(runnable,"窗口三");//第二个参数,是线程的名字
            Thread thread4 = new Thread(runnable,"窗口四");//第二个参数,是线程的名字
    
            //开启线程
            thread1.start();
            thread2.start();
            thread3.start();
            thread4.start();
    
    
        }
    
    }
    
    class MyRunnable2 implements Runnable {
        int size = 100;
    
        @Override
        public void run() {
    
    
            while (size>0){
                try {
                    Thread.sleep(100);//让线程睡眠100毫秒
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                synchronized (this){
    
                    if(size>0){
                        size--;
                        System.out.println(Thread.currentThread().getName() + "已售出一张票,剩余票数为" + size);
                    }
                }
    
            }
    
        }
    
    }
    

确保size>0 和 size–,是一个原子操作

  • 使用同步方法

    • synchronized 返回值类型 方法名称(形参列表){//原子操作}//对当前对象(this)加锁
    public class Sell {
    
        public static void main(String[] args) {
            /*车站四个窗口共卖100张票*/
            MyRunnable runnable = new MyRunnable();
            Thread thread1 = new Thread(runnable,"窗口一");//第二个参数,是线程的名字
            Thread thread2 = new Thread(runnable,"窗口二");//第二个参数,是线程的名字
            Thread thread3 = new Thread(runnable,"窗口三");//第二个参数,是线程的名字
            Thread thread4 = new Thread(runnable,"窗口四");//第二个参数,是线程的名字
    
            //开启线程
            thread1.start();
            thread2.start();
            thread3.start();
            thread4.start();
    
    
        }
    
    }
    class MyRunnable implements Runnable {
        int size = 100;
    
        @Override
        public void run() {
    
    
            while (size>0){
                try {
                    Thread.sleep(100);//让线程睡眠100毫秒
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                sell();
            }
    
        }
    
        private synchronized void sell() {
    
            if(size>0){
                size--;
                System.out.println(Thread.currentThread().getName() + "已售出一张票,剩余票数为" + size);
            }
        }
    
    
    }
    

不能 将while放入synchronized 修饰的方法里面,会导致只有一个线程工作

  • 使用Lock

    Lock没有自动获取锁和自动释放锁概念

    使用void lock();//获取锁 void unlock();//释放锁

    import java.util.concurrent.locks.Lock;
    import java.util.concurrent.locks.ReentrantLock;
    
    public class Sell1 {
    
        public static void main(String[] args) {
            /*车站四个窗口共卖100张票*/
            MyRunnable1 runnable = new MyRunnable1();
            Thread thread1 = new Thread(runnable,"窗口一");//第二个参数,是线程的名字
            Thread thread2 = new Thread(runnable,"窗口二");//第二个参数,是线程的名字
            Thread thread3 = new Thread(runnable,"窗口三");//第二个参数,是线程的名字
            Thread thread4 = new Thread(runnable,"窗口四");//第二个参数,是线程的名字
    
            //开启线程
            thread1.start();
            thread2.start();
            thread3.start();
            thread4.start();
    
    
        }
    
    }
    
    class MyRunnable1 implements Runnable {
        Lock lock = new ReentrantLock();
        int size = 100;
    
    
        @Override
        public void run() {
    
    
            while (size>0){
                try {
                    Thread.sleep(100);//让线程睡眠100毫秒
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                lock.lock();//获取锁
                if(size>0){
                    size--;
                    System.out.println(Thread.currentThread().getName() + "已售出一张票,剩余票数为" + size);
                }
                lock.unlock();//释放锁
            }
    
        }
    }
    
    • Lock常见的方法
    方法说明
    void lock();获取锁,锁被占用,则等待 会阻塞
    boolean tryLock();尝试获取锁,成功返回true,失败false,不阻塞
    void unlock();释放锁
  • Lock与synchronized比较

    synchronizedlock
    实现jvm实现java代码实现
    释放锁的时机1.加锁的代码块执行完毕
    2.异常
    finally中释放锁
    线程是否一直等待一直等待尝试获取锁,非一直等待
    类型可重入,不可中断,非公平可重入,可中断,可公平
    方式悲观锁悲观锁 底层使用了cas
  • 补充

    什么是重入锁

    重入锁,就是指的是以线程为单位,当一个线程获取对象锁之后,这个线程可以再次获取本对象上的锁,而其他的线程是不可以的。 (防止死锁)

    import java.util.concurrent.locks.Lock;
    import java.util.concurrent.locks.ReentrantLock;
    
    public class RepetitionLock {
    
       public static void main(String[] args) {
           Lock lock = new ReentrantLock();
           new Thread(new Runnable() {
               @Override
               public void run() {
                   lock.lock();//获取锁
                   System.out.println("第一此获取锁");
                       lock.lock();//再次获取同一个锁对象
                       System.out.println("第二此获取锁");
                       lock.unlock();//释放锁
                       System.out.println("释放第二次获取的锁");
                   lock.unlock();//释放锁
                   System.out.println("释放第一次获取的锁");
    
               }
           }).start();
    
       }
    }
    

死锁

一个简单的死锁案例

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

public class MyDeadlock {
    public static void main(String[] args) {
        Lock leftLock = new ReentrantLock();//左锁
        Lock rightLock = new ReentrantLock();//右锁

        new Thread(new Runnable() {
            @Override
            public void run() {
                leftLock.lock();//获取左锁
                try {
                    Thread.sleep(10);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(Thread.currentThread().getName()+"获取到了左锁");
                rightLock.lock();//获取右锁
                System.out.println(Thread.currentThread().getName()+"获取到了右锁");
                rightLock.unlock();
                leftLock.unlock();
            }
        }).start();

        new Thread(new Runnable() {
            @Override
            public void run() {
                rightLock.lock();//获取右锁
                try {
                    Thread.sleep(10);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(Thread.currentThread().getName()+"获取到了右锁");
                leftLock.lock();//获取左锁
                System.out.println(Thread.currentThread().getName()+"获取到了左锁");
                leftLock.unlock();
                rightLock.unlock();

            }
        }).start();

    }
}

运行结果:一直在运行

程序卡死了





Lock 在这里知识简单的说了一下,大家要是对Lock感兴趣的话可以在多查查这方面的资料

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值