十一、java线程

目录:

一、线程的基本概念

二、线程的创建和启动

三、线程的调度和优先级

四、线程的状态控制

五、线程同步

一、线程的基本概念

  • 线程是一个程序内部的顺序控制流
  • 线程和进程的区别:
  • 每个进程都由独立的代码和数据空间(进程上下文),进程间的切换会有较大的开销
  • 线程可以看成是轻量级的进程,同一类线程共享代码和数据空间,每个线程有独立的运行栈和程序计数器(pc),线程切换的开销小
  • 多进程:在操作系统中能同时运行多个任务(程序)
  • 多线程:在统一应用程序中有多个顺序流同时执行
  • java的线程是通过java.lang.Thread类实现的
  • VM启动时会有一个由主方法(public static void main(){})所定义的线程
  • 可以通过创建Thread的实例来创建新的线程
  • 每个线程都是通过某个特定Thread对象所对应的方法run()来完成其操作的,方法run()称为线程体
  • 通过调用Thread类的start()方法来启动一个线程

二、线程的创建和启动

有两种方式创建新的线程:

第一种:

  • 定义线程类实现Runnable接口
  • Thread myThread = new Thread (target) //target为uRunnable接口类型
  • Runnable中只有一个方法:public void run();可以定义线程运行体
  • 使用Runnable接口可以为多个线程提供共享的数据
  • 在实现Runnable接口的类和run方法定义中可恶意使用Thread的静态方法:Public static Thread currentThread() 获取当前线程的引用 \

看一个例子:

//实现Runnable接口
public class Runner1 implements Runnable{
    //实现run()方法
    public void run() {
        for(int i=0; i <100; i++) {
            System.out.println("Runner1 :"+i);
        }

    }

}
public class TestThread1 {
    public static void main(String[] args) {
        Runner1 r = new Runner1();
        //r.run();可以将下面两行注释掉然后调用这一行的代码查看并分析下结果
         Thread t = new Thread(r);
        t.start();

        for(int i=0; i<100; i++) {
            System.out.println("Main Thread:----"+i);
        }
    }
}

以上代码在运行的时候我们会发现t.satrt和main方法下的for里的输出是在交替进行输出,此时相当于是两个线程并行的情况

如果将thread t = new Thread(r);  t.start;注释掉,启用r.run相当于是方法调用,那么他在执行的时候就是先执行r.run(),执行完了之后才执行main下的for里面的内容

第二种:

  • 可以定义一个Thread的子类并重写其run方法,如:
class MyThread extends Thread {
    publiuc void run() {
        ......
    }
}
  • 然后生成该类的对象:
MyThread myThread = new MyThread(...)

看一个例子

public class Runner1 extends Thread{
    //实现run()方法
    public void run() {
        for(int i=0; i <100; i++) {
            System.out.println("Runner1 :"+i);
        }

    }
}
public class TestThread1 {
    public static void main(String[] args) {
        Runner1 r = new Runner1();
        //Thread t = new Thread(r);//此处就不需要再new一个thread了,直接调用r.start()就可以了
        r.start();

        for(int i=0; i<100; i++) {
            System.out.println("Main Thread:----"+i);
        }
    }
}

三、线程的调度和优先级

  • java提供一个线程调度器来监控程序中启动后进入就绪状态的所有线程,线程调度器按照线程的优先级决定应先调度哪个线程来执行
  • 线程的优先级用数字表示米范围从1到10,一个线程的缺省优先级是5

Thread.Min_PRIORITY = 1

Thread.Max_PRIORITY = 10

Thread.NORM_PRIORITY = 5

  • 使用getPriority()和setPriority(int newPriority)来后去和设置线程对象的优先级

看一个例子

class T1 implements Runnable{
    public void run() {
        for(int i=0; i<10; i++){
            System.out.println("T1 : "+i);
        }
    }
}
class T2 implements Runnable{
    public void run() {
        for(int i=0; i<10; i++){
            System.out.println("----T2 : "+i);
        }
    }
}
public class TestPriority {
    public static void main(String[] args) {
        Thread t1 = new Thread(new T1());
        Thread t2 = new Thread(new T2());
        t1.setPriority(Thread.NORM_PRIORITY +3);//把t1优先级调高
         t1.start();
        t2.start();
    }
}

