从零开始学java(12)java中的线程

java中的线程

创建线程

java中创建一个线程有两种方法,使用类继承实现Thread类实例化调用start方法,或者使用内部类创建

public class MyThread extends Thread{
    @Override
    public void run() {
        for (int i = 0; i < 10; i++) {
            System.out.println(i);
        }
    }
}
 Thread thread=new MyThread();
        thread.start();

由于Java中的类只支持单继承,在类已经继承一个父类的情况下无法再继承Thread,所以java还提供了Runable接口,可以使用类实现Runable接口来创建线程

public class MyRunable implements Runnable{
    @Override
    public void run() {
        for (int i = 0; i < 10; i++) {
            System.out.println(i);
        }
    }
}

Thread thread1=new Thread(new MyRunable());
        thread1.start();

除此之外,java还支持使用内部类匿名创建线程对象

new Thread(){
    @Override
    public void run() {
        for (int i = 0; i < 10; i++) {
            System.out.println(i);
        }
    }
}.start();
new Thread(new Runnable(){
    @Override
    public void run() {
        for (int i = 0; i < 10; i++) {
            System.out.println(i) ;
        }
    }
}).start();

特殊的守护线程

通过调用线程对象的setDaemon方法可以设定线程为守护线程,当所有非守护线程死亡,守护线程才会死亡,jvm中经典的守护线程就是gc(垃圾回收)

 Thread t1=new Thread(){
    @Override
    public void run() {
        for (int i = 0; i < 100; i++) {

            System.out.println(i);
        }
    }
};
Thread t2=new Thread(){
    @Override
    public void run() {
        while (true){
            System.out.println("守护线程工作中......");
        }
    }
};
t2.setDaemon(true);
t1.start();
t2.start();

线程安全问题

当多个对象创建多个线程对象时对象的成员变量相互独立

public class MyThreadTest extends Thread {
    private int nums = 10;

    @Override
    public void run() {
        while (nums > 10) {
            System.out.println(nums);
            nums--;

        }
    }
}


Thread t1=new MyThreadTest();
Thread t2=new MyThreadTest();
Thread t3=new MyThreadTest();
t1.start();
t2.start();
t3.start();

执行30次

使用多个线程对象代理同一个对象时,成员变量共享,但是当多个线程访问一个共享资源时会出现线程安全问题

public class MyRunableTest implements Runnable {
    private int nums=10;

    @Override
    public void run() {
        while (nums>0){
            System.out.println(nums);
            nums--;
        }
    }
}


MyRunableTest myRunableTest=new MyRunableTest();
Thread t1=new Thread(myRunableTest);
Thread t2=new Thread(myRunableTest);
t1.start();
t2.start();

执行10多次,因为多个线程可能同时进入循环同时拿到值,就会出现打印了两次只减少了一次

原理

cpu在处理指令时为乱序,在多线程情况下随机执行每个线程的语句,java属于高级编程语言,java底层为c/c++实现,c/c++底层为汇编语言,java中一个语句可能需要执行许多指令才能完成,cpu乱序执行这些指令就会导致线程安全问题

在上述的run方法中可以将指令看为,获取值判断,获取值打印,获取值,计算,写回,在例子中可能的执行顺序为

线程1获取值

线程2获取值

线程1计算

线程1写回

线程2计算

线程2写回

由于两个线程先后获取到了值,再进行计算写回,导致打印两次同一个值

线程安全问题解决方案

通过使用java内部提供的同步锁可以解决以上问题,通过使用锁可以使方法在同一时间只有一个线程访问,其他线程则被阻塞

同步锁方法

public synchronized void method(){
    可能会产生线程安全问题的代码
}

同步锁代码块

synchronized(同步锁){
     需要同步操作的代码
}

线程的等待唤醒机制

线程之间的关系不只有竞争抢夺cpu资源,还能够通过等待唤醒机制进行配合

线程可以使用wait方法与notify方法,实现等待和唤起其他相同锁对象的线程

wait:线程不再活动,不再参与调度,进入 wait set 中,因此不会浪费 CPU 资源

notify:则选取所通知对象的 wait set 中的一个线程释放

notifyAll:则释放所通知对象的 wait set 上的全部线程

wait和sleep的区别,wait会释放锁,sleep不会释放

生产者与消费者问题

生产者的主要作用是生成一定量的数据放到缓冲区中,然后重复此过程。与此同时,消费者也在缓冲区消耗这些数据。该问题的关键就是要保证生产者不会在缓冲区满时加入数据,消费者也不会在缓冲区中空时消耗数据。

public class PC {
    private  int num=10;
    public synchronized void in(){
        while (true){
            if (num<10)
            {
                System.out.println(num);
                num++;
                notifyAll();
            }
            try {
                wait();
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }

    }
    public synchronized void out() {
        while (true){
            if (num > 0) {
                System.out.println(num);
                num--;
                notifyAll();
            }
            try {
                wait();
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }

    }
}

  public static void main(String[] args) {
        PC pc=new PC();
        new Thread(){
            @Override
            public void run() {
                pc.in();
            }
        }.start();
        new Thread(){
            @Override
            public void run() {
                pc.in();
            }
        }.start();new Thread(){
            @Override
            public void run() {
                pc.out();
            }
        }.start();
        new Thread(){
            @Override
            public void run() {
                pc.out();
            }
        }.start();
        new Thread(){
            @Override
            public void run() {
                pc.out();
            }
        }.start();

    }

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值