线程入门

线程入门

1.创建线程的三种方式

1.1 继承Thread类,重写run()

public class ExtendThread extends Thread {  //继承Thread类

    @Override
    public void run() {						//重写run()方法, run()方法中包含线程的执行体
        for (int i = 0; i < 1000; i++) {
            System.out.println("画圆");
        }
    }


    public static void main(String[] args) {
        //创建实例,start()使线程进入就绪状态
        ExtendThread extendThread = new ExtendThread();
        extendThread.start();

        //main线程,用于与创建的线程对比
        for (int i = 0; i < 1000; i++) {
            System.out.println("画方");
        }
    }
}

1.2 实现Runnable接口,重写run()

public class ImplementsRunnable implements Runnable {
    //重写run(), run()方法中包含线程的执行体
    @Override
    public void run() {
        //Thread.sleep()使线程休眠,模拟延时
        try {
            Thread.sleep(500);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        for (int i = 0; i < 20; i++) {
            System.out.println("画圆");
        }
    }

    public static void main(String[] args) {
        //创建实现接口的实例
        ImplementsRunnable implementsRunnable = new ImplementsRunnable();

        //创建线程的语法	 
        //Thread thread = new Thread(runnable);
        //start()使线程进入就绪状态
		//thread.start();
        //简化为下
        new Thread((implementsRunnable)).start();

         //main线程,用于与创建的线程对比
        for (int i = 0; i < 20; i++) {
            System.out.println("画方块");
        }
    }
}
应用: 龟兔赛跑
//龟兔赛跑
public class Race implements Runnable {
    static String Winner = null;

    @Override
    public void run() {
        for (int i = 0; i <= 100; i++) {
            System.out.println(Thread.currentThread().getName() + " 跑了 " + i + " 步");

            //跑到终点,产生胜者,结束循环
            if (gameOver(i)) {
                Winner = Thread.currentThread().getName();
                System.out.println(Winner+" 赢了");
                break;
            }
            //已存在胜者,结束循环
            if (Winner != null) {
                break;
            }
        }
    }

    private boolean gameOver(int step) {
        if (step >= 100) {
            return true;
        } else {
            return false;
        }
    }

    public static void main(String[] args) {
        Race race = new Race();

        new Thread(race,"兔子").start();
        new Thread(race,"乌龟").start();
    }
}

1.3 实现Callable接口,重写call()

public class ImplementsCallable implements Callable<Integer> { //Callable后加上泛型
    //该 call() 方法将作为线程执行体,并且有返回值。
    @Override
    public Integer call() throws Exception {
        int i;
        for (i = 0; i < 10; i++) {
            System.out.println(Thread.currentThread().getName()+" "+i);
        }
        return i;
    }

