[重走长征路]线程

创建多线程方式一:集成Thread类
1.创建一个继承自Thread的子类
2.重写Thread中的run方法 线程要做的事情声明在run中
3.创建Thread子类的对象
4.通过此对象调用start方法 start有两个作用 1、启动线程2、调用线程中的run方法
class MyThread extends Thread{
    @Override
    public void run() {
        for (int i = 0; i < 100; i++) {
            if (i%2==0){
                System.out.println(Thread.currentThread().getName()+":"+i);
        }
    }
}

public static class ThreadTest {
    public static void main(String[] args) {
        MyThread myThread = new MyThread();
        //问题一
        myThread.start();
        //不能直接通过调用线程的run方法  启动线程
//        myThread.run();

        //问题二:在启动一个线程,继续遍历一百以内都数
//        myThread.start(); 会报错 会出现线程状态异常
        //要想创建多个线程 就需要new多个对象 一个对象的start方法只能执行一次
        MyThread myThread1 =new MyThread();
        myThread1.start();


    }
}

}
创建多线程方式2
1、创建一个实现Runnable接口的类
2、实现类中实现runnable中的抽象方法
3、创建实现类对象
4、将此对象作为参数传递到Thread类的构造器中,创建Thread类的对象
5、通过Thread类的对象,调用start()
 比较两种创建方式:
 开发过程中优先使用runnable方法创建线程
 原因:1、实现的方式没有类中单继承的局限性
       2、实现方式更适合来处理多个线程有共享数据的场景情况
 联系:
 Thead本身也是实现的runnable接口,
 相同点:都需要重写run方法,将线程执行的逻辑声明在run中。
class MThread implements Runnable{


    @Override
    public void run() {
        for (int i = 0; i < 100; i++) {
            if (i%2==0){
                System.out.println(Thread.currentThread().getName()+":"+i);

            }
        }
    }
}

public class ThreadTest1 {
    public static void main(String[] args) {
        //创建实现类对象
        MThread mThread = new MThread();
        //将此对象作为参数传递到Thread类的构造器中,创建Thread对象
        Thread thread = new Thread(mThread);
        thread.start();//1、启动线程2、调用当前线程的run方法---》d调用的Runnable类型的target的run

        //再起一个线程 再次遍历
        Thread thread1 = new Thread(mThread);
        thread1.start();
    }
}
/**
 * 创建三个接口卖票
 */


class Window extends Thread{
    private static int ticket =100;
    @Override
    public void run() {

        while (true){
            if (ticket>0){
                System.out.println(getName()+":卖票,票号为:"+ticket);
                ticket--;
            }else {
                break;
            }
        }

    }
}
public class WindosTest {
    public static void main(String[] args) {
        Window window1 = new Window();
        Window window2 = new Window();
        Window window3 = new Window();
        window1.start();
        window2.start();
        window3.start();
    }
}
此例子有安全的问题
1、买票的过程中出现 重票、错票、---》出现线程安全问题。
2、出现问题的原因是当某个线程操作车票的过程中,尚未操作完成时,其他的线程参与进来,也操作车票
3、当一个线程操作共享数据的时候,其他线程不能参与进来 知道线程a操作完rticket,其他线程才可以参与进来,
即使a线程出现阻塞,也不能被改变。
4、在Java中通过同步机制,解决线程安全问题。
方式一:同步代码块
synchronized(同步监视器){
    //需要同步的代码
}
说明:1/操作共享数据的代码 ,即是需要被同步的代码
     2/共享数据:多个线程共同操作的变量。比如ticket就是共享数据
     3/同步监视器:俗称锁。任何一个类的对象 都可以充当锁
       隐藏要求:要求多个线程要共用同一把锁。
       补充 在实现Runnable接口创建多线程的方式中,我们可以考虑使用this
方式二:同步方法
     如果操作共享数据的代码完整的声明在一个方法中,我们不妨将此方法同步声明。
5、使用同步的方式,解决了线程安全的问题---好处
   操作同步代码时,只能有一个线程参与,其他线程等待。相当于是单线程过程。效率低---缺点
