线程启动方式
- Thread类
- Runable接口
- Callable接口(有返回值)
private static class Test implements Callable<String> {
@Override
public String call() throws Exception {
return "Callable";
}
}
private static class Test2 extends Thread {
@Override
public void run() {
System.out.println("thread");
}
}
private static class Test3 implements Runnable {
@Override
public void run() {
System.out.println("Runnable");
}
}
public static void main(String[] args) throws ExecutionException, InterruptedException {
// new thread
new Test2().start();
// runable
new Thread(new Test3()).start();
// Callable
FutureTask<String> futureTask = new FutureTask<>(new Test());
new Thread(futureTask).start();
String res = futureTask.get();
System.out.println("返回值" + res);
}
如果直接调用run方法,那么它跟调用一个普通方法是一模一样的,并不会新起一个线程
停止线程
- 等待线程执行结束,或者抛出异常
- stop()。不建议使用,已废弃会强制终结线程,无法保证线程的正常释放,这会导致对象处于不一致的状态。例如某个流程有两步操作,但是执行完第一步,就stop,第二部没有执行,就会导致对象被破坏
- suspend()。不建议使用,已废弃。调用suspend()的时候,目标线程会停下来,但却仍然持有在这之前获得的锁定。此时,其他任何线程都不能访问锁定的资源,除非被”挂起”的线程恢复运行。对任何线程来说,如果它们想恢复目标线程,同时又试图使用任何一个锁定的资源,就会造成死锁。
- interrupt()、isInterrupted()。 interrupt()发出中断线程信号,它仅仅是发出中断的信号,而是否中断线程需要线程自己操作;isInterrupted()判断线程是否处于中断状态
- 使用成员变量做状态标志。不太推荐,因为这种方法必须在阻塞线程运行完之后才会判断状态,可以配合interrupt使用。
static class myThread extends Thread {
@Override
public void run() {
String threadName = Thread.currentThread().getName();
// 必须在线程里去处理线程中断,否则线程不会中断
while (!isInterrupted()) {
System.out.println(threadName + "is running : " + isInterrupted());
}
System.out.println(threadName + "is stop:" + isInterrupted());
}
}
static class myRun implements Runnable {
@Override
public void run() {
String threadName = Thread.currentThread().getName();
// 必须在线程里去处理线程中断,否则线程不会中断
while (!Thread.currentThread().isInterrupted()) {
System.out.println(threadName + "is running : " + Thread.currentThread().isInterrupted());
}
System.out.println(threadName + "is stop:" + Thread.currentThread().isInterrupted());
}
}
public static void main(String[] args) throws InterruptedException {
myThread myThread = new myThread();
myThread.setName("endThread");
myThread.start();
Thread.sleep(2000);
myThread.interrupt();
// run方式
Thread myRun = new Thread(new myRun());
myRun.start();
Thread.sleep(2000);
myRun.interrupt();
}
interrupt()注意点
static class MyThread extends Thread {
@Override
public void run() {
String threadName = Thread.currentThread().getName();
// 必须在线程里去处理线程中断,否则线程不会中断
while (!isInterrupted()) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
// 方法抛出InterruptedException后,中断状态会复位,线程不会停止
// 所以需要再次调用interrupt()
interrupt();
System.out.println(threadName + "in Exception : " + isInterrupted());
}
System.out.println(threadName + "is running : " + isInterrupted());
}
System.out.println(threadName + "is stop:" + isInterrupted());
}
}
优先级
优先级为1-10,缺省为5。数字越高,优先级越高,一般较少使用,因为执行顺序并不可靠。
myThread.setPriority(6);
守护线程
守护线程是指为其他线程服务的线程。主线程退出,守护线程一定会退出。重要的特性是后台运行。
myThread.setDaemon(true);
线程间的共享
-
synchronized锁
-
volatile
它适合一写多读,因为它能保证变量的可见性,https://blog.csdn.net/zgsxhdzxl/article/details/88095284 可以先看一下jvm的内存模型。
1、保证了不同线程对这个变量进行操作时的可见性,即一个线程修改了某个变量的值,这新值对其他线程来说是立即可见的。volatile修饰的变量,它会认为其缓存是无效的,所以每次修改都会立即写入主存,读数据也会每次都从主存中获取最新值。但是它非原子性,不保证线程安全。
public class VolatileTest {
static class Test extends Thread {
private volatile int a = 0;
@Override
public void run() {
String name = Thread.currentThread().getName();
a++;
System.out.println("线程" + name + "的值为" + a);
a++;
System.out.println("线程" + name + "的值为" + a);
}
}
public static void main(String[] args) {
// 可以看到a的值并没有递增, 没有保证安全
Test test = new Test();
Test test2 = new Test();
Test test3 = new Test();
Test test4 = new Test();
test.start();
test2.start();
test3.start();
test4.start();
}
}
2、禁止进行指令重排序..
- threadLocal
https://blog.csdn.net/zgsxhdzxl/article/details/104710702
线程间的协作
- wait()和notify()、notifyAll()
wait()的作用是让当前线程进入等待状态,尽量使用notifyAll(),notify()只会随机唤醒一个线程。
- Join()
Join交出当前线程的执行权,等待其他线程执行完之后再执行。
public static void main(String[] args) throws InterruptedException {
Thread a = new Thread(){
@Override
public void run() {
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
};
a.start();
a.join();
System.out.println("所有子线程执行完成");
}
休眠sleep
Thread.sleep(1000)
TimeUnit.SECONDS.sleep(10)
sleep是Thread类的一个方法,wait是Object类的一个方法。
sleep不会释放资源,wait会释放掉资源yield也不会释放锁,但是他有可能又继续执行。而spleep是睡眠时间结束后,才有可能继续执行