0


JAVA--线程(Thread)

所属包:

            java.lang;

构造方法:

     public Thread();

     public Thread(String name);参数是给线程起个名字

     public Thread(Runnable target);传入要执行的Runnable对象

     public Thread(Runnable target,String name);可以同时传入中间要用“,”隔开

静态方法:

    static Thread currentThread();获取当前线程

    static void  sleep(long millis);具体数字,毫秒值  1000毫秒=1秒

成员变量:

    private Runnable target;
   

成员方法:

    void start();//启动线程后,会执行run方法中的代码

    void run();线程执行的就是这里的内容

    String getName();获取线程的名字

    void setName(String name);也是用来给线程起一个名字

    State getState();获取线程的状态

调度方式:

            抢占式调度:给每个任务分配的时间不等。(可以理解为线程之间进行争抢,谁抢上就是谁执行任务。)
             分时调度:给每个任务分配的时间是均等的。(可以在任务完添加一个sleep来控制每个线程执行的时间。)

进程和线程:

进程:

    一个应用程序就是一个或者多个进程。

线程:

    一个进程有一个或者多个线程。
public class Test01 {
    public static void main(String[] args) {
        while(true);
    }
}

获取线程的名字:Thread.currentThread();+ ?.getName();

public class Test01 {
    public static void main(String[] args) {
        //获取当前线程,执行当前代码的线程
        Thread currentThread = Thread.currentThread();
        //获取线程的名字
        String name = currentThread.getName();
        //打印线程的名字
        System.out.println(name);

    }
}

Runnable接口:

    public abstract void run();

Runnable接口的方式和继承Thread类的方式:建议使用Runnable方式
a.线程和任务分离,解耦合,提高代码的健壮性。
b.避免了Java单继承的局限性
c.线程池里面,只能传入Runnable或者Callable类型的对象,不用new Thread

       每一个线程启动后都会有一个栈,各自在各自的栈中执行任务。
       线程的开销比一般对象的开销要大。

运行一个线程:

第一种方法:

1.先创建一个继承了Thread的的类,并重写run方法(线程执行的任务就是run方法中的代码);