class Window1 implements Runnable{
    
//    private static int ticket= 100;
    private  int ticket= 100;
    Object obj=new Object();

    @Override
    public void run() {
        while (true){

//            synchronized(obj){
            synchronized (this){//直接调用window1的对象 即可 但是继承类的时候不行
                if (ticket>0){
                    try {
                        Thread.sleep(100);
                    } catch (InterruptedException e) {
                        throw new RuntimeException(e);
                    }
                    System.out.println(Thread.currentThread().getName()+":卖票,票号为:"+ticket);
                    ticket--;
                }else {
                    break;
                }
            }


        }
    }
}

public class WindowTest1 {
    public static void main(String[] args) {
        //关键字不需要用static修饰 因为就创建了一个window1对象

        Window1 window1 = new Window1();
        Thread thread = new Thread(window1);
        Thread thread1 = new Thread(window1);
        Thread thread2 = new Thread(window1);
        thread.start();
        thread1.start();
        thread2.start();

    }
}
使用同步代码块,解决继承Thread的线程安全问题
在继承Thread类创建多线程的方式中,慎用this充当同步监视器,
class Window2 extends Thread{
    private static int ticket =100;

    //定义锁 但是在代码中new了三个window2这个类 锁不唯一了
    private static Object obj = new Object();
    @Override
    public void run() {

        while (true){
            //正确的
//            synchronized(obj){
            //用类充当监视器  类只会加载一次 意味是唯一的
            synchronized(Window2.class){
            //错误的 this代表调用时new出来的三个对象
//          synchronized(this){
                if (ticket>0){
                    try {
                        Thread.sleep(100);
                    } catch (InterruptedException e) {
                        throw new RuntimeException(e);
                    }
                    System.out.println(getName()+":卖票,票号为:"+ticket);
                    ticket--;
                }else {
                    break;
                }
            }

        }

    }
}
public class WindowTest2 {
    public static void main(String[] args) {
        Window2 window1 = new Window2();
        Window2 window2 = new Window2();
        Window2 window3 = new Window2();
        window1.start();
        window2.start();
        window3.start();

    }
}
方式二:同步方法
     如果操作共享数据的代码完整的声明在一个方法中,我们不妨将此方法同步声明。
     非静态的同步方法 默认的监视器是this
     静态方法中 同步监视器是当前类本身
class Window3 implements Runnable{
    
//    private static int ticket= 100;
    private  int ticket= 100;
    Object obj=new Object();

    boolean isflag=true;

    @Override
    public void run() {
        while (isflag){
            show();
        }
    }

    private synchronized void show (){//此时的同步监视器是this 是唯一的

        if (ticket>0){
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
            System.out.println(Thread.currentThread().getName()+":卖票,票号为:"+ticket);
            ticket--;
        }else {
            isflag=false;
        }
    }
}

public class WindowTest3{
    public static void main(String[] args) {
        //关键字不需要用static修饰 因为就创建了一个window1对象

        Window3 window3 = new Window3();
        Thread thread = new Thread(window3);
        Thread thread1 = new Thread(window3);
        Thread thread2 = new Thread(window3);
        thread.start();
        thread1.start();
        thread2.start();

    }
}
class Window4 extends Thread{
    private static int ticket =100;

    //定义锁 但是在代码中new了三个window2这个类 锁不唯一了
    private static Object obj = new Object();

    private static boolean isflag= true;
    @Override
    public void run() {

        while (isflag){
            show();
        }

    }

