Java多线程(一)

Java多线程(一):
https://blog.csdn.net/Veer_c/article/details/103842078
Java多线程(二):
https://blog.csdn.net/Veer_c/article/details/103842263
Java多线程(三):
https://blog.csdn.net/Veer_c/article/details/103842317
Java多线程(四):
https://blog.csdn.net/Veer_c/article/details/103842602
java

多线程

线程是依赖于进程而存在的。
进程:正在运行的应用程序,每一正在运行的程序都会对应一个进程。
线程:进程的执行路径,执行单元
单线程和多线程的区别:
好比说有以下代码:web

public class Test {
    public static void main(String[] args) {
        代码1;
        show1();
        代码2;
        show2();
        代码3...
    }
    public static void show1(){
         代码11;
         代码12;
    }
    public staic void show2(){
    代码22;
    代码23;
    }
}

在这里插入图片描述
在这里插入图片描述
在代码执行的时候,因为多线程的存在,效率会很高
多线程的两种方案:
(1)继承Thread类:安全

public class MyThread extends Thread{
    //1.继承Thread类
    //2.重写run方法,重写run方法中的代码以后,当咱们启动了这个线程以后,咱们的这个线程就会执行run方法中的代码
    @Override
    public void run() {
        //需求:开启该线程以后,执行一个for循环
        for (int i = 0; i < 10; i++) {
            System.out.println(i);
        }
    }
}

(2)实现Runable接口:多线程

public class MyThread implements Runnable{
//实现runnable接口
    @Override
    public void run() {
        //启动该线程对象以后,须要执行的代码
        for (int i = 0; i < 10; i++) {
            System.out.println(i);
        }   
    }
}

多线程的问题:

启动线程咱们能够利用线程对象调用start()方法
start()和run()的区别
start()是在启动线程后执行run方法里里面的代码,而直接利用调用run()方法来执行方法,是在主线程里面运行该方法
start():1.开启线程 2.执行run()方法里面的代码
run():执行的是线程里面执行的代码,并不会开启线程
为何要重写run()
由于每一个线程须要执行的代码都是都是不同的,
咱们须要将每一个线程本身独立执行的代码写到run()方法中执行
线程不能够被屡次启动,不然会出现:java.lang.IllegalThreadStateException
由于在执行同一个线程的时候,一个线程刚开始执行,可是同时又从新开始,因此会抛出异常;
注意:若是是俩个不一样的线程,能够同时执行ide

public class MyThread extends Thread{
    @Override
    public void run() {
        for (int i = 0; i < 100; i++) {
            System.out.println(i);
        }
    }
}
public class Test {
    public static void main(String[] args) {
        //B:start()和run()的区别
        // 建立一个线程独享
        MyThread mt = new MyThread();
        MyThread mt2 = new MyThread();
        //mt.start();//1.首先开启了一个独立的线程 2.让这个独立的线程执行run方法中的代码
        System.out.println("--------");
        //mt.run();//1.普通的创对象,调方法 2.代码实在主线程执行,不会开辟新线程
        //D:线程能够屡次启动吗
        mt.start();
        //mt2.start();//若是是不一样的线程对象,是能够同时开启的
        //mt.start();//线程是不能屡次启动的
    }
}

线程的调度和控制
线程休眠(Thread.sleep(毫秒值))
线程名称(setName(),getName();)
线程的调度及优先级setPriority(10)(注意默认值是5,区间在1-10之间)svg

线程优先级:
当咱们多个线程开始运行的时候,线程会枪战CPU的执行权,线程优先级就是你抢到CPU执行权的几率。测试