在实际运行过程中把t1的优先级调高并不表示要先执行完t1再执行t2,而是cpu把大部分的空间都留给了t1,所有t1执行效率会比较高一点

四、线程的状态控制

线程状态转换:

线程控制基本方法:


方法


功能

isAlive() 判断线程是否还”活”着,即线程是否还未终止
getPriority() 获得线程的优先级数值
setPriority() 设置线程的优先级数值
Thread.sleep() 将当前线程睡眠指定毫秒数
join() 调用某线程的该方法,将当前线程与该线程”合并”,即等待该线程结束,再回复当前线程的运行
yield() 让出cpu,当前线程进入就绪队列等待调度
wait() 当前线程进入对象的wait pool
notify()
notifyAll()
唤醒对象的wait pool中的一个/所有等待线程

例子1:

public class MyThread extends Thread{
    public void run() {
        while(true) {
            System.out.println("==="+new Date()+"===");

            try {
                sleep(1000);
            } catch(InterruptedException e){
                return;
            }
        }
    }
}
public class TestInterrup {
    public static void main(String[] args) {
        MyThread thread = new MyThread();
        thread.start();
        try {
            Thread.sleep(10000);//主线程睡眠10秒
        } catch(InterruptedException e) {
            thread.interrupt();//打断该线程
        }
    }
}

例子2:

class MyThread2 extends Thread{
    //接收一个字符串
    MyThread2(String s) {
        super(s);
    }
    public void run() {
        for(int i=1; i<=10; i++) {
            System.out.println("i am "+getName());//获取到s
            try {
                sleep(1000);
            } catch (InterruptedException e) {
                return;
            }
        }
    }
}
public class TestJoin {
    public static void main(String[] args) {
        MyThread2 t1 = new MyThread2("t1");
        t1.start();
        try{
            t1.join();//t1合并到main中一块去执行,他执行完了之后才会执行主线程
        } catch (InterruptedException e) {
            for(int i=1; i<=10; i++) {
                System.out.println("i am Thread");
            }
        }
    }
}

例子3:

class MyThread3 extends Thread{
    MyThread3(String s) {
        super(s);
    }

    public void run() {
        for(int i=1; i<=100; i++) {
            System.out.println(getName()+": "+i);
            if(i % 10 == 0) {
                yield();
            }
        }
    }
}
public class TestYield {
    public static void main(String[] args) {
        MyThread3 t1 = new MyThread3("11111");
        MyThread3 t2 = new MyThread3("22222");
        t1.start();
        t2.start();
    }
}

例子4:

class Runner4 implements Runnable{
    private boolean flag = true;
    public void run() {
        int i = 0;
        while(flag == true) {
            System.out.print(" "+i++);
        }
    }
    public void shutDown(){
        flag = false;
    }
}
public class TestThread4 {
    public static void main(String[] args) {
        Runner4 r = new Runner4();
        Thread t = new Thread(r);
        t.start();
        for(int i=0; i<1000; i++) {
            if(i%100 ==0 && i>0) {
                System.out.println("in thread main i="+i);
            }
        }
        System.out.println("Thread main is over-----------");
        r.shutDown();//用类似于这样的方法结束线程
    }
}
public class Runner6 extends Thread{
    public void run() {
        //currentThread()拿到当前线程
        System.out.println(Thread.currentThread().isAlive());//输出当前线程状态,活着true,死的false
        for(int i=0; i<50;i++) {
            System.out.println("SybThread: "+i);
        }
    }
}
public class TestThread6 {
    public static void main(String[] args) {
        Thread t = new Runner6();
        t.start();

        for(int i=0; i<50; i++) {
            System.out.println("MainThread: "+i);
        }
    }
}

五、线程同步

java语言中,引入了对象互斥锁的概念,保证共享数据操作的完整性,每个对象都对应于一个可成为“互斥锁”的标记,这个标记保证在任一时刻,只能有一个线程方位该对象

关键字synchronized来与对象的互斥练习。当某个对象synchronized修饰时,表明该对象在任一时刻只能由一个线程来访问

使用方法:

1.放到方法生命中,表示整个方法为同步方法,例如:public synchronized void add(){……}

