一、如何在自定义的代码中,自定义一个线程呢?
通过对api的查找,发现java已经提供了对线程这类事物的描述。就是Thread类
二、Thread类
线程 是程序中的执行线程。Java 虚拟机允许应用程序并发地运行多个执行线程。
public class Thread implements Runnable { //变量------------------------------------- private Runnable target; private char name[]; //字段------------------------------------- //线程可以具有的最低优先级。 public final static int MIN_PRIORITY = 1; //分配给线程的默认优先级。 public final static int NORM_PRIORITY = 5; //线程可以具有的最高优先级。 public final static int MAX_PRIORITY = 10; //构造方法---------------------------------- //1,构造方法:分配新的 Thread 对象。 public Thread() { init(null, null, "Thread-" + nextThreadNum(), 0); } //2,构造方法 public Thread(Runnable target) { init(null, target, "Thread-" + nextThreadNum(), 0); } //3,构造方法 public Thread(String name) { init(null, null, name, 0); } //4,构造方法 public Thread(Runnable target, String name) { init(null, target, name, 0); } //方法--------------------------------------- //1,如果该线程是使用独立的 Runnable 运行对象构造的,则调用该 Runnable 对象的 run 方法;否则,该方法不执行任何操作并返回。 public void run() { if (target != null) { target.run(); } } //2,使该线程开始执行;Java 虚拟机调用该线程的 run 方法。 public synchronized void start() { if (threadStatus != 0 || this != me) throw new IllegalThreadStateException(); group.add(this); start0(); if (stopBeforeStart) { stop0(throwableFromStop); } } private native void start0(); //3,返回该线程的字符串表示形式,包括线程名称、优先级和线程组。 public String toString() { ThreadGroup group = getThreadGroup(); if (group != null) { return "Thread[" + getName() + "," + getPriority() + "," + group.getName() + "]"; } else { return "Thread[" + getName() + "," + getPriority() + "," + "" + "]"; } } //4,返回该线程的名称。 public final String getName() { return String.valueOf(name); } //5,返回该线程的标识符。 public long getId() { return tid; } //6,返回线程的优先级。 public final int getPriority() { return priority; } //7,中断线程。 public void interrupt() { //......... } //8,等待该线程终止。 //当a线程执行到了b线程的.join()方法时,a就会等待,等b线程都执行完,a才会执行 //join可以用来临时加入线程执行 public final void join() throws InterruptedException { join(0); } //等待该线程终止的时间最长为 millis 毫秒 public final synchronized void join(long millis)throws InterruptedException { long base = System.currentTimeMillis(); long now = 0; if (millis < 0) { throw new IllegalArgumentException("timeout value is negative"); } if (millis == 0) { while (isAlive()) { wait(0);//等待方法继承自Object } } else { while (isAlive()) { long delay = millis - now; if (delay <= 0) { break; } wait(delay);//等待方法继承自Object now = System.currentTimeMillis() - base; } } } //等待该线程终止的时间最长为 millis 毫秒 + nanos 纳秒。 public final synchronized void join(long millis, int nanos)throws InterruptedException { } //9,将该线程标记为守护线程或用户线程。当正在运行的线程都是守护线程时,Java 虚拟机退出。该方法必须在启动线程前调用。 //Thread t1=new Thread();t1.setDaemon(true); //这时t1就是守护线程,就相当于后台线程,当前台线程都结束时,它自动结束了,就像是前台线程的守护神一样 public final void setDaemon(boolean on) { checkAccess(); if (isAlive()) { throw new IllegalThreadStateException(); } daemon = on; } //10,改变线程名称,使之与参数 name 相同。 public final void setName(String name) { checkAccess(); this.name = name.toCharArray(); } //11,更改线程的优先级。(1-10默认是5) public final void setPriority(int newPriority) { //......... } //12,在指定的毫秒数内让当前正在执行的线程休眠(暂停执行),此操作受到系统计时器和调度程序精度和准确性的影响。 public static native void sleep(long millis) throws InterruptedException; //在指定的毫秒数加指定的纳秒数内让当前正在执行的线程休眠(暂停执行),此操作受到系统计时器和调度程序精度和准确性的影响 public static void sleep(long millis, int nanos) throws InterruptedException //....经过运算得出最终millis sleep(millis); } //13,暂停当前正在执行的线程对象,并执行其他线程。 public static native void yield(); //14,返回对当前正在执行的线程对象的引用。 public static native Thread currentThread(); }
三、创建线程的第一种方式:继承Thread类
- 定义类继承Thread
- 重写Thread类中的run方法
- 目的:将自定义代码存储在run方法中,让线程运行
- 调用线程的start方法
- 该方法有两个作用:启动线程,调用run方法
class Demo extends Thread { public void run() { for(int x=0;x<60;x++) System.out.println("demo run:"+x); } } class ThreadDemo { public static void main(String[] args) { Demo d = new Demo(); d.start(); for(int x=0;x<60;x++) System.out.println("main run:--------"+x); } }
- 发现上面代码运行结果每一次都不同
- 因为多个线程都在获取cpu的执行权,cpu执行到谁,谁就运行
- 明确一点,在某一个时刻,只能有一个程序在运行,(多核除外)
- Cpu在做着快速的切换,以达到看上去是同时运行的效果
- 我们可以形象的把多线程的运行形容为在互相抢夺cpu的执行权
- 这就是多线程的一个特性:随机性。谁抢到谁执行,至于执行多久,cpu说了算
四、为什么要覆盖run方法呢?
- Thread类用于描述线程
- 该类就定义了一个用于存储线程要运行的代码的功能,该存储功能就是run方法,
- 也就是说Thread类中的run方法,用于存储线程要运行的代码
- 示例:
class Demo extends Thread { Demo(String name) { super(name);//设置线程名称 } public void run() { for(int x=0;x<60;x++) System.out.println(Thread.currentThread().getName()+x); } } class ThreadDemo { public static void main(String[] args) { Demo d = new Demo("haha"); d.start(); Demo d1 = new Demo("heihei"); d1.start(); for(int x=0;x<60;x++) System.out.println(Thread.currentThread().getName()+"---"+x); } }
- 线程默认的名称:Thread-编号 该编号从 0 开始
- 设置线程名称: setName()或者构造函数(super())
五、创建线程的第二种方式:实现Runnable接口
- 定义类实现Runnable接口
- 覆盖Runnable接口中的run方法----》将线程要运行的代码存放在run方法中
- 通过Thread类建立线程对象
- 将Runnable接口的子类对象作为实际参数传递给Thread类的构造函数,
- 为什么要将Runnable接口的子类对象传递给Thread的构造函数。
- 因为,自定义的run方法所属的对象是Runnable接口的子类对象。
- 所以要让线程去指向指定对象的run方法,就必须明确该run方法所属的对象
- 调用Thread类的start方法开启线程并调用Runnable接口子类的run方法
- Runnable接口
public interface Runnable { //此接口只有这么一个成员 /** *Runnable
接口应该由那些打算通过某一线程执行其实例的类来实现。 * 设计该接口的目的是为希望在活动时执行代码的对象提供一个公共协议。 * 例如,Thread
类实现了Runnable
。 * 激活的意思是说某个线程已启动并且尚未停止。 * <p> * 此外,Runnable
为非Thread
子类的类提供了一种激活方式。 * 通过实例化某个Thread
实例并将自身作为运行目标, * 就可以运行实现Runnable
的类而无需创建Thread
的子类。 * @see java.lang.Thread#run() */ public abstract void run(); }
六、实现方式和继承方式的区别?
- 实现方式避免了单继承的局限性
- 继承Thread:线程代码存放在Thread子类run方法中
- 实现Runnable:线程代码存在接口的子类的run方法中
- 在定义线程时,建议使用实现方式
七、示例
/** 需求:买车票 */ class Ticket implements Runnable { private int tick = 100; public void run() { while(true) { if(tick > 0) { try{Thread.sleep(10);}catch(Exception e){} System.out.println(Thread.currentThread().getName()+"::"+tick); } } } } class ThreadDemo { public static void main(String[] args) { Ticket t = new Ticket(); new Thread(t).start(); new Thread(t).start(); new Thread(t).start(); new Thread(t).start(); new Thread(t).start(); } }
- 结果可能会出现0 -1 -2 等错票
- 多线程的运行出现了安全问题
- 这里写Thread.sleep(10);是模仿进程特别多的时候出现的情况
八、同步代码块
- 问题原因:当多条语句在操作共享数据时,一个线程对多条语句只执行了一部分,还没有执行完,另一个线程参与进来执行,导致共享数据的错误
- 解决方法:对多条操作共享数据的语句,只能让一个线程都执行完,在执行过程中,其他线程不可以参与执行
- Java对于多线程的安全问题提供了专业的解决方式:就是同步代码块
- 格式:
synchronized(Object) { //需要被同步的代码 }
- 所以上面的代码可以优化如下:
-
class Ticket implements Runnable { private int tick = 100; Object obj = new Object(); public void run() { while(true) { synchronized(obj) { if(tick > 0) { try{Thread.sleep(10);}catch(Exception e){} System.out.println(Thread.currentThread().getName()+"::"+tick--); } } } } }
- obj如同锁,持有锁的线程可以在同步中执行
- 没有持有锁的线程即使获取cpu的执行权,也进不去,因为没有获取锁
- 同步的前提:必须要有两个或者两个以上的线程使用同一个锁
- 好处:解决了多线程的安全问题
- 弊端:多个线程需要判断锁,较为消耗资源
九、同步函数
如:
class Bank { private int sum; public synchronized void add(int n) { sum += n; System.out.println("sum:"+sum); } }
- 只需要用synchronized修饰函数即可,非常简洁方便
- 同步函数就是把操作共享数据的语句封装起来,但是只能封装需要同步的函数,否则还不如用同步代码块呢!
- 同步函数用的是哪一个锁呢?
- 函数需要被对象调用,那么函数都有一个所属对象引用,就是this
- 所以同步函数使用的锁是this
- 可以通过程序来进行验证
- 如果同步函数被静态修饰后,使用的锁是什么呢?
- 通过验证,发现不再是this,因为静态方法中也不可以定义this
- 静态进内存时,内存中没有本类对象,但是一定有该类对应的字节码文件对象—类名.class 该对象的类型是Class
- 静态的同步方法,使用的锁是该方法所在类的字节码文件对象,类名.class
十、死锁
同步中嵌套同步的时候,容易发生死锁
相关推荐
### 编译时出现java.lang.OutOfMemoryError Java heap space异常 #### 一、问题概述 在进行Java项目编译的过程中,可能会遇到`java.lang.OutOfMemoryError: Java heap space`这种异常。这类异常通常表明Java虚拟机...
在Java 2(J2SE)中,`java.lang`包添加了一些新的类和方法,如`InheritableThreadLocal`,`ThreadLocal`的扩展,使得子线程可以继承父线程的变量,以及`SecurityManager`,用于实现安全管理策略。 过时方法在Java ...
Java中的“内存不足OOM (Out Of Memory):java.lang.OutOfMemoryError”是一个常见的运行时错误,它表示Java虚拟机(JVM)在尝试分配新的对象或数据结构时,发现系统内存不足以完成此操作。这个问题通常发生在程序...
在Java 2中,一些新类和方法被添加到`java.lang`包,如`ThreadLocal`、`RuntimePermission`等,以增强安全性、多线程支持和性能优化。 值得注意的是,虽然`java.lang`包中的许多类在早期版本的Java中就已经存在,但...
java.lang包还包含了一些与安全、线程和内存管理相关的类,如SecurityManager用于安全管理,Thread和ThreadGroup是线程相关的类,ClassLoader负责加载类,ThreadLocal为每个线程提供独立的变量副本,而Runtime类提供...
6. **Thread类**:多线程是Java的重要特性。`Thread`类代表程序中的一个执行线程,提供了创建、控制和管理线程的方法,如`start()`(启动线程)、`sleep()`(使线程暂停一段时间)和`join()`(等待当前线程结束)。 ...
"深入研究java.lang.Runtime类" java.lang.Runtime 类是 Java 语言中一个非常重要的类,它提供了访问当前 Java 应用程序的 Runtime 环境的能力。每个 Java 应用程序都有一个 Runtime 类实例,使应用程序能够与其...
2. **运行时系统接口**:Java.lang包还包含了一些用于控制程序执行的类,如`System`、`Runtime`和`Thread`。`System`类提供了一些系统相关的属性和方法,如获取当前时间(`currentTimeMillis()`)。`Runtime`类代表了...
- 解决方案:避免嵌套锁,使用`Thread.dumpStack()`定位死锁,使用`java.util.concurrent`包中的工具类避免死锁。 7. **垃圾回收(Garbage Collection)** - 原因:内存中存在大量无法回收的对象,导致GC频繁。 ...
在Java中,线程是通过`java.lang.Thread`类或者实现`Runnable`接口来创建和管理的。这个资料包包含的是关于Java线程的中英文参考资料,包括书籍和源代码。 "Java.Threads,3rd.Edition.chm"可能是一个关于Java线程的...
总结,`java.lang`包是Java的核心,包含了一系列基础类和接口,它们为Java程序提供了基本的运算、数据类型转换以及多线程、安全性等方面的支持。理解和熟练使用这些类和接口是掌握Java编程的关键。
java.lang.ThreadDeath 是一种线程结束,指的是在程序中调用 Thread 类的 stop 方法时抛出的异常。 30. java.lang.UnknownError 未知错误 java.lang.UnknownError 是一种未知错误,指的是在程序中 Java 虚拟机发生...
除了直接使用`ThreadMXBean`,我们还可以使用`Thread`类的静态方法`getAllStackTraces()`获取所有线程的堆栈跟踪映射,这对于定位线程问题也很有帮助。 此外,`jstack`工具是Java开发工具集(JDK)的一部分,它能以...
java.lang.OutOfMemoryError是Java开发者在工作中经常会遇到的一个内存错误。该错误通常发生在JVM无法为新对象分配足够的堆内存时。由于内存问题导致的程序崩溃给开发和维护带来了极大的挑战,而解决这类问题需要对...
Java编程语言中的java.lang包是一个核心包,它提供了Java语言运行时的基础类库,包含了Java程序运行的基本数据类型、异常处理类以及一些用于系统级别的工具类和接口。java.lang包中的类和接口不需要程序员显示地导入...
Java提供了三种方式用于创建线程,分别是继承java.lang.Thread类、实现java.lang.Runnable接口、应用java.util.concurrent.Callable接口与java.util.concurrent.Future接口。每种方式都有其优缺,需要根据实际工程...
使用java.lang.Thread类或者java.lang.Runnable接口编写代码来定义、实例化和启动新线程。 一个Thread类实例只是一个对象,像Java中的任何其他对象一样,具有变量和方法,生死于堆上。 Java中,每个线程都有一个...
在Java中,通常我们通过Java的`java.lang.Thread`类进行线程的创建和管理,但默认情况下,Java并不提供直接设置线程亲和性的API。Java-Thread-Affinity库填补了这一空白,它允许开发者在Java程序中方便地绑定线程到...
4. Java 供应了类 java.lang.Thread 来便利多线程编程,这个类供应了大量的方法来便利我们限制自己的各个线程。 5. 有两种方法可以实现多线程:继承 Thread 类和实现 Runnable 接口。 6. 继承 Thread 类可以通过覆盖...