public class MyThread extends Thread{
    @Override
    public void run() {
        for (int i = 0; i < 100; i++) {
            //线程休眠(Thread.sleep(毫秒值))
            try {
                Thread.sleep(1000);//在此处出现的异常咱们只能抓取,不能抛出
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
            //获取执行线程的姓名
            System.out.println(this.getName()+i);
        }
    }
}
public class Test {
    public static void main(String[] args) {
        //线程名称(setName(),getName();)
        //建立3个线程对象
        MyThread t1 = new MyThread();
        MyThread t2 = new MyThread();
        MyThread t3 = new MyThread();
        //给三个线程设置姓名
        t1.setName("刘备");
        t2.setName("张飞");
        t3.setName("关羽");
        //设置线程的优先级
        //线程的调度及优先级setPriority(10)(注意默认值是5,区间在1-10之间)
        //t1.setPriority(100);//设置的区间必须在1-10之间
        t1.setPriority(10);
        //开启线程
        t1.start();
        t2.start();
        t3.start(); 
    }
}

多线程案例
1.继承Thread卖票。this

public class MyThread extends Thread{
    //共有100张票,将ticket改成静态以后,被类的全部对象所共享
    static int ticket = 100;
    @Override
    public void run() {
        //用一个while true循环模拟三个窗口一直处于打开的状态
        while (true) {
            //只有当ticket>0的时候,才能够出售票
            if (ticket>0) {
                System.out.println(getName()+"正在出售第:"+ticket--+"张票");
            }
        }
    }
}
//5.1继承Thread卖票
public class Test {
    public static void main(String[] args) {
        //建立三个线程模拟三个售票窗口
        MyThread mt1 = new MyThread();
        MyThread mt2 = new MyThread();
        MyThread mt3 = new MyThread();
        //给线程设置名称
        mt1.setName("窗口一");
        mt2.setName("窗口二");
        mt3.setName("窗口三");
        //启动线程,开启售票
        mt1.start();
        mt2.start();
        mt3.start();
    }
}

2.实现Runnable卖票:spa

public class MyThread implements Runnable{
    int ticket = 100;
    @Override
    public void run() {
        while (true) {
            if (ticket>0) {                
            System.out.println(Thread.currentThread().getName()+"正在出售第:"+ticket--+"张票");
            }
        }
    }
}
public class Test {
    public static void main(String[] args) {
        //建立MyThread这个对象
        MyThread mt = new MyThread();
        //建立3个窗口
        Thread t1 = new Thread(mt);
        Thread t2 = new Thread(mt);
        Thread t3 = new Thread(mt);
        //给线程对象设置名称
        t1.setName("窗口一");
        t2.setName("窗口二");
        t3.setName("窗口三");
        //启动窗口开始售票
        t1.start();
        t2.start();
        t3.start();
    }
}

注意:在实现runnable借口的时候,咱们能够不用把ticket设置成static,由于在实现runnable的时候,咱们原本就建立了一个实现类对象,而后将是实现类对象转换成三个不一样的thread对象,因此咱们的ticket本开就是共享的。.net

在实际生活中,咱们买票的时候跟定会有时间上的延迟,因此咱们给线程中加入延迟,按照真实的情景加入了延迟,却发现出现了这样的两个问题:
相同的票卖了屡次:CPU的一次操做必须是原子性的(操做是CPU执行一次就能够直接完成的)
出现了负数的票:随机性和延迟致使的
出现上面的问题称为线程安全问题。

出现多线程安全问题的条件:
是不是多线程环境
是否有共享数据
是否有多条语句操做共享数据

如何解决多线程安全问题
线程安全执行效率会有所下降
同步代码块:
synchronized(对象) {
须要被同步的代码。
}

需求:
1.测试不是同一把锁的时候线程安全吗?
答:当咱们每一个线程进入程序的时候,咱们都会出建立一个把锁,这样执行仍是会出错的。
2.若是是同一把锁线程安全吗?
若是咱们利用同一把锁,则不会出现上面的问题,线程安全
1.synchronized的对象是什么
答:任意对象 ,至关因而一把锁,只要线程进去就把锁锁上
2.须要同步的代码?
答:被线程执行的代码

锁对象问题:
同步代码块(定义一个抽象类,里面专门定义一个锁),任意对象

public class MyThread implements Runnable{
    //定义100张票
    int ticket = 100;
    Object obj = new Object();
    @Override
    public void run() {
        while (true) {
            //同步代码块
            //synchronized (new Object()) {//t1,t2,t3三个线程不共享同一把锁每一个线程都有本身的议案锁
            synchronized (obj) {//这样3个线程才能够共享同一把锁 
            if (ticket>0) {
                    //考虑到实际的生活中,咱们须要给每个线程加入必定的延迟,模拟一下这种效果
                    try {
                        Thread.sleep(100);
                        /** * 分析:为何会出现两张100张票 * t1抢占到cpu的执行权,此时ticket=100,可是此刻休眠了 * 此时被t2抢占到了cpu的执行权,此时ticket=100, * t1,t2分别睡了100毫秒以后,分别醒来了。。 * t1此时出售第100张票 * t2此时出售第100张票 */
                        /** * 分析:为何会出现0张票和-1张票 * 假设此时票池中仅剩1张票了, * t1进来休眠了 * t2进来休眠了 * t3进来休眠了 */
                    } catch (InterruptedException e) {
                        // TODO Auto-generated catch block
                        e.printStackTrace();
                    }
                 System.out.println(Thread.currentThread().getName()+"正在出售第:"+ticket--+"张票");
                    /* t1醒来,出售的是第1张票,此时tickt=0 * t2醒来,出售第0张票,此时ticket=-1 * t3醒来,出售第-1张票,此时ticket=-2 */

                    /* ticket--这个动做一共包含几步: * 1.打印出ticket此刻自己的值 * 2.ticket自减1 * 3.将自减以后的ticket的最新的值赋值给变量ticket */
                }
            }
            //当被同步的代码执行完毕以后,t1手里拿着的obj这个锁才会被释放,
            //t1,t2,t3从新抢占cpu的执行权,谁抢到了继续拿着obj这个锁,执行同步代码块中的内容
        }
    }

同步方法(仅适用于实现runable接口)
public synchronized void sellTicket(){同步代码}
//this

private static synchronized void sellTicket() { 
    if (ticket>0) {
            //考虑到实际的生活中,咱们须要给每个线程加入必定的延迟,模拟一下这种效果
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }         System.out.println(Thread.currentThread().getName()+"正在出售第:"+ticket--+"张票");
    }
}
}

将synchronized关键字加到方法上

静态同步方法
类的字节码对象
public static synchronized void sellTicket() {
须要同步的代码
}

匿名内部类的方式使用多线程

new Thread() {
            public void run() {
                ...
            }
        }.start();
new Thread(new Runnable(){
            public void run() {
                ...
            }
        }).start();

利用匿名内部类启动多线程

public void run() {
        while (true) {
            if (x%2==0) {
                synchronized (MyThread.class) {//这样3个线程才能够共享同一把锁 
                    if (ticket>0) {
                            //考虑到实际的生活中,咱们须要给每个线程加入必定的延迟,模拟一下这种效果
                            try {
                                Thread.sleep(100);
                            } catch (InterruptedException e) {
                                // TODO Auto-generated catch block
                                e.printStackTrace();
                            }                  System.out.println(Thread.currentThread().getName()+"正在出售第:"+ticket--+"张票");
            }
         }

Java多线程(一):
https://blog.csdn.net/Veer_c/article/details/103842078
Java多线程(二):
https://blog.csdn.net/Veer_c/article/details/103842263
Java多线程(三):
https://blog.csdn.net/Veer_c/article/details/103842317
Java多线程(四): https://blog.csdn.net/Veer_c/article/details/103842602