2.使用sunchronized(this){}来包住需要同步的代码,被包住的部分就是要同步的部分

看一个例子:

public class Timer {

    private int num = 0;
    //第二种锁定对象的写法是这里写public synchronized void add (String name){ 
public void add(String name) {
        //第一种锁定对象的写法
//synchronized (this) {
        num++;
        try {
            Thread.sleep(1);
        } catch (InterruptedException e) {
        }
        System.out.println(name+",你是第"+num+"个使用timer的线程");
        //}
    }
}
public class TestSync implements Runnable{
    Timer timer = new Timer();
    public static void main(String[] args) {
        TestSync test = new TestSync();
        Thread t1 = new Thread(test);
        Thread t2 = new Thread(test);
        t1.setName("t1");
        t2.setName("t2");
        t1.start();
        t2.start();
    }
    //拿到当前线程的名子
    public void run() {
        timer.add(Thread.currentThread().getName());

    }
}

上面的程序执行完之后结果为:

因为线程在执行add方法的过程中被另外的线程给打断了,所以才会出现上面的情况

如果不想出现上面样的情况,将第一段代码的注释部分还原,相当于执行方法过程中锁定当前对象,不允许被打断,这样就不存在被打断的情况,运行结果:

这里引入一个死锁的概念:假设线程1和线程2都在执行的过程中都使用了两把锁:

线程1先执行锁1,再执行锁2,

线程2先执行锁2,再执行锁1,

那么当线程1和线程2同时启动的时候就会出现死锁的情况,可以想象一下一个只能通过一人的桥左右两边都有人要同时过桥的情景

看一个死锁的例子:

public class TestDeadLock implements Runnable {
    public int flag = 1;
    static Object o1 = new Object();
    static Object o2 = new Object();

    public void run() {
        System.out.println("flag="+flag);

        if (flag == 1) {
            synchronized(o1) {
                try {
                    Thread.sleep(500);
                } catch (Exception e) {
                    e.printStackTrace();
                }
                synchronized(o2) {
                    System.out.println("1");
                }
            }
        }

        if (flag == 0) {
            synchronized(o2) {
                try {
                    Thread.sleep(500);
                } catch (Exception e) {
                    e.printStackTrace();
                }
                synchronized(o1) {
                    System.out.println("0");
                }
            }
        }
    }

    public static void main(String[] args) {
        TestDeadLock td1 = new TestDeadLock();
        TestDeadLock td2 = new TestDeadLock();
        td1.flag = 1;
        td2.flag = 0;
        Thread t1 = new Thread(td1);
        Thread t2 = new Thread(td2);
        t1.start();
        t2.start();
    }
}

再看一个例子:

public class TT implements Runnable{
    int b = 100;

    public synchronized void m1() throws Exception {
        b = 1000;
        Thread.sleep(5000);
        System.out.println("b="+b);
    }

    public void m2() {
        System.out.println(b);
    }