    public static void main(String[] args) {
        //创建 Callable接口实现类的实例
        ImplementsCallable ctt = new ImplementsCallable();
        //使用 FutureTask 类来包装 Callable 对象,该 FutureTask 对象封装了该 Callable 对象的 call() 方法的返回值
        //类似C++中容器
        FutureTask<Integer> ft = new FutureTask<>(ctt);

        for (int i = 0; i < 100; i++) {
            System.out.println(Thread.currentThread().getName() + " 的循环变量i的值" + i);
            if (i == 20) {
                //使用 FutureTask类的对象作为 Thread 对象的 target 创建并启动新线程
                new Thread(ft, "有返回值的线程").start();
            }
        }
        //调用 FutureTask 对象的 get() 方法来获得子线程执行结束后的返回值
        try {
            System.out.println("子线程的返回值:" + ft.get());
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

2.操作线程

sleep()

Thread.sleep(millis);

用sleep()模拟网络延时,放大问题的发生可能性

每个对象都有一把锁,sleep()不会释放锁,类似于抱着锁睡觉

//应用: 模拟倒计时
public class CountDown {
    public static void main(String[] args) {
        count(10);

    }

    static void count(int time) {
        for (; time >= 0; time--) {
            try {
                Thread.sleep(1000);  // 模拟延时, 1000ms
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(time + " second left...");
        }
    }
}

stop()

在Runnable的实现类中定义标志位, 并定义一个用于转换标志位停止线程的stop()方法, 在线程体run()中通过判断标志位状态来控制线程

public class ThreadStop implements Runnable {

    //定义标志位
    boolean flag = true;

    //线程体
    @Override
    public void run() {
        //线程体使用标志位控制
        while (flag) {
            //循环输出,直至主线程中for循环到第80次
            System.out.println("Thread is running...");
        }
    }

    //定义一个公开的stop()方法,用于转换标志位,停止线程
    public void stop() {
        this.flag = false;
        System.out.println("分线程停止了");
    }

    public static void main(String[] args) {

        ThreadStop runnable = new ThreadStop();
        Thread thread = new Thread(runnable);
        thread.start();

        //主线程
        for (int i = 0; i < 100; i++) {
            System.out.println("main is running..." +i);
            if (i == 80) {
                //stop方法,转换标志位
                runnable.stop();
            }
        }
    }
}

getState()

线程的六种基本状态:

新建状态(New) 即用new关键字新建一个线程,这个线程就处于新建状态

就绪状态(Runnable) 操作系统中的就绪和运行两种状态,在Java中统称为RUNNABLE。

等待状态(WAITING) 进入该状态表示当前线程需要等待其他线程做出一些的特定的动作(通知或中断)。

超时等待状态(TIMED_WAITING) ** 区别于WAITING,它可以在指定的时间**自行返回。

阻塞状态(Blocked) 阻塞状态表示线程正等待监视器锁,而陷入的状态。

消亡状态(Dead)线程的终止,表示线程已经执行完毕。

使用getState()方法能够获得线程当前所处的状态

public class ThreadState {
    public static void main(String[] args) throws InterruptedException {
        //使用lambda表达式,创建Thread实例,重写run()方法
        Thread thread = new Thread(() -> {
            for (int i = 0; i < 50; i++) {
                if (i % 5 == 0) {
                    try {
                        Thread.sleep(100);  //3.TIMED_WAITING
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
            System.out.println("---------------------");
            //4.TERMINATED
        });

        System.out.println("thread.getState() = " + thread.getState()); //1.NEW

        thread.start(); //2.RUNNABLE
        while (thread.getState() != Thread.State.TERMINATED) {
            System.out.println("thread.getState() = " + thread.getState());

            Thread.sleep(100);
        }
    }
}

setPriority()

使用setPriority()方法来设置线程的优先级

setPriority()方法的参数为可以为Thread的枚举类型属性, 或是1~10的整数, 数字越大优先级越高

public class ThreadPriority {
    public static void main(String[] args) {
        //主线程默认优先级
        System.out.println(Thread.currentThread().getName()+" --> "+ Thread.currentThread().getPriority());

        MyPriority myPriority = new MyPriority();
        
        Thread t1 = new Thread(myPriority, "t1");
        Thread t2 = new Thread(myPriority, "t2");
        Thread t3 = new Thread(myPriority, "t3");
        Thread t4 = new Thread(myPriority, "t4");

        //先设置优先级再启动
        t1.start();

        t2.setPriority(1);
        t2.start();

        t3.setPriority(4);
        t3.start();

        t4.setPriority(Thread.MAX_PRIORITY);
        t4.start();
    }
}

class MyPriority implements Runnable {

    @Override
    public void run() {
        //执行体打印当前线程名和优先级
        System.out.println(Thread.currentThread().getName()+" --> "+ Thread.currentThread().getPriority());
    }
}

join()

join()调用合并线程,其他线程阻塞, 待调用join的线程执行完毕后,再执行其他线程,类似插队

public class ThreadJoin implements Runnable {
    public static void main(String[] args) throws InterruptedException {
        ThreadJoin threadJoin = new ThreadJoin();
        Thread thread = new Thread(threadJoin, "分线程");
        thread.start();

        for (int i = 0; i < 200; i++) {
            //main线程的i=100时,分线程插入,main线程阻塞
            if (i == 100) thread.join();
            //分线程完全结束后,main线程继续执行
            System.out.println("main is running..." + i);
        }
    }

    @Override
    public void run() {
        for (int i = 0; i < 50; i++) {
            System.out.println(Thread.currentThread().getName() + " is running..." + i);
        }
    }
}

yield()

yield()使得线程礼让,让运行状态的线程重新进入就绪状态, 等待CPU的调度

public class ThreadYield {
    public static void main(String[] args) {
        MyYield myYield = new MyYield();

        new Thread(myYield,"Apple").start();
        new Thread(myYield,"Google").start();
    }
}

class MyYield implements Runnable {

    @Override
    //首次运行时从头开始执行run()
    public void run() {
        System.out.println(Thread.currentThread().getName()+"线程开始执行。。。");
        
        //线程礼让, 暂停线程, 重新进入就绪状态等待调度
        Thread.yield();
        
        //礼让后继续执行yield()语句后的代码
        System.out.println(Thread.currentThread().getName()+"线程停止执行。。。");
    }
}

setDaemon()

线程分为用户线程(如main…)和守护线程(如垃圾回收机制gc…)

虚拟机不必等待守护线程结束才结束进程

public class ThreadDaemon {
    public static void main(String[] args) {
        God god = new God();
        People people = new People();

        Thread thread_god = new Thread(god);
        thread_god.setDaemon(true);//通过setDaemon()方法将thread_god线程设为守护线程

        Thread thread_people = new Thread(people);
        
        thread_god.start();
        thread_people.start();

    }
}

class God implements Runnable {
    @Override
    public void run() {
        //发现死循环并不会一直执行,到用户线程结束即结束
        while (true) {
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("god bless you!");
        }
    }
}

class People implements Runnable {
    @Override
    public void run() {
        for (int i = 0; i < 100; i++) {
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("I am happy!");
        }
        System.out.println("Goodbye World!");
    }
}

3.线程池

创建线程池使用Executors.newFixedThreadPool(nThread) 静态工厂方法,参数为池子大小

public class ThreadPool {
    public static void main(String[] args) {
        //1.创建服务,创建线程池
        //  Executors.newFixedThreadPool() 静态工厂方法,参数为池子大小
        ExecutorService service = Executors.newFixedThreadPool(10);

        //2.将线程放入线程池
        service.execute(new MyRunnable());
        service.execute(new MyRunnable());
        service.execute(new MyRunnable());
        service.execute(new MyRunnable());

        //3.关闭线程池
        service.shutdown();
    }
}

class MyRunnable implements Runnable {
    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName() + " is running...");
    }
}

4.锁

在并发编程中存在线程安全问题,主要原因有:1.存在共享数据 2.多线程共同操作共享数据。

当多线程同时操作同一个对象, 会导致数据紊乱, 线程不安全, 故引入

为了解决这些问题, 引入了SynchronizedReentrantLock两种方式

Synchronized

语法为: synchronized (需要上锁的对象) { 代码块 }

public class BuyTicket implements Runnable {

    //票数
    private Integer ticketNum = 20;

    @Override
    public void run() {
        //循环抢票
        while (true) {
            //引入锁
            //使用synchronized锁上ticketNum,使得多个线程不能同时对ticketNum进行操作
            //执行完一次代码块中的内容就释放锁
            synchronized (ticketNum) {
                if (ticketNum <= 0) {
                    break;
                }
                System.out.println(Thread.currentThread().getName() + "拿到了第" + (ticketNum--) + "张票");
            }
        }
    }

    public static void main(String[] args) {
        //创建Runnable对象
        BuyTicket buyTicket = new BuyTicket();

        //三个线程操作一个Runnable对象
        new Thread(buyTicket, "小红").start();
        new Thread(buyTicket, "小黄").start();
        new Thread(buyTicket, "小蓝").start();
    }
}

ReentrantLock

public class LockDemo {
    public static void main(String[] args) {
        //创建Runnable对象
        Ticket ticket = new Ticket();

        //三个线程操作一个Runnable对象
        new Thread(ticket, "小红").start();
        new Thread(ticket, "小黄").start();
        new Thread(ticket, "小蓝").start();
    }
}

class Ticket implements Runnable {
    private int number = 1000;

    //定义ReentrantLock类的Lock锁
    private ReentrantLock lock = new ReentrantLock();

    @Override
    public void run() {

        while (true) {
            try {
                //lock()加锁
                lock.lock();

                //加锁后判断,不能让多个线程同时判断
                if (number <= 0) {
                    System.out.println(Thread.currentThread().getName() + "--> 没票了");
                    break;
                }
                System.out.println(Thread.currentThread().getName() + " 拿到了 " + (number--) + " 号票");

                //unlock()解锁
                lock.unlock();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }
}

5.死锁

多个线程互相持有对方所需要的资源,相互等待,形成僵持

public class DeadLock {
    public static void main(String[] args) {
        Thread lea = new Thread(new Girl("Lea", 1));
        Thread anna = new Thread(new Girl("Anna", 0));

        lea.start();
        anna.start();
    }
}
//镜子类
class Mirror {
}
//口红类
class LipStick {
}

class Girl implements Runnable {
    private String name;
    private int choice;

    //mirror,lipStick设为static,表示只有一个镜子和口红
    static Mirror mirror = new Mirror();
    static LipStick lipStick = new LipStick();

	//构造器  choice=0或1
    public Girl(String name, int choice) {
        this.name = name;
        this.choice = choice;
    }

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

    void makeup() {
        //choice为0,先获得镜子的锁,再等待拿口红的锁
        if (choice == 0) {
            synchronized (mirror) {//获得镜子锁
                System.out.println(this.name + " 获得了镜子的锁");
                try {
                    Thread.sleep(100);//模拟延时,保证另一个人拿到了另一件物品的锁
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                //锁中拿锁,造成死锁
                synchronized (lipStick) {//获得口红锁
                    System.out.println(this.name + " 获得了口红的锁");
                }
            }
        } else {
            synchronized (lipStick) {//获得口红锁
                System.out.println(this.name + " 获得了口红的锁");
                try {
                    Thread.sleep(200);//模拟延时,保证另一个人拿到了另一件物品的锁
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                //锁中拿锁,造成死锁
                synchronized (mirror) {//获得镜子锁
                    System.out.println(this.name + " 获得了镜子的锁");
                }
            }
        }
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值