线程 是进程中的执行过程,一个进程包含有多个线程
进程 是一个运行的应用程序,每个进程都有自己独立的内存空间。在Windows系统中,一个运行的exe(应用程序)就是一个进程
实现线程线程主要有 java.lang.Thread类 与 Runnable接口
Class Thread
java.lang.Object
java.lang.Thread
Thread类中实例化的对象代表线程,启动需要Thread实例
常用构造方法
Thread()
Thread(String name)
name: 自定义线程名
常用方法
修饰符 | 方法 | 说明 |
---|---|---|
long | getId() | 返回线程标识符 |
String | getName() | 返回线程名称 |
int | getPriority() | 返回线程的优先级 |
Thread.State | getState() | 返回线程的状态 |
void | interrupt() | 中断线程 |
boolean | isAlive() | 线程是否运行 |
boolean | isInterrupted() | 线程是否被中断 |
void | run() | 调用该Runnable对象的run方法 |
void | start() | 线程开始执行run方法 |
void | sleep(long ms) | 线程以指定的毫秒数暂停(等待) |
void | join() | 在线程中加入另一个线程 |
boolean | interrupted() | 中断线程 |
void | setPriority(int newPriority) | 设置线程的优先级newPriority的范围(1-10) |
String | toString() | 返回线程的字符串表示、线程的名、优先级、线程组 |
例子:
public class Demo {
public static void main(String[] args) {
//实例对象
Thread a = new MyThreadA();
Thread b = new MyThreadB();
/**其他方法测试*/
System.out.println("==========");
System.out.println("a.getId():"+a.getId());
System.out.println("a.getName():"+a.getName());
System.out.println("a.getPriority():"+a.getPriority());
System.out.println("a.getState():"+a.getState());
System.out.println("a.isAlive():"+a.isAlive());
System.out.println("a.isInterrupted():"+a.isInterrupted());
System.out.println("a.toString():"+a.toString());
System.out.println("==========");
//执行线程(双线程执行,同时执行)
/**线程A*/
a.start();
/**线程B*/
b.start();
/**
以下代码为运行a对象的run()方法再执行b对象run()方法
a.run();
b.run();
*/
}
}
class MyThreadA extends Thread{
@Override
public void run() {
for (int i = 0; i < 26; i++) {
System.out.print("A:"+i);
/**休眠1秒(等待)*/
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
class MyThreadB extends Thread{
@Override
public void run() {
for (char i = 'a'; i < 'z'; i++) {
System.out.println("B:"+i);
/**休眠1秒(等待)*/
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
/**运行结果
==========
a.getId():13
a.getName():No.1
a.getPriority():5
a.getState():NEW
a.isAlive():false
a.isInterrupted():false
a.toString():Thread[No.1,5,main]
==========
B:a
A:0
B:b
A:1
B:c
A:2
A:3
B:d
B:e
A:4
A:5
B:f
A:6
B:g
A:7
B:h
A:8
B:i
B:j
A:9
A:10
B:k
A:11
B:l
A:12
B:m
B:n
A:13
A:14
B:o
B:p
A:15
B:q
A:16
B:r
A:17
B:s
A:18
A:19
B:t
B:u
A:20
A:21
B:v
A:22
B:w
B:x
A:23
B:y
A:24
A:25
*/
线程一般通过Thread类创建的。如果该类要实现进程且该类又继承了其他类(非Thread类),可以通过Runnable接口实现进程
构造方法
Thread(Runnable target)
Thread(Runnable target , String name)
target: 启动线程时调用,run方法
name: 自定义线程名
例子:
import javax.swing.*;
import java.awt.*;
//继承JFrame类 且 实现Runnable接口
public class MyRunnable extends JFrame implements Runnable{
private JLabel jl = null;
private String str = "内容";
public MyRunnable(){
//标题
//setTitle("移动字体");
super("移动字体");
//窗体关闭模式
setDefaultCloseOperation(3);
//坐标大小
setBounds(400,400,280,80);
//获取窗体容器
Container c = getContentPane();
c.setLayout(null);
jl = new JLabel(str);
jl.setFont(new Font("微软雅黑",Font.PLAIN,20));
jl.setBounds(10,0,50,40);
c.add(jl);
//可见
setVisible(true);
}
@Override
public void run() {
//No.1改变字体内容实现移动 (空格
//String tmp = "";
//while (true){
// jl.setText(tmp+str);
// tmp += " ";
// //50空格清空
// if (tmp.length() > 50){
// tmp = "";
// }
// try {
// //等待 50毫秒
// Thread.sleep(50);
// } catch (InterruptedException e) {
// e.printStackTrace();
// }
//}
//No.2更改字体本身的位置 (坐标
int count = 10 ;
while(true){
//更改坐标
jl.setLocation(count++,0);
if(count >= 250) {
count = 10;
}
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public static void main(String[] args) {
MyRunnable runnable = new MyRunnable();
Thread t = new Thread(runnable);
t.start();
}
}
运行结果
线程具有生命周期,有7种分别为:出生状态、就绪状态、运行状态、休眠状态、阻塞状态、死亡状态
周期的说明:
出生状态: 线程创建后到线程的start()方法前
就绪状态: 可执行状态,当线程获取系统资源就进入运行状态
运行状态: run()方法,没有意外线程则一直运行到结束
休眠状态: 执行sleep()方法休眠(等待)
阻塞状态: 等待用户 输入/输出 ,用户 输入/输出 结束后进入就绪状态
死亡状态: 线程的run()方法执行完毕,则进入死亡状态
Windows操作系统中,会为每个线程分配一小段CPU时间片,一旦CPU时间片结束会切换到下一线程
再次进入运行状态:
- 线程调用notify()方法
- 线程调用notifyAll()方法
- 线程调用interrupt()方法
- 线程的休眠时间结束
- 输入/输出 结束
sleep(long ms) 方法 线程休眠(暂停。它可能会抛出InterruptedException异常,所以放在try-catch块中。暂停后醒来,不能保证它能立即运行!
//休眠1s
Thread.sleep(1000);
join() 方法 加入线程。当一个线程执行中,join()方法可使另一个线程加入,线程需要等另一个线程执行完才可以继续执行原旧的线程!
join([long millis] [, int nanos])
millis: 等待另一个线程的时间(秒
**nanos: **另一个线程的死亡时间(纳秒
//加入ThreadB线程
··· run(){
//线程执行中
while(true){
//加入另一个线程
ThreadB.join();
}
}
interrupted() 方法 中断线程。 能使线程离开run()方法,同时结束线程,但会抛出InterruptedException异常!
//终止线程:ThreadA
ThreadA.interrupted();
yield() 方法 线程礼让,给当前运行状态的线程提个醒,告知它可以将资源礼让给其他线程。但目前情况没有机制能保证当前线程会礼让将资源分配!(操作系统会自动分配CPU时间片执行 (不常用
每个线程都有自己的优先级,线程的优先级代表着该线程的重要性。多个处于就绪的线程就能体现优先级的作用!
Thread类中包含成员变量代表有:
Thread.MAX_PRIORITY = 10 (常量10
Thread.MIN_PRIORITY = 1 (常量1
Thread.NORM_PRIORITY = 5 (默认5
setPriority(int newPriority) 方法 进程优先级调整。调整线程的优先级的情况newPriority范围(0-10)
import javax.swing.*;
import java.awt.*;
public class PriorityBookTest extends JFrame {
private Container c = getContentPane();
private JProgressBar
jp1 = new JProgressBar(),
jp2 = new JProgressBar(),
jp3 = new JProgressBar(),
jp4 = new JProgressBar();
private Thread
threadA = null,
threadB = null,
threadC = null,
threadD = null;
public PriorityBookTest(){
super("线程优先级");
setBounds(300 , 230 ,100,150);
setLayout(new FlowLayout());
setVisible(true);
threadA = new Thread(newThread(c , jp1));
threadB = new Thread(newThread(c , jp2));
threadC = new Thread(newThread(c , jp3));
threadD = new Thread(newThread(c , jp4));
setPriority("A" , 10 , threadA);
setPriority("B" , 7 , threadB);
setPriority("C" , 4 , threadC);
setPriority("D" , 1 , threadD);
}
private static Thread newThread(Container c , JProgressBar jp){
c.add(jp);
jp.setStringPainted(true);
Thread thread = new Thread(new Runnable() {
@Override
public void run() {
int count = 0 ;
while (count <= 100){
jp.setValue(count++);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
});
return thread;
}
public static void setPriority(String threadName , int priority , Thread t){
//设置进程优先级、名,启动
t.setPriority(priority);
t.setName(threadName);
t.start();
}
public static void main(String[] args) {
new PriorityBookTest();
}
}
多线程执行程序,会出现线程抢资源的现象,该现象会导致数据脏读!为了防止多线程的冲突,JAVA提供了线程同步的机制来防止资源抢占的问题!
synchronized关键字采用定时只允许一个线程访问共享资源 (道理跟上排队上厕所一样
每个对象都有标志位,它具有0和1两个值。0代表同步块中在运行其他线程;1代表该线程能被同步块执行,执行中途并将Object对象的标志位设置为0,防止其他线程执行同步块中的代码。(一个线程运行到同步块时首先检查该对象的标志位)
synchronized(Object){
···
}
//Object:任意对象,仅作为标志
public class ThreadSafeTestCodeBlock implements Runnable {
//模拟售票系统 (同步代码块
int count = 10; //票池
@Override
public void run() {
//同步代码块
synchronized(this) {
while (true) {
if (count > 0) {
//try {
// Thread.sleep(1000);
//} catch (InterruptedException e) {
// e.printStackTrace();
//}
//打印前休眠会导致数字存在负数
System.out.println(this.count--);
//打印后休眠可防止数字负数
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
public static void main(String[] args) {
ThreadSafeTest t = new ThreadSafeTest();
//5个线程 (5个售票处
Thread t1 = new Thread(t);
Thread t2 = new Thread(t);
Thread t3 = new Thread(t);
Thread t4 = new Thread(t);
Thread t5 = new Thread(t);
t1.start();
t2.start();
t3.start();
t4.start();
t5.start();
}
}
在自定义方法 前面修饰 synchronized关键字 形成同步方法。
某对象调用同步方法时,该对象的其他同步方法必须等待该同步方法执行完毕后才能被执行。必须将每个能访问共享资源的方法修饰为synchronized,否则会报错!
synchronized void sell(){
···
}
public class ThreadSafeTest implements Runnable {
//模拟售票系统 (同步方法
int count = 10; //票池
private synchronized void sell(){
while (true) {
if (count > 0) {
//try {
// Thread.sleep(1000);
//} catch (InterruptedException e) {
// e.printStackTrace();
//}
//打印前休眠会导致数字存在负数
System.out.println(this.count--);
//打印后休眠可防止数字负数
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
@Override
public void run() {
sell();
}
public static void main(String[] args) {
ThreadSafeTest t = new ThreadSafeTest();
//5个线程 (5个售票处
Thread t1 = new Thread(t);
Thread t2 = new Thread(t);
Thread t3 = new Thread(t);
Thread t4 = new Thread(t);
Thread t5 = new Thread(t);
t1.start();
t2.start();
t3.start();
t4.start();
t5.start();
}
}