用户既可以使用计算机听歌,也可以使用它打印文件,而这些活动完全可以同时进行,这种思想放在java中被称为并发,而将并发完成的每一件事情称为线程。
实现线程的两种方式:
在java种主要提供两种方式实现线程,分别为继承java.lang.Thread类与实现java.lang.Runnable接口。
继承Thread类方式
首先需要定义的线程类继承Thread类,然后重写覆盖run()方法,run()方法中的代码就是线程索要完成的任务代码,然后在执行Thread中的start()方法执行线程,也就是调用run()方法。
代码示例:
package com.huangxt.thread;
/**
* @Title: MyThread
* @Description:
* @Auther: huangxt
* @Version: 1.0
* @create 2019/7/11 16:25
*/
public class MyThread1 extends Thread{
@Override
public void run() {
System.out.println("执行线程任务代码");
}
public static void main(String[] args) {
Thread thread = new MyThread1();
thread.start();
}
}
“执行代码时控制台会打印出执行线程任务代码”
实现Runnable接口方式
当一个自定义的线程类已经继承了一个别的什么类,就不能再继承Thread类了,此时就可以实现Runnable接口一样的可以完成线程定义。
在源码中Thread也是实现了Runnable接口的。
实现Runnable接口的程序会创建一个Thread对象,并将Runnable对象与Thread对象相关联。
Thread类中的两个构造方法:
1、public Thread(Runnable target)。
2、public Thread(Runnable target,String name)。
这两个构造方法的参数中都存在Runnable实例。使用这两个构造方法可以将Runnable实例与Thread相关联。
使用Runnable接口步骤如下,定义一个实现Runnable接口的类,并重写run()方法,该方法是和Thread类中的run()方法一样的都是线程任务的代码。然后再用Thread类的以上两个构造生成Thread对象,执行start()方法。
代码示例:
package com.huangxt.thread;
/**
* @Title: MyRunnable
* @Description:
* @Auther: huangxt
* @Version: 1.0
* @create 2019/7/11 16:59
*/
public class MyRunnable implements Runnable{
@Override
public void run() {
System.out.println("执行实现Runnable的线程任务代码");
}
public static void main(String[] args) {
MyRunnable myRunnable = new MyRunnable();
Thread thread = new Thread(myRunnable);
thread.start();
}
}
线程的生命周期
线程的生命周期有7种状态,分别是出生状态,就绪状态、运行状态、等待状态、休眠状态、阻塞状态和死亡状态。
出生状态:就是线程被创建时处于的状态,在用户使用该线程实例调用start()之前都属于出生状态。
就绪状态:当执行start()方法后。
运行状态:当线程获得系统资源后就进入运行状态。
等待状态:在运行状态下调用wait()方法时,该线程便进入等待状态,进入等待状态的进程必须调用notify()方法才能被唤醒,而notifyAll方法时将所有处于等待状态的线程唤醒。
休眠状态:当线程调用Thread类的sleep()方法时,线程进入休眠状态。
阻塞状态:当线程运行状态时发出输入/输出请求,该线程进入阻塞状态。在其等待输入/输出完成进入就绪状态,对于阻塞的线程来说,即使系统资源空闲,线程依然不能回到运行状态。
死亡状态:当线程的run()方法执行完毕时,线程进入死亡状态。
线程的休眠
Thread.sleep(1000),sleep()是一个静态方法。
线程的加入
如果某程序为多线程程序,假如存在一个线程A,现在需要插入线程B,并要求线程B先执行完毕,然后在继续执行线程A,此时可以使用Thread类中的join()方法来完成。这就好比甲某正在看电视,突然有乙某上门收水费,甲某需要付完水费再继续看电视。
package com.huangxt.thread;
/**
* @Title: JoinMothedTest
* @Description:
* @Auther: huangxt
* @Version: 1.0
* @create 2019/7/11 19:02
*/
public class JoinMothedTest {
private Thread threadA = new Thread(new Runnable(){
public void run(){
System.out.println("开始交水费");
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("完成交水费");
}
});
private Thread threadB = new Thread(new Runnable(){
public void run(){
System.out.println("开始看电视");
try {
threadA.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("结束看电视");
}
});
public static void main(String[] args) {
JoinMothedTest joinMothedTest = new JoinMothedTest();
joinMothedTest.threadB.start();
joinMothedTest.threadA.start();
}
}
线程的中断
以往有的时候回使用stop()方法停止线程,但当前版本的JDK早已废除了stop()方法,不建议使用stop()方法停止一个线程的运行。
可以设置是否符合条件结束线程。如下代码:
package com.huangxt.thread;
/**
* @Title: StopThread
* @Description:
* @Auther: huangxt
* @Version: 1.0
* @create 2019/7/11 19:32
*/
public class StopThread implements Runnable{
private boolean isStop = false;
@Override
public void run() {
while (true){
//...
if(isStop)
break;
}
}
public void setStop(){
this.isStop = true;
}
}
线程的礼让
在Thread类中提供了一种礼让方法,使用yield()方法表示,他只是给当前正处于运行撞他的线程一个提醒,告知它可以将资源礼让给其他线程,但这仅是一种暗示,没有任何一种机制保证当前线程会将资源礼让。
yeild()方法使具有同样优先级的线程有进入可执行状态的机会,当当前线程放弃执行权时会再度回到就绪状态,对于支持多任务的操作系统来说,不需要调用yield()方法,因为操作系统会为线程自动分配CPU时间片来执行。
线程的优先级
线程有优先级之说,如果有很多线程处于就绪状态,优先级越大的线程就越先执行进入运行状态,优先级越小的运行的几率较小。
在Thread中包含成员变量代表线程的某些优先级,如MIN_PRIORITY等于1,NORM_PRIORITY等于5,MAX_PRIORITY=10。
线程优先级可以使用setPriority()方法调整,如果设置的优先级不在1~10之内,将产生IllegalArgmentException异常。
package com.huangxt.thread;
/**
* @Title: Priority
* @Description:
* @Auther: huangxt
* @Version: 1.0
* @create 2019/7/11 20:06
*/
public class Priority {
public static void main(String[] args) {
Thread thread1 = new MyThread1();
Thread thread2 = new MyThread1();
Thread thread3 = new MyThread1();
Thread thread4 = new MyThread1();
thread1.setPriority(8);
thread2.setPriority(8);
thread3.setPriority(4);
thread4.setPriority(1);
}
}
线程安全
何为线程安全?好比两个线程同时抢占资源的冲突,线程安全问题源于两个或者多个线程同时存取单一对象的数据。比如售票系统。
不处于线程安全问题票取到后面可能变成负数。如下代码:
package com.huangxt.thread;
/**
* @Title: TestThread
* @Description:
* @Auther: huangxt
* @Version: 1.0
* @create 2019/6/21 15:28
*/
public class TestThread implements Runnable{
int num = 10;
@Override
public void run() {
while (true){
//synchronized ("") {
if (num > 0) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}else{
break;
}
System.out.println("tickets" + --num);
// }
}
}
public static void main(String[] args) {
TestThread testThread = new TestThread();
Thread threadA = new Thread(testThread);
Thread threadB = new Thread(testThread);
Thread threadC = new Thread(testThread);
Thread threadD = new Thread(testThread);
threadA.start();
threadB.start();
threadC.start();
threadD.start();
}
}
为了处理线程安全问题引出了线程同步机制
1、同步块
在java中提供了同步机制,可以有效地防止资源冲突。同步机制使用synchronized关键字。
同步块(同步代码块)语法:
synchronized(Object){
}
实例代码:
package com.huangxt.thread;
/**
* @Title: TestThread
* @Description:
* @Auther: huangxt
* @Version: 1.0
* @create 2019/6/21 15:28
*/
public class TestThread implements Runnable{
int num = 10;
@Override
public void run() {
while (true){
synchronized ("") {
if (num > 0) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}else{
break;
}
System.out.println("tickets" + --num);
}
}
}
public static void main(String[] args) {
TestThread testThread = new TestThread();
Thread threadA = new Thread(testThread);
Thread threadB = new Thread(testThread);
Thread threadC = new Thread(testThread);
Thread threadD = new Thread(testThread);
threadA.start();
threadB.start();
threadC.start();
threadD.start();
}
}
2、同步方法
同步方法就是在方法前面修饰synchronized关键字的方法,语法如下:
synchronized void function(){}
当某对象调用同步方法时,该对象的其他同步方法需要等待该同步方法执行完后才能被执行。
实例:
package com.huangxt.thread;
/**
* @Title: TestThread
* @Description:
* @Auther: huangxt
* @Version: 1.0
* @create 2019/6/21 15:28
*/
public class TestThread implements Runnable{
int num = 10;
@Override
public void run() {
doit();
}
public synchronized void doit(){
while (true){
synchronized ("") {
if (num > 0) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}else{
break;
}
System.out.println("tickets" + --num);
}
}
}
public static void main(String[] args) {
TestThread testThread = new TestThread();
Thread threadA = new Thread(testThread);
Thread threadB = new Thread(testThread);
Thread threadC = new Thread(testThread);
Thread threadD = new Thread(testThread);
threadA.start();
threadB.start();
threadC.start();
threadD.start();
}
}