Java多线程常用操作方法
线程的命名和取得
Thread
类中的线程名称操作方法
- 构造方法:
public Thread(Runnable targe, String name);
- 设置名字:
public final void setName(String name);
- 取得名字:
public final String getName()
对于线程对象的获取不可能只是依靠一个this来完成的, 因为线程的状态不可控, 但是一定要执行run()
方法, 所以可以获取当前线程
- 获取当前线程:
public static Thread currentThread();
eg.
class MyThreadc implements Runnable {
@Override
public void run() {
System.out.println(Thread.currentThread().getName());
}
}
public class RunnableDemo {
public static void main(String[] args) {
MyThreadc mt = new MyThreadc();
new Thread(mt, "线程A").start();
new Thread(mt).start();
new Thread(mt, "线程B").start();
}
}
output
线程A
线程B
Thread-0
当开发者为线程命名的时候就使用设置的名字, 如果没有设置名字, 则会自动生成一个名字, 主要依靠static
属性完成的, 在Thread
类里定义有如下操作:
private static int threadInitNumber;
private static synchronized int nextThreadNum() {
return threadInitNumber++;
}
主线程
class MyThreadc implements Runnable {
@Override
public void run() {
System.out.println(Thread.currentThread().getName());
}
}
public class RunnableDemo {
public static void main(String[] args) {
MyThreadc mt = new MyThreadc();
new Thread(mt, "线程A").start();
mt.run();
}
}
输出
线程A
main
说明在主函数中执行的是主线程, 在主线程可以创建若干个子线程, 将复杂的逻辑和耗时操作交给子线程.
进程
在执行java命令的时候就表示启动了一个JVM进程, 一台电脑上可以同时启动若干个JVM进程, 每一个JVM的进程都会有各自的线程.
线程的休眠
public static void sleep(long millis) throws InterruptedException
public static void sleep(long millis, int nanos) throws InterruptedException
new Thread(()->{
for (int i = 0; i < 6; i++) {
System.out.println(Thread.currentThread().getName() + ", i = " + i);
try {
Thread.sleep(1000);
} catch (Exception e) {
e.printStackTrace();
}
}
}, "线程对象").start();
多个线程执行休眠操作, 休眠顺序有先有后.
线程中断
- 判断线程是否被中断
public boolean isInterrupted();
- 中断线程执行
public void interrupt();
所有正在执行的线程都是可以被终端的, 中断线程的操作必须被异常处理
public static void main(String[] args) throws Exception {
Thread thread = new Thread(new Runnable() {
@Override
public void run() {
System.out.println("线程开启");
try {
Thread.sleep(10000);
System.out.println("线程继续");
} catch (Exception e) {
System.out.println("线程卡顿");
}
System.out.println("线程继续");
}
});
thread.start();
Thread.sleep(1000);
if (!thread.isInterrupted()) {
System.out.println("线程中断");
thread.interrupt();
}
}
线程强制运行
线程强制运行是指当满足于一些条件以后, 某个线程可以独占资源, 一直到该线程的程序执行结束。
public final void jion() throws InterruptedException
举例
public static void main(String[] args) throws Exception {
Thread thread = new Thread(new Runnable() {
Thread mainThread = Thread.currentThread();
@Override
public void run() {
// 不加try..catch...那么主线程和子线程交替执行
try {
mainThread.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
try {
Thread.sleep(100);
} catch (Exception e) {
}
for (int i = 0; i < 10; i++) {
System.out.println("***一个线程正在执行***" + Thread.currentThread().getName() + ":" + i);
}
}
}, "打酱油");
thread.start();
for (int i = 0; i < 10; i++) {
System.out.println("***一个霸道的线程正在执行***" + Thread.currentThread().getName() + ":" + i);
}
}
线程的礼让
线程的礼让是指先将资源让出去给别的线程执行。线程的礼让可以使用Thread
的中的
public static void yield()
举例
public static void main(String[] args) throws Exception {
Thread thread = new Thread(new Runnable() {
@Override
public void run() {
for (int i = 0; i < 100; i++) {
if (i % 3 == 0) {
Thread.yield();
System.out.println("这个线程正在礼让");
}
try {
Thread.sleep(100);
} catch (Exception e) {
}
System.out.println("***一个线程正在执行***" + Thread.currentThread().getName() + ":" + i);
}
}
}, "打酱油");
thread.start();
for (int i = 0; i < 100; i++) {
try {
Thread.sleep(100);
} catch (Exception e) {
}
System.out.println("***一个霸道的线程正在执行***" + Thread.currentThread().getName() + ":" + i);
}
}
礼让执行的时候每次调用yield()
方法,每次礼让一次当前的资源。
线程的优先级
从理论上来讲线程的优先级越高越有可能先执行(越有可能先抢占到资源)。在Thread
类里面针对于优先级,有两个处理方法
获取优先级:
public final int getPriority()
设置优先级:
public final void setPriority(int newPriority)
在进行优先级定义的时候都是通过int
型的数字来完成的,而对于此数字的选择在Thread
类中就有明确的定义:
最高优先级
public static final int MAX_PRIORITY // 10
中等优先级
public static final int NORM_PRIORITY // 5
最低优先级
public static final int MIN_PRIORITY // 1
举例
public static void main(String[] args) throws Exception {
Runnable run = new Runnable() {
@Override
public void run() {
for (int i = 0; i < 10; i++) {
try {
Thread.sleep(100);
} catch (Exception e) {
}
System.out.println(Thread.currentThread().getName() + "执行:" + i);
}
}
};
Thread threadA = new Thread(run, "线程A");
Thread threadB = new Thread(run, "线程B");
Thread threadC = new Thread(run, "线程C");
threadA.setPriority(Thread.MAX_PRIORITY);
threadB.setPriority(Thread.MIN_PRIORITY);
threadC.setPriority(Thread.MIN_PRIORITY);
threadA.start();
threadB.start();
threadC.start();
}
主线程属于中等优先级, 默认的线程的优先级属于中等优先级. 优先级高的有可能先执行, 而不是绝对先执行
线程同步
所谓的线程同步就是指多个操作在同一时间段内只能有一个线程进行, 其他进程等待此线程完成以后才可以继续执行.
线程同步
-
同步代码块: 实现线程同步使用
synchronized
关键字来实现, 在同步代码块里的代码只允许一个线程执行.
synchronized(同步对象) {
同步代码操作
}
public static void main(String[] args) throws Exception {
Runnable run = new Runnable() {
private int tiket = 5;
@Override
public void run() {
while (true) {
synchronized (this) {
if (this.tiket > 0) {
try {
Thread.sleep(100);
} catch (Exception e) {
}
System.out.println(Thread.currentThread().getName() + "卖票, tiket=" + this.tiket--);
} else {
System.out.println("卖光啦~");
break;
}
}
}
}
};
Thread threadA = new Thread(run, "A");
Thread threadB = new Thread(run, "B");
Thread threadC = new Thread(run, "C");
threadA.start();
threadB.start();
threadC.start();
}
-
同步方法: 使用
synchronized
修饰方法
public static void main(String[] args) throws Exception {
Runnable run = new Runnable() {
private int tiket = 5;
public synchronized void sale() {
if (this.tiket > 0) {
try {
Thread.sleep(1000);
} catch (Exception e) {
}
System.out.println(Thread.currentThread().getName() + "卖票, tiket=" + this.tiket--);
} else {
System.out.println("卖光啦~");
}
}
@Override
public void run() {
while (true) {
this.sale();
}
}
};
Thread threadA = new Thread(run, "A");
Thread threadB = new Thread(run, "B");
Thread threadC = new Thread(run, "C");
threadA.start();
threadB.start();
threadC.start();
}
在同步处理以后会造成性能的降低.
线程死锁
- 死锁是在多线程同步的处理之中可能产生的一种问题
- 数据同步越多, 死锁造成的就相对会严重一些
优雅的停止线程
多线程的启动是使用的Thread中的start()方法, 而如果对于多线程需要进行停止处理, Thread类提供了stop()方法, 但是从JDK1.2开始就已经被移除
- 停止多线程:
public void stop()
- 销毁多线程:
public void destroy()
- 挂起线程:
public final void suspend()
- 恢复挂起线程执行:
public final void resume()
废除这些方法的原因: 有可能导致线程的死锁
柔和的停止线程
public class Main {
public static boolean flag = true;
public static void main(String[] args) throws Exception {
Runnable run = new Runnable() {
@Override
public void run() {
long num = 0;
while (flag) {
try {
Thread.sleep(50);
} catch (Exception e) {}
System.out.println(Thread.currentThread().getName() + "正在运行" + num++);
}
}
};
Thread threadA = new Thread(run, "A");
threadA.start();
Thread.sleep(200);
flag = false;
}
}
flag 设置为false以后并不是立刻就停, 而是在修改完flag以后, 在进行下次flag判断的时候才会停止
后台守护线程
在多线程里面可以进行守护线程的定义, 也就是说如果现在主线程的程序或者其他线程还在运行的时候, 那么守护线程一直存在, 并且一直在后台运行
- 设置为守护线程:
public final void seDaemon(boolean on);
- 判断是否是守护线程:
public final boolean isDaemon();
守护线程示例
public class Main {
public static void main(String[] args) throws Exception {
Runnable run = new Runnable() {
@Override
public void run() {
for (int i = 0; i < 6; i++) {
try {
Thread.sleep(100);
} catch (Exception e) {}
System.out.println(Thread.currentThread().getName() + "正在运行" + i);
}
}
};
Runnable run2 = new Runnable() {
@Override
public void run() {
for (int i = 0; i < Integer.MAX_VALUE; i++) {
try {
Thread.sleep(100);
} catch (Exception e) {}
System.out.println(Thread.currentThread().getName() + "正在运行" + i);
}
}
};
Thread threadA = new Thread(run, "A");
Thread threadB = new Thread(run2, "守护线程");
threadB.setDaemon(true);
threadA.start();
threadB.start();
}
}
如果程序执行完毕, 那么守护线程就会消失, 在JVM里最大的守护线程就是GC.
volatile
在正常进行变量处理的时候, 往往会经历如下几个步骤
- 获取变量原有的数据内容副本
- 利用副本为变量进行数学计算
-
将计算以后的变量, 保存到原始空间中
而如果一个属性上追加了一个volatile关键字, 表示的就是不使用副本, 而是直接操作原有的内存空间, 相当于节省了拷贝副本, 重新保存的步骤.
volatile
volatile与synchronized的区别
- volatile主要在属性上使用, 而synchronized是在代码块和方法上使用.
- volatile无法描述同步的处理, 只是一种直接内存的处理, 避免了副本的操作, 而synchronized是实现同步的.