public class SubThread extends Thread{
    @Override
    public void run() {
        //获取当前线程,执行当前代码的线程
        Thread t = Thread.currentThread();
        //获取线程的名称
        String name = t.getName();
        //System.out.println(name+" hello Thread");
        
        for (int i = 0; i < 100; i++) {
            System.out.println(name+"--"+i);
            try {
        //目的是让线程执行完任务睡一会儿,执行速度慢一些,方便我们看效果
                Thread.sleep(100);//单位是毫秒
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

try{}catch()是解决异常,详情可见https://so.csdn.net/so/search?spm=1001.2101.3001.7498&q=java%E5%BC%82%E5%B8%B8%E5%A4%84%E7%90%86&t=&u=&utm_term=java%E5%BC%82%E5%B8%B8%E5%A4%84%E7%90%86&utm_medium=distribute.pc_toolbar_associateword.none-task-associate_word-opensearch_query-4-java%3Cem%3E%E5%BC%82%E5%B8%B8%3C%2Fem%3E%E5%A4%84%E7%90%86-null-null.nonecase&depth_1-utm_source=distribute.pc_toolbar_associateword.none-task-associate_word-opensearch_query-4-java%3Cem%3E%E5%BC%82%E5%B8%B8%3C%2Fem%3E%E5%A4%84%E7%90%86-null-null.nonecase&request_id=165423633116781685375078&opensearch_request_id=165423633116781685375078https://so.csdn.net/so/search?spm=1001.2101.3001.7498&q=java%E5%BC%82%E5%B8%B8%E5%A4%84%E7%90%86&t=&u=&utm_term=java%E5%BC%82%E5%B8%B8%E5%A4%84%E7%90%86&utm_medium=distribute.pc_toolbar_associateword.none-task-associate_word-opensearch_query-4-java%3Cem%3E%E5%BC%82%E5%B8%B8%3C%2Fem%3E%E5%A4%84%E7%90%86-null-null.nonecase&depth_1-utm_source=distribute.pc_toolbar_associateword.none-task-associate_word-opensearch_query-4-java%3Cem%3E%E5%BC%82%E5%B8%B8%3C%2Fem%3E%E5%A4%84%E7%90%86-null-null.nonecase&request_id=165423633116781685375078&opensearch_request_id=165423633116781685375078

2.在测试类中创建该线程对象,并开始任务(?.start();)

public class Test02 {
    public static void main(String[] args) {
        //创建一个子线程
        Thread t = new SubThread();
        //开始任务
        t.start();
        
    }
}

注意:run()与start()的区别:

public class Test02 {
    public static void main(String[] args) {
        //创建一个子线程
        Thread t = new SubThread();
        //t.start();
        
        //如果直接调用run方法,run方法中的代码是由主线程执行的
        t.run();
    }
}
    直接调用run()方法,是主线程main去执行的,没有显示出我们子线程的作用,没有达到我们写线程的目的。

第二种方法:

使用匿名内部类创建Runnable对象;

    public static void main(String[] args) {
        Runnable a = new Runnable(){

            @Override
            public void run() {
                Thread x = Thread.currentThread();
                String name = x.getName();
                for(int i =0;i<100;i++) {
                    System.out.println(name+i);
                }
                
            }
            
        };
        Thread b = new Thread(a,"线程一");
        b.start();

    }

}

线程的并行和并发:

    并行(必须是多核CPU在可以达到):    一个或者多个事件(任务)在同一时间点(同时)执行,好比你可以一般打游戏一边吃饭,同事进行。
     并发():    一个或者多个事件在同一时间段(先后)执行,你有两碗饭,你只有把嘴里的解决了才能吃下一口。

多线程:

    顾名思义是多个线程同时运行
    public static void main(String[] args) {
        Runnable a = new Runnable(){

            @Override
            public void run() {
                Thread x = Thread.currentThread();
                String name = x.getName();
                for(int i =0;i<100;i++) {
                    System.out.println(name+i);
                }
                
            }
            
        };
        Thread b = new Thread(a,"线程一");
        b.start();
        new Thread(a,"线程二").start();
    }

}

多线程注意:

     多个线程执行的时候:谁线程值不确定,谁执行多长时间不确定。

线程的安全:

public static void main(String[] args) throws InterruptedException {
        //创建一个集合,使其可以存入20000个元素
        ArrayList<Integer> list = new ArrayList<>(20000);
        
        //通过匿名内部类的方式创建Runnable类型的对象
        Runnable r = new Runnable() {
            @Override
            public void run() {
                for (int i = 0; i < 10000; i++) {
                    list.add(i);
                }
               //用来判断是否两个线程都执行任务了
                System.out.println("=================");
            }
        };
        //用匿名对象的形式创建两个线程去执行任务
        new Thread(r).start();
        new Thread(r).start();
        
        //用来限制主线程main,防止线程还没执行完,主线程已经执行了sout;
        Thread.sleep(3000);
        System.out.println(list.size());
    }

第一次运行:

第二次运行:

正常下应该是存储20000个,现在会发生丢失问题。

为什么会发生丢失问题?线程安全产生的前提:多个线程访问同一资源(数据)

    线程的执行无非就是这三步;

    ![](https://img-blog.csdnimg.cn/95f8dee5c9214f5cb19a7cffe6b9873b.png)

之所以会发生错误就是线程一拿到数字了,进行第二部+1了,但是还没有执行第三步,任务被线程二抢去了,所以没存上。再等到线程一抢回来,继续存,两个数存了同一个数,就相当于丢了。

    static int n = 0;
    public static void main(String[] args) throws InterruptedException {
        Runnable r = new Runnable() {
            @Override
            public void run() {
                String name = Thread.currentThread().getName();
                for (int i = 0; i < 100; i++) {
                    n++;
                    System.out.println(name+n);
                }
                
            }
        };
        
        
        new Thread(r,"天线宝宝").start();
        new Thread(r,"花园宝宝").start();
        
        Thread.sleep(3000);
        System.out.println(n);
    }
}

如何解决线程丢失(上锁):整个的操作不是原子操作:

    在线程一进行操作时,另一个线程二进行等待,只有线程一操作完,释放锁对象,线程二才能抢。

1.使用synchronized代码块

    语法:
               synchronized(锁对象){//可以是任意类型的对象
                   //写有可能发生线程安全问题的代码
               }
static int n = 0;
    public static void main(String[] args) throws InterruptedException {
        Runnable r = new Runnable() {
            @Override
            public void run() {
                for (int i = 0; i < 10000; i++) {
                    synchronized (this) {
                        n++;
                    }
                }
            }
        };
        
        new Thread(r).start();
        new Thread(r).start();
        
        Thread.sleep(3000);
        System.out.println(n);
    }

2.使用synchronized方法

    静态synchronized方法:在static和返回值之间加synchronized关键字
     非静态synchronized方法:在返回值之前加synchronized关键字

    synchronized代码块和方法:代码执行完毕后,自动释放锁
public class Testhah {
    static int n = 0;
    public static void main(String[] args) throws InterruptedException {
        
        Runnable r = new Runnable() {
            @Override
            public void run() {
                for (int i = 0; i < 10000; i++) {
                    //调用synchronized方法
                    add();
                }
            }
        };
        
        new Thread(r).start();
        new Thread(r).start();
        
        Thread.sleep(3000);
        System.out.println(n);
    }
    
    public static synchronized void add() {
        n++;
    }
}

3.使用Lock锁

    Lock接口:
               void lock();//上锁
               void unlock();//开锁

               常用实现类:ReentrantLock
                   构造方法:
                       public ReentrantLock();
public class lulalei {
    static int n = 0;
    public static void main(String[] args) throws InterruptedException {
        Lock lock = new ReentrantLock();
        
        Runnable r = new Runnable() {
            @Override
            public void run() {
                for (int i = 0; i < 10000; i++) {
                    //上锁
                    lock.lock();
                    n++;
                    //解锁
                    lock.unlock();
                }
            }
        };
        
        
        new Thread(r).start();
        new Thread(r).start();
        
        Thread.sleep(3000);
        System.out.println(n);
    }
    
}

线程的状态:

    NEW:新建状态。创建了一个线程,启动之前处于该状态
public class Test01 {
    public static void main(String[] args) {
        Thread t = new Thread();
        State  s = t.getState();
        System.out.println(s);//NEW
    }
}

     RUNNABLE:可运行状态
public class Test03 {
    public static void main(String[] args) throws InterruptedException {
        Runnable r = new Runnable() {
            @Override
            public void run() {
                for(;;);
            }
        };
        Thread t = new Thread(r);
        t.start();
        
        Thread.sleep(100);//100毫秒保证子线程开始执行Thread.sleep(1000000);这一行代码了
        //获取并打印线程的状态
        System.out.println(t.getState());
    }
}

    BLOCKED:阻塞状态
public class Test05 {
    public static void main(String[] args) throws InterruptedException {
        Runnable r = new Runnable() {
            @Override
            public void run() {
                synchronized(this) {
                    for(;;);
                }
            }
        };
        Thread t1 = new Thread(r);
        t1.start();
        Thread t2 = new Thread(r);
        t2.start();
        
        Thread.sleep(100);//100毫秒保证两个子线程已经启动,并开始执行代码了
        //获取并打印线程的状态
        System.out.println(t1.getState());
        System.out.println(t2.getState());
        //获取了锁对象开始执行for循环的线程处于RUNNABLE状态
        //没有获取锁对象的线程处于BLOCKED状态。
    }
}

    WAITING:无限等待状态
public class Test06 {
    public static void main(String[] args) throws InterruptedException {
        Lock lock = new ReentrantLock();
        
        Runnable r = new Runnable() {
            @Override
            public void run() {
                    lock.lock();
                    for(;;);
            }
        };
        Thread t1 = new Thread(r);
        t1.start();
        Thread t2 = new Thread(r);
        t2.start();
        
        Thread.sleep(100);//100毫秒保证两个子线程已经启动,并开始执行代码了
        //获取并打印线程的状态
        System.out.println(t1.getState());
        System.out.println(t2.getState());
        //获取了锁对象开始执行for循环的线程处于RUNNABLE状态
        //没有获取Lock锁对象的线程处于WAITING状态。
    }
}

    TIMED_WAITING:计时等待状态
public class Test04 {
    public static void main(String[] args) throws InterruptedException {
        Runnable r = new Runnable() {
            @Override
            public void run() {
                try {
                    Thread.sleep(1000000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        };
        Thread t = new Thread(r);
        t.start();
        
        Thread.sleep(100);//100毫秒保证子线程开始执行Thread.sleep(1000000);这一行代码了
        //获取并打印线程的状态
        System.out.println(t.getState());
    }
}

    TERMINATED:消亡状态
public class Test02 {
    public static void main(String[] args) throws InterruptedException {
        Runnable r = new Runnable() {
            @Override
            public void run() {
                System.out.println(Thread.currentThread().getName());
            }
        };
        Thread t = new Thread(r);
        t.start();
        
        Thread.sleep(1000);//1000毫秒子线程一定把任务执行完毕了
        //获取并打印线程的状态
        System.out.println(t.getState());
    }
}

线程的通信:线程和线程之间的沟通

等待唤醒机制:

Object类:

           void wait();//就会让线程进入等待状态。WAITING状态
           void wait(long time);//调用该方法,会让线程进入计时等待状态。TIMED_WAITING
           void notify();//调用该方法,会让线程醒来,接着执行任务。
           void notifyAll();//调用该方法,会唤醒当前锁对象上等待的所有线程

注意事项:

    1.这些方法都必须写在synchronized代码块或者synchronized方法中           

    2.调用这些方法的对象,必须和锁对象一致。

    3.  t.notify方法,只能唤醒t锁对象上等待的线程

    4.  调用了wait(不论是否有参数)方法后,会自动释放锁对象。

例子:

1.创建一个Fruit类

public class Fruit {
    private int stock;
    public int getStock() {
        return stock;
    }
    public void setStock(int stock) {
        this.stock = stock;
    }
    
}

2.官网线程类

public class NetShop implements Runnable{
    private Fruit f;
    public NetShop(Fruit f) {
        super();
        this.f = f;
    }

    @Override
    public void run() {
        while(true) {
            synchronized(f) {
                //判断是否退出循环
                if(f.getStock()<=0) {
                    break;
                }
                //偶数份,官网卖
                if(f.getStock()%2==0) {
                    //每卖一份,库存减1
                    f.setStock(f.getStock()-1);
                    System.out.println("官网正在卖出第"+(100-f.getStock())+"份,还剩余"+f.getStock()+"份");
                    try {
                        Thread.sleep(100);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    //唤醒当前锁对象上等待的线程
                    f.notify();
                }else {
                    //奇数份,官网进入等待状态
                    try {
                        f.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        }
    }
}

3.实体店线程类

public class FrontShop implements Runnable{
    private Fruit f;
    public FrontShop(Fruit f) {
        super();
        this.f = f;
    }

    @Override
    public void run() {
        while(true) {
            synchronized(f) {
                //判断是否退出循环
                if(f.getStock()<=0) {
                    break;
                }
                //奇数份,实体店卖
                if(f.getStock()%2==1) {
                    //每卖一份,库存减1
                    f.setStock(f.getStock()-1);
                    System.out.println("实体店正在卖出第"+(100-f.getStock())+"份,还剩余"+f.getStock()+"份");
                    try {
                        Thread.sleep(100);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    //唤醒当前锁对象上等待的线程
                    f.notify();
                }else {
                    //偶数份,实体店进入等待状态
                    try {
                        f.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        }
    }
}

4.测试类

public class Test01 {
    public static void main(String[] args) {
        //创建Fruit类型的对象
        Fruit f = new Fruit();
        f.setStock(100);
        
        //创建官网和实体店类型的对象
        Runnable r1 = new NetShop(f);
        Runnable r2 = new FrontShop(f);
        
        //创建两个线程,分别指向这两个任务
        new Thread(r1).start();
        new Thread(r2).start();
    }
}

注意:

    我们做这个题要明确两点:

    1.这两边需要一个共同的锁对象
     2.根据库存去判断谁该执行了,谁该等待了

线程池:

    简单来说就是一个存放线程的池子,线程执行完任务会回到池子里面等待下一个任务的到来,提高效率。

    1.提高响应速度。预先创建好了线程,只等任务过来执行。

    2.降低资源消耗。线程池中的线程,执行完任务后,又返回到线程池中,下一个任务到来后可以继续使用该线程。

    3.提高线程的可管理性。一个线程大约需要消耗1M的空间,线程池可以设置最大线程的数量。

Executors类:

    static ExecutorService newFiexdThredadPool(int nThread);

ExecutorService接口:

    void execute(Runnable r);//执行任务
     <T> Future<T> submit(Callable<T> c);//执行任务
     Future<?> submit(Runnable r);//执行任务
     void shutdown();//关闭线程池
public class Test01 {
    public static void main(String[] args) {
        Runnable r = new Runnable() {
            @Override
            public void run() {
                System.out.println(Thread.currentThread().getName());
            }
        };
        
        //创建一个固定线程池对象
        ExecutorService es = Executors.newFixedThreadPool(2);
        //执行任务
        es.execute(r);
        //关闭线程池
        es.shutdown();
    }
}

下面是如果有多个任务线程池怎么运行:

public class Test02 {
    public static void main(String[] args) {
        Runnable r = new Runnable() {
            @Override
            public void run() {
                System.out.println(Thread.currentThread().getName());
                try {
                    Thread.sleep(10000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        };
        
        //创建一个固定线程池对象
        ExecutorService es = Executors.newFixedThreadPool(2);
        //执行任务
        es.execute(r);
        es.execute(r);
        es.execute(r);
        //关闭线程池
        es.shutdown();
    }
}
     从结果,可以看出来,先执行任务的那个把任务执行完,接着把第三次任务执行了,后执行任务的,执行完任务就回到了线程池。

Future接口

    T get();//必须等子线程把任务执行完成,return以后才可以获取返回的结果。

Callable接口(与Runnable很像):

    T call();
public class Test04 {
    public static void main(String[] args) throws InterruptedException, ExecutionException {
        Runnable r = new Runnable() {
            @Override
            public void run() {
                System.out.println(Thread.currentThread().getName());
            }
        };
        
        //创建一个固定线程池对象
        ExecutorService es = Executors.newFixedThreadPool(2);
        Future<?> future = es.submit(r);
        Object result = future.get();//null
        System.out.println(result);
        //关闭线程池
        es.shutdown();
    }
}
   ![](https://img-blog.csdnimg.cn/6034b33f85f442dc8f6472cc5fbd1875.png) 

可以看出来Runnable没有返回值,所以就不用接收了。

public class Test03 {
    public static void main(String[] args) throws InterruptedException, ExecutionException {
        Callable<String> c = new Callable<String>() {
            @Override
            public String call() throws Exception {
                //call方法中的代码是某个子线程执行的
                System.out.println(Thread.currentThread().getName());
                Thread.sleep(5000);
                return "test callable";
            }
        };
        
        //创建一个固定线程池对象
        ExecutorService es = Executors.newFixedThreadPool(2);
        Future<String> future = es.submit(c);
        
        //这行代码是主线程执行的
        String result = future.get();
        System.out.println(result);
        //关闭线程池
        es.shutdown();
    }
}

标签: java

本文转载自: https://blog.csdn.net/zailalaniubi/article/details/125130696
版权归原作者 意Ω外 所有, 如有侵权,请联系我们删除。

“JAVA--线程(Thread)”的评论:

还没有评论