目录
1,创建并启动一个线程
1.1 方式一:继承Thread类
public class ThreadDemo {
public static void main(String[] args) {
SaLuluThread saLuluThread = new SaLuluThread();
saLuluThread.start();// 启动线程
}
}
/**
* 继承Thread类,重写run方法
*/
class SaLuluThread extends Thread{
@Override
public void run() {
System.out.println(Thread.currentThread().toString());
}
}
1.2 方式二:实现runnable接口
public class ThreadDemo2 {
public static void main(String[] args) {
SaluluThread saluluThread = new SaluluThread();
// 将线程放入到Thread中
Thread thread = new Thread(saluluThread, "salulu-Thread");
// 启动线程
thread.start();
}
}
/**
* 实现runnable接口,实现run方法
*/
class SaluluThread implements Runnable{
@Override
public void run() {
System.out.println(Thread.currentThread().toString());
}
}
2,线程的方法
Thread类的静态方法:
2.1 currentThread()方法
该方法可返回当前代码正在被哪个线程调用的信息。
2.2 yield()方法
该方法让当前运行线程让出cpu的执行权,回到可运行性状态,使得有相同优先级的线程有机会执行。
2.3 sleep()方法
该方法是让当前正在执行的线程休眠指定的毫秒数。
2.4 interrupted()方法
清除中断标记,并返回上一次中断标记的值
线程对象的方法:
2.5 isAlive()方法
该方法是判断当前线程释放处于活动状态。
2.6 join()方法
调用线程阻塞,等待子线程执行完成后继续执行。
举例说明:主线程创建并启动子线程,但是子线程中有大量的耗时运算,主线程将会早于子线程结束前结束,这时,如果主线程想要等待子线程执行完成之后再结束,就要调用子线程的join()方法,来等待子线程运算完成。
2.7 interrupt()方法
设置中断标记
interrupt 中断操作时,非自身打断需要先检测是否有中断权限,这由jvm的安全机制配置;
如果线程处于sleep, wait, join 等状态,那么线程将立即退出被阻塞状态,并抛出一个InterruptedException异常;
如果线程处于I/O阻塞状态,将会抛出ClosedByInterruptException(IOException的子类)异常;
如果非以上情况,将直接标记 interrupt 状态;
2.8 isInterrupted()方法
获取中断标记的值
3,使用interrupt()方法停止线程
public class InterruptTest {
public static void main(String[] args) {
MyThread myThread = new MyThread();
myThread.start();// 启动线程
try {
Thread.sleep(3);// 等待3毫秒
} catch (InterruptedException e) {
e.printStackTrace();
}
// 通知线程中断
myThread.interrupt();
}
}
class MyThread extends Thread{
@Override
public void run() {
try {
// 在线程中执行业务逻辑
for (int i = 0; i < 10000; i++) {
// 判断当前线程是否为中断状态
if(this.isInterrupted()){// 如果是,则抛出Interrupted异常
System.out.println(this.getName()+" 线程已经是停止状态了");
throw new InterruptedException();
}
System.out.println("i="+i);
}
}catch (InterruptedException e){
// 在Interrupted异常时,做出处理,例如关闭资源
System.out.println(this.getName()+" 线程抛出Interrupted异常了,做出其他处理。。。");
e.printStackTrace();
}
}
}
运行结果:
4,线程的优先级
线程可以设置优先级,优先级高的线程得到CPU资源的机率比较大。
4.1 用户线程(普通线程)
在java 中:
线程优先级的范围是1~10,默认的优先级是5。
线程的优先级具有继承性,比如A线程中启动了B线程,则B线程的优先级与A线程的优先级是一样的。
优先级高的线程不一定每次都先执行完,线程的调度具有随机性。
4.2 守护线程
守护线程是一种特殊的线程,当进程中不存在非守护线程之后,守护线程会自动销毁。
JVM中的垃圾回收线程就是守护线程。
5,线程的状态
6,synchronized关键字
当多个线程对同一个对象进行并发操作时,很有可能会产生数据不一致的情况,或者读到一些脏数据。
这就是非线程安全问题。
Synchronized关键字解决的是多个线程之间访问资源的同步性,它可以保证被它修饰的方法或者代码块在任意时刻只能有一个线程执行,从而保证数据的可见性与原子性。
7,volatie关键字
volatile关键字,使一个变量在多个线程中可见,但不能保证原子性。
8,线程之间的通信
8.1 wait()方法:
该方法的作用就是使当前执行代码的线程进行等待,直到该线程收到通知或被中断为止。
在调用wait()方法之前,线程必须要获得锁,即只能在同步方法或者同步代码块中调用wait()方法。
如果在调用wait()方法是没有持有锁,则会抛出异常。
在执行wait()方法后,当前线程释放锁。
8.2 notify()方法:
该方法会使随机一个在wait等待状态的线程去争取锁,然后继续往下执行。
在执行完notify()方法后,当前线程不会马上释放锁,wait状态的线程也不能马上就获取锁,要等到执行notify()方法的线程将程序执行完,即退出同步代码后,当前线程才会释放锁。
与此相似的还有一个notifyAll()方法:可以唤醒全部等待状态的线程。
8.3 举个例子:
package com.salulu.t3;
public class CommunicationTest {
// 使用一个全局的字符串作为两个线程的锁对象
public static final String lock = "lock";
public static void main(String[] args) throws InterruptedException {
// 需要注意的是,如果B线程被先执行了notify方法,但是此时A线程还没有执行wait方法时
// 会导致A线程和B线程都在等待状态(假死状态)
// 为了避免这种情况,一定要让A线程的wait方法先与B线程的notify方法执行
// 所以这里在A线程启动之后等待100毫秒
new ThreadA().start();// 启动线程A
Thread.sleep(100);
new ThreadB().start();// 启动线程B
}
}
class ThreadA extends Thread{
@Override
public void run() {
synchronized (CommunicationTest.lock){
for (int i = 0; i < 20000; i++) {
try {
System.out.println("ThreadA: "+i);
CommunicationTest.lock.wait();//当前线程等待
CommunicationTest.lock.notify();// 然后唤醒另一个线程
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
class ThreadB extends Thread{
@Override
public void run() {
synchronized (CommunicationTest.lock){
for (int i = 0; i < 20000; i++) {
try {
System.out.println("ThreadB: "+i);
CommunicationTest.lock.notify();// 唤醒另一个等待中的线程
CommunicationTest.lock.wait();// 然后自己等待
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
运行结果:
可以看到线程A和线程B交替输出。
注意:该代码示例只是为了演示wait(),notify()方法的。实际工作中不会使用让线程休眠的方法来保证线程A先执行,也不会使用字符串来作为锁对象。