多线程的理解和学习

2.2初始线程:线程的基本操作

        进行java并发程序设计的第一步, 就必须要知道java中为线程操作提供了那些API。比如创建,启动,终止。因为并行操作比串行要复杂,所以围绕这些常用的接口,总有一大堆坑等着你去踩。

2.2.1 新建线程

        新建线程很简单,只需要用new关键字创建一个线程对象,并且将它start()起来就可以

 Thread t1 = new Thread()
 t1.start()

那线程start()以后,要做什么才是问题的关键,线程Thread,有一个run()方法,start()方法会新启动一个线程,让这个线程去执行run()方法。

Thread tl=new Thread(); 
tl.run(); 

注意::不要用 run()方法来开启新线程 它只会在当前线中串行执行 run()方法中的代码。

start()方法开启线程, run()方法来定义开启这个线程后这个线程去执行的工作

 在默认的情况下,线程Thread的run()方法什么都没有做,因此,这个线程一启动就结束了,如果你想让线程做点什么,就必须重写run()方法,把你的“任务”加进去

 Thread t1 = new Thread(){
            @Override
            public void run() {
                for (int i = 0; i < 10; i++) {
                    System.out.println("我是线程t1 执行的 第"+i+"个");
                }
            }
        };

t1.start();

上面这个代码使用了匿名内部类, 重写了run方法,循环打印,如果没有特别的需要,都可以通过继承线程Thread,重写run()方法来自定义线程,继承本身也是宝贵的资源,所以,我们使用Runnable接口来实现操作,Runnable是单方法接口,只有一个run()方法。

public interface Runnable {

    public abstract void run();
}

Thread 类有 个非常重 的构造方法:

public Thread(Runnable target) 

它传入一 Runnable 口的 实例,在 tart() 方法调用时,新的线程就会执行Runnable.run()方法 。实际上,默认的 Thr ad.run()方法就是这么做的:

public class ThreadDemo implements Runnable {
    @Override
    public void run() {
        for (int i = 0; i < 10; i++) {
            System.out.println("我是t1 列的 第"+i+"个");
        }
    }
    public static void main(String[] args) {
        Thread thread = new Thread(new ThreadDemo());
        thread.start();
        for (int i = 0; i < 1000; i++) {
            System.out.println(i);
        }
    }
}

上述代码实现了 Runnabl 接口,并将该实例传入线程 Thread 中。这样避免重写 Thread.run()方法,单纯使用接口来定义线程 Thread 。

         如何正常的关闭一个线程,查阅JDK,线程Thread提供了一个stop()的方法,如果你使用 sto () 方法,就可以立即将 个钱程终,但如果你使用工具写代码 ,就会发现stop() 方法是一个被标注为废弃的方法。 也就是说,在将来, JDK 可能就会移除该方法。
        注:废弃的原因是 stop()方法过于暴力,强行把执行到 半的线程终止,可能会引起 些数据不一致的问题。

        当强行使用stop() 去终止线程的时候造成数据不一致,就会造成数据永久的破坏,后果不堪设想。一般单线程不会出现这个问题,但是如果在并行程序中, 如果考虑不全面,就会出现这种情况。

        Thread.stop()方法在结束线程时,会直接终止线程,并立即释放这个线程所持有的锁, 而这些锁恰恰是用来维持对象一致性的 如果此时 写线程写入数据正写到一半, 并强行 终止,那么对象就会被写坏,同时,由于锁己经被释放,另外一个等待该锁的读线程就会读到一个不一致的对象。

       首先,对象 持有 ID NAME 两个字段 假设当 ID 等于 NAME 时表示对象是一致,否则表示对象出错,写线程总是会把ID和NAME写成相同的值,初始值都是0,当写线程在写对象时,读线程是无法获得锁,因此必须得等待,所以读线程是看不见一个写了一半的对象的,当写线程写完ID后被stop就会造成ID和NAMe的值不一样。而被终止的写线程简单的将锁释放,读线程争夺到锁后,读取数据,于是出现了错误值

package com.study.thread.thread;

/**
 * @author yd
 * @version 1.0
 * @date 2022/3/28 22:17
 */
public class ThreadStopDemo {

    public static User user = new User();
    public static class User{
        private int id;
        private String name;
        public User(){
            id=0;
            name="0";
        }
        public int getId() {   return id;  }
        public void setId(int id) {    this.id = id;  }
        public String getName() { return name;  }
        public void setName(String name) {this.name = name;}
        @Override
        public String toString() {
            return "User{" +"id=" + id +", name='" + name + '\'' +'}';
        }
    }


    public static class ChangeThread extends Thread{
        @Override
        public void run() {
            while (true){
                synchronized (user){
                    int v = (int) (System.currentTimeMillis()/1000);
                    user.setId(v);
                    try {
                        Thread.sleep(100);
                    }catch (Exception e){
                        e.printStackTrace();
                    }
                    user.setName(String.valueOf(v));
                }
                Thread.yield();
            }
        }
    }
    public static class ReadObjectThread extends Thread{
        @Override
        public void run() {
            while (true){
                synchronized (user){
                    if (user.getId()!=Integer.parseInt(user.getName())){
                        System.out.println(user.toString());
                    }
                }
                Thread.yield();
            }
        }
    }

    public static void main(String[] args) throws InterruptedException {
        new ReadObjectThread().start();
        while (true){
            ChangeThread changeThread = new ChangeThread();
            changeThread.start();
            Thread.sleep(150);
            changeThread.stop();

        }
    }
}

 输出结果。

User{id=1648648685, name='1648648646'}
User{id=1648648685, name='1648648646'}

那么问题来了,咱咋安全的停止一个线程呢, 我们自己决定线程在何时退出就可以了。

 public static class ChangeThread extends Thread{
        private boolean stopme=false;
        public void stopMe(){
            stopme=true;
        }

        @Override
        public void run() {
            while (true){
                if (stopme){
                    System.out.println("你想让我退出? 我就退出");
                    break;
                }
                synchronized (user){
                    int v = (int) (System.currentTimeMillis()/1000);
                    user.setId(v);
                    try {
                        Thread.sleep(100);
                    }catch (Exception e){
                        e.printStackTrace();
                    }
                    user.setName(String.valueOf(v));
                }
                Thread.yield();
            }
        }
    }

简单来说就是不要在写id和NAME中间停, 要么ID和NAME都写,或者不写的时候,停掉线程。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值