Java线程相关概念
要想明白线程的概念,我们要先搞清楚进程的概念
进程
进程的概念
为了使参与并发执行的每个程序都能独立运行,在操作系统中必须为止分配一个专门的数据结构,就叫做进程。
进程是指运行中的程序,比如说我们在电脑上玩LOL,就启动了一个进程,操作系统就会为该进程分配内存空间
进程的特征
1.动态性:进程有自身的产生,存在和消亡过程。
2.并发性:是指多个进程一起存在于内存中,且能够在一段时间内同时运行,引入进程的目的也是为了使一个进程能与其他进程同时执行。
3.独立性:进程是一个能独立运行,独立获取资源和独立接受调度的基本单位。
4.异步性:进程按异步方式运行的,即按各自独立的、不可预知的速度运行。
线程
线程的概念
1.线程由进程创建的,是进程的一个实体。
2.一个进程可以有多个线程,如下图所示。
可以理解为一个浏览器是一个进程,而每个网页就是一个线程。
线程其他相关的概念
单线程
同一个时刻,只允许执行一个线程。
多线程
同一个时刻,可以执行多个线程,比如:一个qq进程,可以同时打开多个聊天窗口。
并发
同一个时刻,多个任务交替执行,造成一种“同时执行”的错觉,简单的说,单核cpu实现的多任务就是并发。
并行
同一个时刻,多个任务同时执行。多核cpu可以实现并行。比如说在电脑上我可以边听音乐边打游戏。
Java中创建线程的两种方式
继承Thread类,重写run方法
下面我们给出一道题来引出线程
在控制台每隔一秒钟输入一个数字,输出1-10。
public static void main(String[] args) {
Cat cat = new Cat();
cat.start();//cat.start()是开始执行线程
}
class Cat extends Thread{
int count = 0;
@Override
public void run() {
while (true){
System.out.println("子线程执行"+ ++count+"次");
try {
Thread.sleep(1000);//Thread.sleep()是休眠的意思,这里的1000单位是ms
//意思是每执行一次线程,需要休眠1秒后再开始执行
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
if(count == 10){//当count=10时,就会终止线程
System.out.println("子线程结束");
break;
}
}
}
}
实现Runnable接口,重写run方法
public static void main(String[] args) {
Cat cat = new Cat();
Thread thread = new Thread(cat);
thread.start();
}
class Cat implements Runnable{
int count = 0;
@Override
public void run() {
while (true){
System.out.println("子线程执行"+ ++count+"次");
try {
Thread.sleep(1000);//Thread.sleep()是休眠的意思,这里的1000单位是ms
//意思是每执行一次线程,需要休眠1秒后再开始执行
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
if(count == 10){//当count=10时,就会终止线程
System.out.println("子线程结束");
break;
}
}
}
}
Java中线程的相关方法
setName和getName
setName: 设置线程名称
getName: 返回该线程的名称
public static void main(String[] args) {
Cat cat = new Cat();
Cat cat1 = new Cat();
Cat cat2 = new Cat();
System.out.println(cat.getName());
System.out.println(cat1.getName());
System.out.println(cat2.getName());
}
可以看到,我们用getName方法获得了线程的名称
public static void main(String[] args) {
Cat cat = new Cat();
Cat cat1 = new Cat();
Cat cat2 = new Cat();
cat2.setName("线程2");
System.out.println(cat.getName());
System.out.println(cat1.getName());
System.out.println(cat2.getName());
}
可以看到,我们用setName方法将Thread-2的名称改为了线程2
start和run
run:调用线程对象run方法
start:使该线程开始执行;
start底层会创建新的线程,调用run, run 就是一个简单的方法调用,不会启动新线程。
Java虚拟机底层调用该线程的start0方法,start0方法又调用了run方法
public synchronized void start() {
/**
* This method is not invoked for the main method thread or "system"
* group threads created/set up by the VM. Any new functionality added
* to this method in the future may have to also be added to the VM.
*
* A zero status value corresponds to state "NEW".
*/
if (threadStatus != 0)
throw new IllegalThreadStateException();
/* Notify the group that this thread is about to be started
* so that it can be added to the group's list of threads
* and the group's unstarted count can be decremented. */
group.add(this);
boolean started = false;
try {
start0();
started = true;
} finally {
try {
if (!started) {
group.threadStartFailed(this);
}
} catch (Throwable ignore) {
/* do nothing. If start0 threw a Throwable then
it will be passed up the call stack */
}
}
}
private native void start0();
/**
* If this thread was constructed using a separate
* <code>Runnable</code> run object, then that
* <code>Runnable</code> object's <code>run</code> method is called;
* otherwise, this method does nothing and returns.
* <p>
* Subclasses of <code>Thread</code> should override this method.
*
* @see #start()
* @see #stop()
* @see #Thread(ThreadGroup, Runnable, String)
*/
@Override
public void run() {
if (target != null) {
target.run();
}
}
start()方法调用start0()方法后,该线程并不一定会立马执行,只是将线程变成了可运行状态。具体什么时候执行,取决于CPU,由CPU统一调度。
setPriority和 getPriority
高优先级的线程比低优先级的线程有更高的几率得到执行,实际上这和CPU的调度有关,有可能设置了优先级也不会出现想要的结果。
线程优先级范围[1-10]。
setPriority:更改线程的优先级
getPriority:获取线程的优先级
public static void main(String[] args) {
Cat cat = new Cat();
Cat cat1 = new Cat();
System.out.println(cat.getPriority());
System.out.println(cat1.getPriority())
}
用getPriority方法获得了线程的优先级
public static void main(String[] args) {
Cat cat = new Cat();
Cat cat1 = new Cat();
cat.setPriority(9);
System.out.println(cat.getPriority());
System.out.println(cat1.getPriority());
}
用setPriority方法将线程cat的优先级改为了9
sleep和interrupt
sleep: 在指定的毫秒数内让当前正在执行的线程休眠(暂停执行)
interrupt:中断线程,但并没有真正的结束线程。一般用于中断正在休眠线程。
public static void main(String[] args) throws InterruptedException {
Cat cat = new Cat();
cat.setName("子线程");
cat.start();
for (int i = 0; i < 3; i++) {
Thread.sleep(1000);//休眠1000ms
System.out.println("主线程"+i);
}
cat.interrupt();
}
class Cat extends Thread{
@Override
public void run() {
while (true){
for (int i = 0; i < 3; i++) {
System.out.println(Thread.currentThread().getName()+i");
}
try {
System.out.println(Thread.currentThread().getName()+"休眠中");
Thread.sleep(30000);//休眠30000ms
} catch (InterruptedException e) {
System.out.println(Thread.currentThread().getName()+
"被interrupt了");
}
}
}
}
可以看到主方法中,先是创建了一个cat线程,然后让cat线程开始执行(cat.start())。
在线程类Cat中的run方法重写了。
先打印子线程0-子线程2,然后调用sleep方法使该线程休眠30s。
按道理来说应该等待30秒后又会重新开始打印子线程0-子线程2
即38s才开始打印子线程0-子线程2。
但是实际上是第9秒钟线程又恢复了打印。
这是因为当主线程打印完了主线程0-主线程2后,调用了cat.interrupt,该方法会中断正在休眠线程。
join
join:线程的插队。插队的线程一旦插队成功,则肯定先执行完插入的线程所有的任务。
public static void main(String[] args) {
int count = 0;
T t = new T();
Thread thread = new Thread(t);
thread.start();
for (int i = 1; i <= 5; i++) {
System.out.println(Thread.currentThread().getName()+ "小弟吃"+ ++count +"包子");
try {
Thread.sleep(1000);
if(count == 2){
System.out.println("小弟让大哥先吃");
thread.join();//当小弟吃到第二个包子时让大哥先吃
}
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
}
class T implements Runnable{
int count = 0;
@Override
public void run() {
while (true){
System.out.println(Thread.currentThread().getName()+"大哥吃"+ ++count +"包子");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
if (count == 5){
break;
}
}
}
}
该程序一开始是主线程和子线程一起运行,即大哥(子线程)和小弟(主线程)一起吃包子。
当小弟(主线程)吃到第二个包子时(count == 2)时,小弟让大哥先吃(thread.join),大哥(子线程)插队。
当大哥(子线程)吃完5个包子(count == 5时),子线程退出。
然后小弟再吃(主线程再执行)。