线程基础介绍

线程基础知识

本文主要是对线程的概念及用法做一个基础介绍


进程:内存中运行的应用程序,每个进程都有一个独立的内存空间。

线程:进程中的一个执行路径,共享一个内存空间,线程之间可以自由切换,并发执行,一个进程最少一个线程(线程实际上是在进程的基础上进一步划分,一个进程启动之后,里面的若干执行路径又可以分划分成若干个线程)

需要明白一个线程调度的概念:
1、分时调度:所有线程轮流使用CPU使用权,平均分配每个线程占用CPU的时间
2、抢占式调度:优先让优先级高的线程使用CPU,如果线程的优先级相同(线程随机性),而java使用抢占式调度

一、线程使用(三种方式)

1、继承Thread类

1、继承 Thread 线程类
2、重写run方法(线程要执行的任务方法)  
3、实例MyThread 后,调用start方法来启动任务执行run方法

代码如下(示例):

public class MyThread extends Thread{
    @Override
    public void run(){
		 System.out.println("锄禾日当午");
    }
}

public class ThreadTest {
    public static void main(String[] args) {
        MyThread run  =new MyThread();
        run.start();
    }
}

2、Runnable接口实现

1、定义MyRunnable 类实现接口Runnable
2、重写run方法
3、实例MyRunnable,
4、实例Thread,并将实例MyRunnable对象传入其中
5、线程对象调用start方法

代码如下(示例):

public class MyRunnable implements Runnable{
    @override
    public void run(){
      System.out.println("锄禾日当午");
    }
}


public class ThreadTest {
    public static void main(String[] args) {
     	 //执行还是需要使用Thread
		//创建一个任务对象
		MyRunnable  r= new MyRunnable();
		//MyRunnable  r= new MyRunnable(name);//name 线程名称
		//创建一个线程,并分配一个任务
		Thread t =new Thread(r);
		t.start();
    }
}

3、带返回值的线程Callable

1、编写类,实现接口Callable
2、创建FutureTask对象,并传入第一步创建的Callable对象
3、通过Thread,启动线程

代码如下(示例):

public class MyCallable implements Callable<Integer>{

    @Override
    public Integer call() throws Exception {
     	System.out.println("子线程执行..");
        Thread.sleep(3000)      
        return 100;
    }
}

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

        //1、编写类,实现接口Callable
        //2、创建FutureTask对象,并传入第一步创建的Callable对象
        //3、通过Thread,启动线程
        Callable<Integer> mc = new MyCallable();
        FutureTask<Integer> ft = new FutureTask<>(mc);
        Thread t = new Thread(ft);
        t.start();
        //如果任务ft调用了get方法, 主线程会等到拿到子线程的返回值之后再继续执行
        try {
            Integer m =  ft.get();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        }
		//3秒之后再输出
        System.out.println("主线程执行..");
    }
}

二.线程中断

一个线程是一个独立的执行路径,它是否应该结束,应该由其自身决定

代码如下(示例):

class MyRunnable  implements Runnable{
    @Override
    public void run() {
        for (int i = 0; i <10 ; i++) {
            System.out.println(Thread.currentThread().getName()+":"+i);
            try {
                //在进行部分特殊的操作时,会检查中断标记,发现之后,就会进入catch块
                //Object.wait(), Object.wait(long), Object.wait(long, int),
                // Thread.sleep(long), Thread.interrupt(), Thread.interrupted()
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
                System.out.println("子线程结束");
                return;
            }
        }
    }
}


public static void main(Stirng[] arg){
    //创建一个任务对象
    MyRunnable  r= new MyRunnable();
    //创建一个线程,并分配一个任务
    Thread t =new Thread(r);
    //执行
    t.start();    
	//主线程循环 
     for (int i = 0; i <5 ; i++) {
            System.out.println(Thread.currentThread().getName()+":"+i);
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
    }
  //给线程t添加中断标记--》这里是主线程执行完毕,给子线程打标记后,
  //子线程再执行就会进入catch语句中,catch语句中return,子线程自动结束;
  t.interrupt()
}
输出结果:
Thread-0:1
main:1
Thread-0:2
main:2
main:3
Thread-0:3
Thread-0:4
main:4
Thread-0:5
子线程结束

三、线程安全问题

线程不安全:多个线程同时运行时,容易发生数据紊乱的问题

假设:count值为1,线程1执行到sleep后,失去时间片,此时count还是1,线程2或者线程3争抢执行,进入while循环,造成的结果就是count小于0

代码如下(示例):

public class ThreadTest {
    public static void main(String[] args) {
        //创建三个线程,执行同一个卖票任务
        Runnable run  =new MyRunnable();
        new Thread(run).start();
        new Thread(run).start();
        new Thread(run).start();
        //结果,count有可能小于0
    }
}

