Java 线程

线程
    程序:为解决某种问题,使用计算机语言编写的一系列指令(代码)的集合.
        本章中的程序,特指的是静态的,安装在硬盘上的代码集合.
    进程:运行中的程序(被加载到内存中),是操作系统进行资源分配的最小单位
    线程:进程可以进一步细化为线程,是进程内最小的执行单元,
        是cpu进行任务调度的最小单位

        运行中的QQ就是一个进程,操作系统会为这个进程分配内存资源,
        一个聊天窗口就认为是一个线程,这多个聊天窗口可以同时被cpu执行
        但是这些聊天窗口属于进程

        早期没有线程,早期cpu执行的时候,是以进程为单位执行
        进程单位还是比较大的,当一个进程运行时,其他的进程就不能执行,
        所以后来,将进程中的多个任务,细化为线程
        cpu执行单位,也从进程转为更小的线程

   进程和线程的关系
       一个进程中可以包含多个线程,一个线程只能隶属于一个进程,
       线程不能有力进程存在
       一个进程中至少有一个线程 java中的main方法,就是用来启动主线程
       在主线程中可以创建并启动其他线程,所有线程都共享进程的内存资源
   需求:想在java程序中有几件不相关的事情同时有机会执行
        可以在java中创建线程,把一些要执行的任务放在线程执行
创建线程
   ● 继承Thread类的方式
   ● 实现Runnable接口的方式
创建方式1:
    继承Thread类的方式,重写run()
    创建自己定义的线程类对象,调用start()启动线程
    MyThead extends Thread{

    }
创建方式2:实现Runable接口方式
    类  实现Runable接口,把这个类不能成为线程,是一个人任务类
    重写run()
    new Thead(任务对象);
    调用线程对象的start(),启动线程
以后用第二种方式多一些:
    1.避免单继承的局限性,因为java是单继承的,继承了Thread类,就不能继承其他类了
    2.更适合多线程共享同一份资源的场景
package day16thread.demo1;

/*
    创建线程方式1:
        Mythead 继承 Thead(继承)
        重写Thead类中的run()方法, 在run()方法中编写我们需要执行的代码
        不能直接调用run()方法
 */
public class Mythead extends Thread{

    public void run(){
        for(int i=0;i<1000;i++){
            System.out.println("Mythead"+i);
        }
    }
}
package day16thread.demo1;

public class Test {
    public static void main(String[] args) {

        Mythead mythead=new Mythead();
        //mythead.run();//普通方法调用,不是启动线程
        mythead.start();//启动线程,并不是会立即执行,需要操作系统的调度
        for(int i=0;i<1000;i++){
            System.out.println("main"+i);
        }
    }
}






package day16thread.demo2;

/*

    创建一个任务,实现Runable接口
    重写Runable中的run()方法

 */
public class MyTask implements Runnable{
    @Override
    public void run() {

        Thread thread=Thread.currentThread();//获得当前正在执行的线程
        System.out.println(thread.getName());
        /*for(int i=0;i<10000;i++){
            System.out.println("mytask"+i);
        }*/
    }
}

package day16thread.demo2;

public class Test {
    public static void main(String[] args) {
        MyTask myTask=new MyTask();
        Thread t1=new Thread(myTask);
        t1.setName("我的线程1");
        t1.start();

        Thread t2=new Thread(myTask);
        t2.setName("我的线程2");
        t2.start();

        /*for(int i=0;i<10000;i++){
            System.out.println("main"+i);
        }*/
    }
}
Thread类常用的方法
      run(); 
      start(); 线程启动
      构造方法
          new Thread(Runable runable),接收一个任务对象
          new Thread(Runable runable,String name),接收一个任务对象,并为线程设置名字
      setName();为线程设置名字
      String getName();获得线程的名字
      Thread.currentThread();获得当前正在执行的线程
      setPriority();设置优先级 1-10之间,默认是5
      getPriority();获取优先级
      long sleep();让线程休眠一段时间
      join();让其他线程等待当前线程结束
Thread类有如下3个静态常量来表示优先级
MAX_PRIORITY:取值为10,表示最高优先级。
MIN_PRIORITY:取值为1,表示最底优先级。
NORM_PRIORITY:取值为5,表示默认的优先级
package day16thread.demo3;

public class MyTask implements Runnable{

