线程的创建!收藏起来

1.  什么是线程

线程。又称轻量级进程。线程是进程中的一条执行路径,也是CPU的基本调单位。

若一个程序可同一时间执行多个线程,就是支持多线程的。

一个进程由一个或多个线程组成,彼此间完成不同的工作,同时执行,称为多线程。

为什么使用多线程 

 充分利用cpu,提高程序的效率

2.  java如何实现多线程 

提供了三种实现多线程的方式:

  • 继承Thread类
  • 实现Runnable接口
  • 实现Callable接口

 2.1  继承Thread类

语法:

public class MyThread extends Thread {
    @Override
    public void run() {
        for (int a=0;a<20;a++){
            //getName 获取线程名,默认的名称Thread-n,该方法必须在Thread类下才能使用
            //Thread.currentThread():获取当前正在执行的线程对象
            System.out.println(Thread.currentThread().getName()+"-------------------"+a);
        }
    }
}
public class TestT01 {
    public static void main(String[] args) {
        MyThread myThread = new MyThread();
        myThread.start();
        for (int a=0;a<20;a++){
            System.out.println(Thread.currentThread().getName()+"-------------------"+a);
        }
    }
}

 案例

public class SellTictet01 extends MyThread {
    private int tictets = 20;

    @Override
    public void run() {
        while (true) {
            if (tictets > 0) {
                tictets--;
                System.out.println(Thread.currentThread().getName() + "买了一张票,剩余" + tictets + "张");
            } else {
                System.out.println(Thread.currentThread().getName() + "卖完了");
                break;
            }
        }
    }
}
public class Stest01 {
    public static void main(String[] args) {
        SellTictet01 sellTictet1 = new SellTictet01();
        SellTictet01 sellTictet2 = new SellTictet01();
        SellTictet01 sellTictet3 = new SellTictet01();
        SellTictet01 sellTictet4 = new SellTictet01();
        Thread thread1 = new Thread(sellTictet1);
        Thread thread2 = new Thread(sellTictet2);
        Thread thread3 = new Thread(sellTictet3);
        Thread thread4 = new Thread(sellTictet4);
        thread1.start();
        thread2.start();
        thread3.start();
        thread4.start();
    }
}

2.2  实现Runnable接口

public class Myrunnable implements Runnable{
    @Override
    public void run() {
        for (int i=0;i<20;i++){
            System.out.println(Thread.currentThread().getName()+"===="+i);
        }
    }
}
public class TestR01 {
    public static void main(String[] args) {
        Myrunnable myrunnable = new Myrunnable();
        Thread thread = new Thread(myrunnable);
        thread.start();
        for (int i=0;i<20;i++){
            System.out.println(Thread.currentThread().getName()+"===="+i);
        }
    }
}

 案例

public class SellTictet02 implements Runnable{
    private int tict=100;
    @Override
    public void run() {
        while (true) {
            if (tict > 0) {
                try {
                    //睡眠
                    Thread.sleep(10);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                tict--;
                System.out.println(Thread.currentThread().getName() + "卖了一张票,剩余" + tict + "张");
            } else {
                break;
            }
        }
    }
}
public class Rtest01 {
    public static void main(String[] args) {
        SellTictet02 sellTictet = new SellTictet02();
        Thread thread1 = new Thread(sellTictet, "窗口A");
        Thread thread2 = new Thread(sellTictet, "窗口B");
        Thread thread3 = new Thread(sellTictet, "窗口C");
        Thread thread4 = new Thread(sellTictet, "窗口D");
        thread1.start();
        thread2.start();
        thread3.start();
        thread4.start();
    }
}

 分析出超卖和重卖现象。这是因为多个线程共享一个资源,导致线程安全隐患问题,后期解决线程安全问题。后期我们可以使用锁解决

两种方式继承Thread和实现Runnnable 哪种方式比较好

使用第二种,第二种的扩展性更好。

2.3  实现Callable接口 

public class MyCallable implements Callable {
    //1到100的和
    @Override
    public Object call() throws Exception {
        double sum=0;
        for (int i=0;i<=100;i++){
            sum+=i;
        }
        return sum;
    }
}
public class TestC01 {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        MyCallable myCallable = new MyCallable();
        FutureTask task = new FutureTask(myCallable);
        Thread thread = new Thread(task);
        thread.start();
        Object o = task.get();
        System.out.println(o);
    }
}

 3.Thread类中常用的一些方法

3.1 线程休眠方法-static void sleep

public class MyThread extends Thread {
    @Override
    public void run() {
        for (int a=0;a<20;a++){
            try {
                //休眠十毫秒
                Thread.sleep(10);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName()+"-------------------"+a);
        }
    }
}

3.2  yield 当前线程让出cpu-参与下次的竞争

3, 

public class MyThread extends Thread {
    @Override
    public void run() {
        for (int a=0;a<20;a++){
            //当前线程让出CPU参与下次竞争
            //使用yield线程出现交换执行的频率高了。
           Thread.yield();
            System.out.println(Thread.currentThread().getName()+"-------------------"+a);
        }
    }
}

3.3  join加入当前线程上

插入的线程执行完毕主线程才会执行

public class TestT01 {
    public static void main(String[] args) throws InterruptedException {
        MyThread t1 = new MyThread();
        t1.setName("线程A");
        MyThread t2 = new MyThread();
        t2.setName("线程B");
        t1.start();
        t2.start();
        t1.join();
        //t1加入主线程上,主线程需要等t1执行完毕后才会执行。
        for (int a=0;a<20;a++){
            System.out.println(Thread.currentThread().getName()+"-------------------"+a);
        }
    }
}

 3.4  setDaemon()设置线程为守护线程