    public void run() {
        try {
            m1();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public static void main(String[] args)  throws Exception{
        TT tt = new TT();
        Thread t = new Thread(tt);
        t.start();
        Thread.sleep(1000);
        tt.m2();
    }
}

上面这个例子的输出结果是b=1000,而不是想象中的100,说明被锁定的对象只是不允许另外的线程使用,但是其他的方法还是可以访问的

wait和sleep的区别:

wiat(Object类的方法)时别的线程可以访问锁定对象,调用wait方法的时候必需锁定该对象

sleep(Thread类的方法)时别的线程也不可以访问锁定对象

时间: 2024-11-07 01:16:59

十一、java线程的相关文章

Java 线程第三版 第三章数据同步 读书笔记

多线程间共享数据问题 一.Synchronized关键字 atomic一词与"原子"无关,它曾经被认为是物质的最小的单元,不能再被拆解成更小的部分. 当一个方法被声明成synchronized,要执行此方法的thread必须先取得一个token,我们将它称为锁.一旦该方法取得(或者说是获得)锁,它将运行此方法然后释放掉(或者返回)此锁.不管方法时怎样返回的(包括通过异常)该锁会被释放. 二.Volatile关键字 如果变量被标示为volatile,每次使用该变量时都必须从主寄存器中读出

JAVA线程万年历(阳历+阴历+节日+时间)

JAVA线程万年历(阳历+阴历+节日+时间) package myCalendar; import java.awt.Canvas; import java.awt.Color; import java.awt.Font; import java.awt.Graphics; import java.text.SimpleDateFormat; import java.util.Calendar; class Clock extends Canvas implements Runnable{ /*

Java线程专栏文章汇总

JDK5.0之前传统线程        Java线程(一):线程安全与不安全 Java线程(二):线程同步synchronized和volatile Java线程(三):线程协作-生产者/消费者问题 Java线程(四):线程中断.线程让步.线程睡眠.线程合并 Java线程(五):Timer和TimerTask JDK5.0之后并发包        Java线程(六):线程池 Java线程(七):Callable和Future Java线程(八):锁对象Lock-同步问题更完美的处理方式 Java

java 线程详解

5月7号  周末看了一下线程方面的内容 ,边看视频边看书还附带着参考了很多人的博客,一天的收获,写下来整理一下:感觉收获还是挺多的:过段时间可能看完java  这几大块要去看一下关于spring boot  的内容顺便  也整理一下:附上我参考的 几本书: 关于java  线程,首先要了解一下线程和进程之间的关系.区别以及他们之间的概念: 首先是线程: 什么是线程? 线程是在程序执行过程中能够执行部分代码的一个执行单元,也看看做是一个轻量级的进程:线程是程序内的程序控制流只能使用程序内分配给程序

Java线程工作内存与主内存变量交换过程及volatile关键字理解

Java线程工作内存与主内存变量交换过程及volatile关键字理解 1. Java内存模型规定在多线程情况下,线程操作主内存变量,需要通过线程独有的工作内存拷贝主内存变量副本来进行.此处的所谓内存模型要区别于通常所说的虚拟机堆模型: 2. 线程独有的工作内存和进程内存(主内存)之间通过8中原子操作来实现,如下图所示: 原子操作的规则(部分): 1) read,load必须连续执行,但是不保证原子性. 2) store,write必须连续执行,但是不保证原子性. 3) 不能丢失变量最后一次ass

java线程

Java线程详解 1.操作系统中的线程和进程讲解: 现在的操作系统大都是多任务操作系统,多线程是多任务的一种. 进程是指操作系统中运行的一个程序,每个进程都有自己的一块内存空间,一个进程中可以启动多个线程. 线程是指进程中的一个执行流程,一个进程中可以运行多个线程.比如java.exe进程中可以运行很多线程.线程总是属于某个进程,进程中的多个线程共享进程的内存. “同时”执行是人的感觉,在线程之间实际上轮换执行. Java线程的两种具体实现方法: 第一种继承:具体代码实现如下: Public (

Java 线程第三版 第四章 Thread Notification 读书笔记

一.等待与通知 public final void wait() throws InterruptedException 等待条件的发生. public final void wait(long timeout) throws InterruptedException 等待条件的发生.如果通知没有在timeout指定的时间内发生,它还是会返回. public final void wait(long timeout, int nanos) throws InterruptedException

Java线程使用大全

1.线程实现 1.Thread类 构造方法: 案例代码: public class Ex10_1_CaseThread extends Thread {// 创建一个类继承(extend)Thread类 String studentName; public Ex10_1_CaseThread(String studentName) {// 定义类的构造函数,传递参数 System.out.println(studentName + "申请访问服务器"); this.studentNam

java线程五种状态

java线程五种状态: 创建 -> 就绪 -> 运行 -> 销毁 创建 -> 就绪 -> 运行 -> 等待(缺少资源) -> 销毁 下图:各种状态转换

java线程详细介绍

目录(?)[-] 一扩展javalangThread类 二实现javalangRunnable接口 三Thread和Runnable的区别 四线程状态转换 五线程调度 六常用函数说明 使用方式 为什么要用join方法 七常见线程名词解释 八线程同步 九线程数据传递 本文主要讲了java中多线程的使用方法.线程同步.线程数据传递.线程状态及相应的一些线程函数用法.概述等. 首先讲一下进程和线程的区别: 进程:每个进程都有独立的代码和数据空间(进程上下文),进程间的切换会有较大的开销,一个进程包含1