cpublic lass MyRunnable implements Runnable{
    private int count = 10
    @Override
    public void run() {
        while (count>0){
            System.out.println("正在卖票");
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            count--;
            System.out.println("出票成功,余票:"+count);
        }

    }
}
结果:
正在卖票
出票成功,余票:4
正在卖票
出票成功,余票:3
正在卖票
出票成功,余票:2
正在卖票
出票成功,余票:1
正在卖票
出票成功,余票:0
出票成功,余票:-1
出票成功,余票:-2

解决方案有三种

1、方法1–同步代码块

代码如下(示例):

class MyRunnable implements Runnable{
    private int count = 10;
    private Object obj = new Object();
    @Override
    public void run() {
        while (true){
            synchronized (obj){//obj-->锁对象,一个线程运行时,其余两个线程在外面方位obj是否解锁,同一时间,只有一个线程能执行if语句
                if(count>0){
                    System.out.println("正在卖票");
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    count--;
                    System.out.println("出票成功,余票:"+count);
                }else{
                    break;
                }
            }
        }
    }
}

2、方法2–同步方法

锁的对象, 实例方法–锁的是this,当前调用对象
静态方法-锁的是类名.class 字节码class文件对象
多个同步方法存在且锁定同一个this时,只能有一个方法执行,其余都执行不了,(同一把锁的原因)

代码如下(示例):

class MyRunnable implements Runnable{
    private int count = 10;
    private Object obj = new Object();
    @Override
    public void run() {
        while (true){
            boolean flag = sale();
            if(!flag)
                break;
        }
    }
    //方法
    public synchronized boolean sale(){  
        if(count>0){
            System.out.println("正在卖票");
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            count--;
            System.out.println("出票成功,余票:"+count);
            return  true;
        }
        return false;
    }
}

3、方法3–显示锁 Lock -->使用子类 ReentrantLock

代码如下(示例):

class MyRunnable implements Runnable{
    private int count = 10;
    //实例一个锁对象
    private Lock l= new ReentrantLock ();
    @Override
    public void run() {
        while (true){
                 l.lock();//上锁
                if(count>0){
                    System.out.println("正在卖票");
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    count--;
                    System.out.println("出票成功,余票:"+count);
                }else{
                    break;
                }
            }
            l.unlock();//解锁      
    }
}

四、多线程通信

Object 方法
wait():导出当前线程等待被唤醒,可以传入具体的时候,
notify():唤醒正在此对象监视器上等待的单个线程
notifyAll():唤醒正在此对象监视器上等待的所有线程
代码如下(示例):

//多线程通信问题--生产者与消费者
//厨师产生一份食物,服务员取走一份,交替执行
public class ManyThreadNews {
    public static void main(String[] args) {
    	//穿件
        Food f = new Food();
        new Cook(f).start();
        new Waiter(f).start();
    }
}
//厨师
class Cook extends Thread{
    private Food f;
    public Cool(Food f) {
        this.f = f;
    }
    @Override
    public void run() {
        //循环100次做菜-->厨师线程循环100次,生成各50份的菜式
        for (int i = 0; i < 100; i++) {
            if(i%2==0){
                f.setNameAndTaste("白面馒头","白味的");
            }else{
                f.setNameAndTaste("大包子","甜甜的");
            }
        }
    }
}
//服务员
class Waiter extends Thread{
    private Food f;
    public Waiter(Food f) {
        this.f = f;
    }
    @Override
    public void run() {
        for (int i = 0; i < 100; i++) {
            f.get();//取走
        }
    }
}
//食物
//为什么加了同步关键字也不行?因为,方法解锁之后,同一个线程可能又抢到了时间片,继续执行(现在需要的是交替执行)
class Food{
    private String name;
    private String taste;
    //true,厨师执行,false,服务员执行
    private boolean flag = true;
    //厨师生成一份,设置名称和味道
    synchronized void  setNameAndTaste(String name, String taste){
       if(flag){
           this.name = name;
           //为了显示效果,此处等待100ms,此时,厨师线程有可能失去时间片,但菜名已经更新,而服务员开始执行取走任务,味道是上一次的,数据紊乱
           try {
               Thread.sleep(100);
           } catch (InterruptedException e) {
               e.printStackTrace();
           }
           this.taste =taste;
           this.flag = false;
           //此时还不行,还需要此线程睡眠,并唤醒服务员线程,才能交替执行
           this.notifyAll();//唤醒睡眠的线程
           try {
               this.wait();//当前线程进入睡眠
           } catch (InterruptedException e) {
               e.printStackTrace();
           }

       }
    }
    //服务员取走一份菜
    synchronized void get(){
       if(!flag){      
           System.out.println("服务员取走:"+this.name+",味道:"+this.taste);
           this.flag = true ;
           this.notifyAll();
           try {
               this.wait();
           } catch (InterruptedException e) {
               e.printStackTrace();
           }
       }
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值