当所有线程执行完毕后,守护线程也会终止

public class TestT01 {
    public static void main(String[] args) throws InterruptedException {
        MyThread t1 = new MyThread();
        t1.setName("线程A");
        t1.setDaemon(true);
        t1.start();
        for (int a=0;a<20;a++){
            System.out.println(Thread.currentThread().getName()+"-------------------"+a);
        }
    }
}

 JDK--默认就要有一个守护线程,GC垃圾回收

4.  解决线程安全问题 

4.1  什么情况下会出现线程安全问题

当多个线程操作同一个资源时,则出现线程安全问题。

4.2  java如何解决线程安全问题 

提高了两种方法:第一种:使用synchronized自动锁

                             第二种:使用Lock手动锁

使用锁相当于把原来的异步转化为同步操作

使用synchronized关键字解决

synchronized(共享锁对象){
   同步代码块

}

解决超卖现象 

public class SellTictet02 implements Runnable {
    private int tict = 1000;

    @Override
    public void run() {
        while (true) {
            synchronized (this) {
                if (tict > 0) {
                    try {
                        Thread.sleep(10);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    tict--;
                    System.out.println(Thread.currentThread().getName() + "卖了一张票,剩余" + tict + "张");
                } else {
                    break;
                }
            }
        }
    }
}

 使用Lock手动

public class SellTictet02 implements Runnable {
    private int tict = 1000;
    private Lock l = new ReentrantLock();

    @Override
    public void run() {
        while (true)
            try {
                //加锁
                l.lock();
                if (tict > 0) {
                    try {
                        Thread.sleep(10);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    tict--;
                    System.out.println(Thread.currentThread().getName() + "卖了一张票,剩余" + tict + "张");
                } else {
                    break;
                }
            }finally {
                //解锁
                l.unlock();
            }
    }
}

在这有一个问题(synchronized和Lock都有这个问题),如果执行卖一百张票,只有先开启的线程再卖, 但卖一千张,甚至一万张,四个线程才能交替执行,根据ai的回答是因为这个,我也不知道,大家姑且看一下:

 

synchronized和Lock 的区别

syn可以使用代码块和方法。自动加锁和释放锁。不会出现死锁问题。

lock它只能使用在代码块中。需要手动加锁和释放锁。如果不释放锁,死锁问题。灵活。它的释放锁必须放在finally

5.  死锁 

线程A拥有资源a,希望获得资源b,线程B拥有资源b,希望获得资源a。两个线程互相拥有对方希望获得的锁资源。可能会出现程序堵塞,从而造成死锁。

public class locka implements Runnable{
    @Override
    public void run() {
        synchronized ("locka"){
            System.out.println(Thread.currentThread().getName()+"拥有了锁a");
            synchronized ("lockb"){
                System.out.println(Thread.currentThread().getName()+"拥有了锁b");
                System.out.println(Thread.currentThread().getName()+"拥有了锁a和b");
            }
        }

    }
}

public class lockb implements Runnable{
    @Override
    public void run() {
        synchronized ("lockb"){
            System.out.println(Thread.currentThread().getName()+"拥有了锁b");
            synchronized ("locka"){
                System.out.println(Thread.currentThread().getName()+"拥有了锁a");
                System.out.println(Thread.currentThread().getName()+"拥有了锁b和锁a");
            }
        }
    }
}

public class test {
    public static void main(String[] args) {
        locka a = new locka();
        lockb b = new lockb();
        Thread t1 = new Thread(a);
        Thread t2 = new Thread(b);
        t1.setName("线程a");
        t2.setName("线程b");
        t1.start();
        t2.start();
    }
}

解决:

  1. 不要使用锁嵌套。
  2. 设置超过时间。 --Lock类中tryLock。

  3. 使用安全java.util.concurrent下的类

 6.  线程通信

wait方法和notify方法。

public class Mybank {
    //余额
    private double balance;
    //状态 true 有钱 false 没钱
    private boolean flag;
    //银行存钱
    public synchronized void cun(double money){
        if (flag==true){
            try {
                //进入等待队列,并且释放拥有的锁
                wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        //存钱
        balance += money;
        flag = true;
        //唤醒等等待队列中的某个线程
        notify();
        System.out.println(Thread.currentThread().getName()+"存钱成功,余额为:"+balance);
    }
    //银行取钱
    public synchronized void qu(double money){
        if (flag==false){
            try {
                wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        balance-=money;
        flag = false;
        notify();
        System.out.println(Thread.currentThread().getName()+"取钱成功,余额为:"+balance);
    }
}

//实现存钱
public class Cuntread extends Thread{
    private Mybank mybank;
    public Cuntread(Mybank mybank){
        this.mybank=mybank;
    }

    @Override
    public void run() {
        for (int i=0;i<10;i++){
            mybank.cun(1000);
        }
    }
}

//实现取钱
public class Qutread extends Thread {
    private Mybank mybank;

    public Qutread(Mybank mybank) {
        this.mybank = mybank;
    }

    public void run() {
        for (int i = 0; i < 10; i++) {
            mybank.qu(1000);
        }
    }
}

//测试
public class test {
    public static void main(String[] args) {
        Mybank mybank = new Mybank();
        Cuntread cuntread = new Cuntread(mybank);
        cuntread.setName("我");
        Qutread qutread = new Qutread(mybank);
        qutread.setName("你");
        cuntread.start();
        qutread.start();
    }
}

7.线程状态 

NEW                                        新建状态

RUNNABLE                             就绪状态和运行状态

BLOCKED                                堵塞状态

WATTING                                 等待状态

TIMED_WATTING                    时间等待

TERMINATED                          终止

通过调用不同的方法相互转换 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值