    public synchronized void show(){ //此时同步监视器是this 那么和test2中问题一致 this对应的是w1 w2 w3 仍然是线程不安全的
        if (ticket>0){
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
            System.out.println(getName()+":卖票,票号为:"+ticket);
            ticket--;
        }else {
            isflag=false;
        }
    }
}
public class WindowTest4 {
    public static void main(String[] args) {
        Window4 window1 = new Window4();
        Window4 window2 = new Window4();
        Window4 window3 = new Window4();
        window1.start();
        window2.start();
        window3.start();

    }
}
测试Thread常用的方法
1、start()启动当前线程,调用当前线程的run方法
2、run:通常需要重写Thread类中的此方法,将需要执行的具体操作写在run方法中
3、currentThread() 静态方法 返回执行当前代码的线程
4、getName() 获取当前线程名
5、setName() 设置当前线程名
6、yield() :释放当前cpu的执行权,让步 但是不等于让给其他线程执行 还有可能会分配给自己
7、join():在线程a中调用线程b的join()方法 线程a就会进入阻塞状态,在线程b完全执行完之后 线程a才结束阻塞状态。
8、stop():强制结束线程的生命周期,不推荐使用
9、sleep()阻塞当前线程指定毫秒数
10、isAlive()判断当前线程是否存活
线程的优先级
1、MAX_PRIORITY:10
2、MIN_PRIORITY:1
3、NORM_PRIORITY:5 默认是5
如何当前设置线程的优先级
getPriority():获取线程的线程优先级
setPriority(int p):设置线程的优先级
 说明:高优先级线程会抢占低优先级线程执行的权限,但是从概率来讲,高优先级线程会高概率被执行,并不意味着只有高优先级线程执行完之后,低优先级的才执行。
public static void main(String[] args) {
        HelloThread helloThread = new HelloThread("czcz");
        //默认的线程名为:Thread-0
        //重新设置线程名 需要在线程执行之前
//        helloThread.setName("czThread");

        //设置线程的优先级
        helloThread.setPriority(Thread.MAX_PRIORITY);
        helloThread.start();

        /**
         * main方法 主线程
         */
        //给主线程命名
        Thread.currentThread().setName("主线程");
        for (int i = 0; i < 100; i++) {
            if(i%2==0){
                System.out.println(Thread.currentThread().getName()+":"+i+"优先级:"+Thread.currentThread().getPriority());
            }
//            if(i==20){
//                try {
//                    helloThread.join();
//                } catch (InterruptedException e) {
//                    throw new RuntimeException(e);
//                }
//            }
        }
//        boolean alive = helloThread.isAlive();
//        System.out.println(alive);
    }


class HelloThread extends Thread{
    @Override
    public void run() {
        for (int i = 0; i < 100; i++) {
            try {
                sleep(10);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
            if(i%2==0){
                System.out.println(Thread.currentThread().getName()+":"+i+"优先级:"+Thread.currentThread().getPriority());
            }
//            if (i%20==0){
//                this.yield();
//            }
        }
    }

    //可以通过有参数构造的方法 给线程命名
    public HelloThread(String name){
        super(name);
    }
}
 通过实现callable接口实现多线程的操作
与runnable接口的实现方式对比
1、call可以有返回值更加灵活
2、call可以使用throws的方式处理异常,更加灵活
3、callable 使用了泛型参数 可以定义返回值的类型,更加灵活
缺点:
如果在主线程中获取分线程call的返回值,则此时主线程是阻塞状态
class PrintNum implements Callable{
    //实现call方法
    @Override
    public Object call() throws Exception {
        int sum=0;
        for (int i = 0; i < 100; i++) {
            if (i%2==0){
                System.out.println(i);
                sum+=i;
            }
        }
        return sum;
    }
}

public class CallableTest {
    public static void main(String[] args) {
        //创建callable实现类的对象
        PrintNum printNum = new PrintNum();
        //将此callable接口实现类的对象作为参数传递到FutureTask的对象构造器中,创建FutureTask的对象
        FutureTask futureTask = new FutureTask<>(printNum);
        //将futuretask的对象作为参数传递到Thread类构造器中,创建Thread对象,并调用start方法。
        Thread thread = new Thread(futureTask);
        thread.start();

        try {
            //获取callable中call方法的返回值
            //get返回值即为futuretask构造器中实现callable中call方法的返回值
            Object o = futureTask.get();
            System.out.println("相加之和为:"+ o);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        } catch (ExecutionException e) {
            throw new RuntimeException(e);
        }

    }
}

你可能感兴趣的:(java,开发语言)