创建多线程的四种方法总结+同步+线程生命周期

实现多线程的第一种方式:继承Thread类

步骤:

一、写一个类继承Thread类
二、重写run()方法,run方法中写此线程执行的代码
三、在main方法中new此类对象,调用start方法

class MyThread extends Thread{

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

public class Note1 {
    public static void main(String[] args) {
        //给主方法设置线程名字
        Thread.currentThread().setName("Main线程");
        //新建两个线程
        MyThread myThread=new MyThread();
        MyThread myThread1=new MyThread();
        //给新建的线程设置名字
        myThread.setName("MyThread");
        myThread1.setName("第二个线程对象");
        //新建线程就绪,注意这个时候线程并未执行,当它强到CPU执行权时才执行
        myThread.start();
        myThread1.start();
        for (int i=0;i<99;i++){
            if(i%2!=0){
                System.out.println(Thread.currentThread().getName()+" : "+i);
            }
        }
    }
}
	通过上面的代码可以发现,用这个方式新建线程的话,继承Thread的类就是线程类,它的对象就是具体的线程,
	不同的线程有不同的对象,对象之间的非静态属性不共享数据。
	总结:一个继承Thread的类,多个对象,多个线程,线程之间不共享非静态数据

实现多线程的第二种方式:实现Runnable接口

步骤:

第一步:写一个类实现Runnable接口
第二步:实现run方法
第三步:主方法中new此实现类
第四步:将此对象作为参数传给Thread的构造器形成线程对象
第五步:线程对象start

class RunThread implements Runnable{
    @Override
    public void run() {
        for(int i=0;i<99;i++){
            if (i%2==0){
                System.out.println(Thread.currentThread().getName()+" : "+i);
            }
        }
    }
}
public class Note2 {
    public static void main(String[] args) {
        RunThread runThread=new RunThread();
        Thread thread1=new Thread(runThread);
        Thread thread2=new Thread(runThread);
        thread1.start();
        thread2.start();
    }
}

通过上面的代码可以发现,用这个方式新建线程的话,实现Runnable的类的对象是作为参数传给Thread的
不同的线程可以有相同的对象,不同的线程之间可以共享数据
总结:一个实现Runnable的类,一个对象,多个线程,线程之间共享数据

实现多线程的第三种方式-实现Callabel接口(了解,日后补充)

步骤:

一、写一个类实现Callable接口
二、重写run方法
三、在主方法中new对象
四、此对象传给FutureTask的构造器,new一个FutureTasl的对象
五、将FutureTask对象传给Thread的构造器,new一个Thread对象
六、Thread对象调用start方法

class  CallThread implements Callable{

    @Override
    public Object call() throws Exception {
        int sum=0;
        for(int i=0;i<=100;i++){
            sum+=i;
            System.out.println(Thread.currentThread().getName()+": "+i);
        }
        return  sum;
    }
}
public class Note3 {
    public static void main(String[] args) {
        CallThread callThread=new CallThread();
        FutureTask futureTask=new FutureTask(callThread);
        FutureTask futureTask1=new FutureTask(callThread);
        Thread thread=new Thread(futureTask);
        Thread thread1=new Thread(futureTask1);
        thread.start();
        thread1.start();
    }
}

实现多线程的第四种方式-线程池(了解,后面再补充)

在这里插入图片描述
在这里插入图片描述

线程的同步

问题:三个窗口总共有100个门票,用多线程实现(提示:一个窗口一个线程)

方法一:用继承Thread类的方法新建多线程,三个对象分别代表三个窗口。由于票数是固定的,三个窗口共享的,所以将票的数目声明为了类变量。下面的代码是没有考虑线程安全的问题,为了让线程安全的问题更加逼真,在run方法中使用了让不同的对象阻塞1秒的方法。(注意,Thread类里有sleep方法,this.sleep()调用的不是Object类的sleep,所以休眠的是线程)

class  Window extends Thread{
    public  static  int ticketNum=100;
    @Override
    public void run() {
        for(;ticketNum>=0;ticketNum--){
            System.out.println(Thread.currentThread().getName()+"窗口卖第"+ticketNum+"号票");
            try {
                this.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}
public class Note4 {
    public static void main(String[] args) {
        Window window1=new Window();
        window1.setName("Window1");
        Window window2=new Window();
        window2.setName("Window2");
        window1.start();
        window2.start();
    }
}

在这里插入图片描述

会发现出现了上面的问题,问题的原因就是:
不同的线程处理共享的数据时,会出现线程安全问题。这里由于一个线程在打印语句结束后,tickNum并没有立马减1,而是先休眠了一秒钟,在这一秒钟里,另外一个线程打印语句执行时ticket还是没用减1时的状态,所以出现了问题。

方法2:用实现Runnable接口的方法创造线程对象

注意,这里ticketNum可以是非静态属性

class RunWindow implements Runnable{
    private  int ticketNum=100;
    @Override
    public void run() {
        for(;ticketNum>=0;ticketNum--){
            System.out.println(Thread.currentThread().getName()+"窗口卖第"+ticketNum+"号票");
            try {
                Thread.currentThread().sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}
public class Note5 {
    public static void main(String[] args) {
        RunWindow runWindow=new RunWindow();
        Thread thread1=new Thread(runWindow);
        thread1.setName("Window1");
        Thread thread2=new Thread(runWindow);
        thread2.setName("Window2");
        thread1.start();
        thread2.start();
    }
}

在这里插入图片描述
这也是出现了问题,问题的原因同方法一一样

方法一的解决

 synchronized (Window.class){
        for(;ticketNum>=0;ticketNum--){
            System.out.println(Thread.currentThread().getName()+"窗口卖第"+ticketNum+"号票");
            try {
                this.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        }

synchronized的参数是同步监视器,这个同步监视器必须是唯一的,Thread继承创建的线程对象是多个,对象不唯一,所以不能用this

方法二的解决

 public void run() {
        synchronized (this) {
            for (; ticketNum >= 0; ticketNum--) {
                System.out.println(Thread.currentThread().getName() + "窗口卖第" + ticketNum + "号票");
                try {
                    Thread.currentThread().sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

这个方法的同步监视器是this

总结线程安全问题:
当多个线程处理共享数据(堆内存中的数据)时,就要考虑线程安全问题
对于方法一,它的共享数据是静态数据
对于方法二,它的共享数据是属性和非静态数据
解决线程安全问题的思路:用同步代码块将涉及到共享数据的代码括起来
对于方法一,锁是类.class
对于方法二,锁是this

噢,时间太晚了,明天总结线程生命周期以及控制方法。

    synchronized (this) {
        for (this.i = 0; this.i< 100; ) {
            i++;
            this.notify();
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName() + " :" + i);

            try {
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值