    public void run(){
        Thread thread=Thread.currentThread();
        if(thread.getName().equals("线程1")){
            try {
                Thread.sleep(8000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        System.out.println(thread.getName()+":"+thread.getPriority());

    }
}
package day16thread.demo3;

public class Test {
    public static void main(String[] args) throws InterruptedException {
        MyTask myTask=new MyTask();
        Thread t1=new Thread(myTask,"线程1");
        Thread t2=new Thread(myTask,"线程2");

        t1.setPriority(6);//设置优先级,1-10之间,默认是5,

        t1.start();
        t1.join();//让其他线程等待当前线程
        t2.start();

        System.out.println(Thread.currentThread().getPriority());

    }
}
线程生命周期,线程从创建到销毁期间经理5个状态
    新建 new Thread();此状态还不能被执行
          调用start()启动线程,让线程进入到就绪状态
    就绪
        当获得CPU执行权后,线程进入到CPU执行
    运行
        运行中的线程可以被切换,又回到就绪状态,也可能因为休眠等原因进入到阻塞状态
    阻塞
        线程休眠时间到了,回到就绪状态
    死亡
        当线程中所有任务执行完了,自动销毁
守护线程
   守护线程也是线程的一种,区别在于他的结束
   如果一个线程是守护线程,那么他会等java中其他线程任务结束后,自动终止
   守护线程为其他线程提供服务,例如JVM中的垃圾回收线程
package day16thread.demo4;

public class Task implements Runnable{
    public void run(){
        while(true){
            System.out.println("我是守护线程,我为大家服务");
        }
    }
}
package day16thread.demo4;

public class Test {
    public static void main(String[] args) {
        Task task=new Task();
        Thread thread=new Thread(task);
        thread.setDaemon(true);//设置线程为守护线程,守护线程必须在启动之前设置
        thread.start();
        for(int i=0;i<10000;i++){
            System.out.println("main"+i);
        }
    }
}
多线程
    在一个应用程序中,存在多个线程,不同的线程可以并行的执行任务
    优点:
        提高程序处理能力,提高效率,提高cpu利用率
        杀毒软件
          垃圾清理
          病毒查杀
          ....
    缺点:
         线程也是需要占用内存资源和CPU资源
         多个线程对同一个共享的资源进行访问,,会出现线程安全问题
多线程同步
多个线程同时读写同一份共享资源时,可能会引起冲突。所以引入线程“同步”机制, 即各线程间     要有先来后到;
如何解决多个线程访问同一个共享资源时不出现问题?
      同步 = 排队+锁     一次只能有一个线程访问共享资源

   加锁方式1:
        使用synchronized关键字修饰代码块 和 方法
          修饰代码块
             同步对象要求:
               多个线程用到的的必须是同一个对象
               可以是java中任何类的对象
              作用:用来记录有没有线程进入到同步代码中
        synchronized(同步对象/锁){
            同步代码块,一次只允许一个线程进入
        }


        修饰方法:
           1.锁不需要我们提供了,会默认提供锁对象
           2.synchronized如果修饰的是非静态方法,锁对象是this
           3.synchronized如果修饰的是静态方法,锁对象修饰的是类的Class对象
             一个类只有一个Class对象
package day16thread.demo6;

/*
  柜台角色   共享数据
 */
public class Counter {
    int num=1;//代表商品数量

    //负责生产商品的方法  锁对象是this  add() 和 sub() 用的是同一把锁
    public synchronized void add(){
        if(num==0){
            System.out.println("生产者生产了一件商品");
            num=1;//生产了一件商品
            this.notify();//唤醒消费者线程
        }else{
            try {
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    //负责消费商品的方法
    public synchronized void sub(){
        if(num>0){
            System.out.println("消费者拿走了一件商品");
            num=0;//消费者拿走了商品
            this.notify();
        }else{
            try {
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}
package day16thread.demo6;

public class CustomerThread extends Thread{

    Counter counter;
    public  CustomerThread(Counter counter){
        this.counter=counter;
    }
    @Override
    public void run() {
            counter.sub();//消费者线程一直消费

    }
}
package day16thread.demo6;

/*
  生产者线程
 */
public class ProducterThread extends Thread{

    Counter counter;
    public ProducterThread(Counter counter){
        this.counter=counter;
    }

    @Override
    public void run(){

            counter.add();//生产者线程一直生产

    }
}
package day16thread.demo6;

public class Test {
    public static void main(String[] args) {
        Counter counter=new Counter();//创建的唯一的柜台对象

        ProducterThread p=new ProducterThread(counter);
        CustomerThread c=new CustomerThread(counter);
        p.start();
        c.start();
    }
}
  枷锁方式2:
        使用jdk中提供的ReentrantLock类实现加锁
        ReentrantLock只能对某一段代码进行加锁,不能对整个方法进行加锁
package day16thread.ticket3;

import java.util.concurrent.locks.ReentrantLock;

public class TicketTask implements Runnable{
    int t=10;
    //实现枷锁的对象
    ReentrantLock reentrantLock=new ReentrantLock();

    @Override
    public void run() {
        while(true){
            try{
                reentrantLock.lock();//加锁
                if(t>0){
                    System.out.println(Thread.currentThread().getName()+"买了"+t+"票");
                    t--;
                }else{
                    break;
                }
            }finally{//在finally块中保证锁必须释放
                reentrantLock.unlock();//释放锁
            }
        }
    }
}
package day16thread.ticket3;

public class Test {
    public static void main(String[] args) {
        TicketTask ticketTask=new TicketTask();

        Thread t1=new Thread(ticketTask,"窗口1");
        Thread t2=new Thread(ticketTask,"窗口2");

        t1.start();
        t2.start();
    }
}
synchronized 和 ReentrantLock区别
相同点:都实现了加锁功能
不同点:
     1.synchronized 是一个关键字,ReentrantLock是一个类
     2.synchronized修饰代码块和方法,ReentrantLock只能修饰代码块
     3.synchronized可以隐式的加锁和释放锁,运行过程中如出现了异常可以自动释放
       R
创建线程方式3:  实现Callable 接口
     重写call()方法
     call() 可以有返回值,可以抛出异常,还可以自定义返回结果的类型
     eentrantLock需要手动的添加锁和释放锁,建议在finally代码块中释放
实现Callable接口与使用Runnable相比,Callable功能更强大些.
相比run()方法,可以有返回值
方法可以抛出异常
支持泛型的返回值
需要借助FutureTask类,获取返回结果
package day16thread.demo7;

import java.util.concurrent.Callable;

public class SumTask<T> implements Callable<T> {

    @Override
    public T call() throws Exception {
        Integer i=0;
        for(int j=0;j<10;j++){
            i+=j;
        }
        return (T)i;
    }
}
package day16thread.demo7;

import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;

public class Test {
    public static void main(String[] args) {
        SumTask<Integer> sumTask=new SumTask<>();
        FutureTask<Integer> futureTask=new FutureTask(sumTask);
        Thread thread=new Thread(futureTask);
                thread.start();

        try {
            Integer integer=futureTask.get();//获取到线程返回结果
            System.out.println(integer);
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        }
    }
}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值