1. 하위 클래스를 생성하고 Thread에서 상속하고 실행 메서드를 재정의합니다.
class MyThread extends Thread { @Override public void run() { System.out.println("hello thread"); } } public class Demo1 { public static void main(String[] args) { // 最基本的创建线程的办法. Thread t = new MyThread(); //调用了start方法才是真正的在系统中创建了线程,执行run方法 t.start(); } }
2. 클래스를 생성하고 Runnable을 생성합니다. 인스턴스를 Thread
class MyRunnable implements Runnable{ @Override public void run() { System.out.println("hello"); } } public class Demo3 { public static void main(String[] args) { Thread t = new Thread(new MyRunnable()); t.start(); } }
3에 전달합니다. 익명 내부 클래스
는 익명 내부 클래스를 생성하고 Thread 클래스에서 상속하고 run 메서드를 재정의한 다음 익명 내부 클래스
public class Demo4 { public static void main(String[] args) { Thread t = new Thread(){ @Override public void run() { System.out.println("hello"); } }; t.start(); } }
new의 인스턴스를 생성합니다. Runnable은 이 익명 내부 클래스를 위해 생성되었으며 새로운 Runnable 인스턴스는 Thread 생성자
public class Demo5 { public static void main(String[] args) { Thread t = new Thread(new Runnable() { @Override public void run() { System.out.println("hello"); } }); t.start(); } }
4.lambda 표현식 람다가 Runnable
public class Demo6 { public static void main(String[] args) { Thread t = new Thread(() ->{ System.out.println("hello"); }); t.start(); } }
1.isDaemon();
을 대체합니다. 백그라운드 스레드입니까? 백그라운드 스레드는 프로세스 종료에 영향을 주지 않습니다. 백그라운드 스레드는 프로세스 종료에 영향을 미칩니다.2.isAlive();
시작을 호출하기 전에 시스템에 해당 스레드가 없습니다. run 메소드가 실행된 후에도 t 객체가 여전히 존재할 수 있습니다3.isinterrupted();
중단되었는지 여부
run은 단순히 내용을 설명하는 일반적인 메소드입니다. 스레드는 시스템 내부에서 생성됩니다
스레드를 중지하는 핵심은 해당 실행 메서드가 실행을 마치도록 하는 것입니다. 메인 메소드 실행이 끝날 때까지 종료되지 않습니다
1. 수동 설정 플래그 비트
스레드에서 이 플래그 비트를 제어하면 이 스레드의 끝에 영향을 미칠 수 있지만 여기의 여러 스레드는 가상 공간을 공유하므로 isQuit이 수정되었습니다. 메인 스레드와 t 스레드가 판단한 isQuit은 동일한 값입니다
public class Demo10 { // 通过这个变量来控制线程是否结束. private static boolean isQuit = false; public static void main(String[] args) { Thread t = new Thread(() -> { while (!isQuit) { System.out.println("hello thread"); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } } }); t.start(); // 就可以在 main 线程中通过修改 isQuit 的值, 来影响到线程是否退出 try { Thread.sleep(5000); } catch (InterruptedException e) { e.printStackTrace(); } // main 线程在 5s 之后, 修改 isQuit 的状态. isQuit = true; } }
2 Thread에 내장된 플래그 비트를 사용하여 Thread.interruted()를 결정합니다. 이것은 정적 메서드 Thread.currentThread( ).isInterrupted() 이것은 currentThread가 현재 스레드의 인스턴스를 얻을 수 있는 인스턴스 메소드입니다.
public class Demo7 { public static void main(String[] args) { Thread t = new Thread(() -> { while(!Thread.currentThread().isInterrupted()){ System.out.println("hello"); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); // 当触发异常之后, 立即就退出循环~ System.out.println("这是收尾工作"); break; } } }); t.start(); try{ Thread.sleep(5000); }catch (InterruptedException e){ e.printStackTrace(); } // 在主线程中, 调用 interrupt 方法, 来中断这个线程. // t.interrupt 的意思就是让 t 线程被中断!! t.interrupt(); } }
이 메소드 t.interrupt()를 호출하면 두 가지 상황이 발생할 수 있습니다.
1) t 스레드가 준비되면 스레드의 플래그 비트를 true로 설정합니다
2) t 스레드가 Blocking 상태(sleep)이면 InterruptExeception이 트리거됩니다
여러 스레드 간의 예약 순서가 불확실할 때가 있습니다. 스레드 간의 순서를 제어해야 합니다. 스레드 대기는 스레드 실행을 제어하는 방법입니다. 여기서 대기하는 스레드는 스레드가 끝나는 순서만 제어하면 됩니다.
어떤 스레드가 조인하는지, 어떤 스레드가 해당 스레드가 실행을 완료할 때까지 차단하고 기다릴지.
t.join();
이 메서드를 호출하는 스레드가 메인 스레드입니다. 개체 t를 호출하면 메인이 t를 기다리게 됩니다.
조인 라인에 도달하면 코드가 중지되고 t가 먼저 끝난 다음 main이 계속됩니다.
t.join(10000);
join은 하나의 매개변수가 있는 다른 버전을 제공합니다. 매개변수는 조인이 직접 반환된 후 10초의 대기 시간이므로 더 이상 기다릴 필요가 없습니다
Thread. currentThread()
현재 스레드를 가져올 수 있는 응용 프로그램. 어떤 스레드가 currentThread를 호출하면 다음과 같이 비교해 보세요.
이 코드의 경우 스레드는 Thread 메서드를 상속하여 생성됩니다. 이때 run 메소드에서 이를 통해 직접 얻는 것은 현재 Thread 인스턴스
public class Demo4 { public static void main(String[] args) { Thread t = new Thread(){ @Override public void run() { System.out.println(Thread.currentThread().getName()); System.out.println(this.getName()); } }; t.start(); } }
그러나 여기서는 Thread 유형을 가리키는 것이 아니라 Runnable은 name 속성이 없는 단순한 작업일 뿐입니다. . 스레드를 얻으려면 이름은 Thread.currentThread()
public class Demo5 { public static void main(String[] args) { Thread t = new Thread(new Runnable() { @Override public void run() { //err //System.out.println(this.getName()); //right System.out.println(Thread.currentThread().getName()); } }); t.start(); } }
를 통해서만 전달할 수 있습니다. 시스템 수준의 경우:
Ready
Blocking
Java의 Thread 클래스가 더욱 개선되었습니다.
NEW: The Thread 객체가 생성되었지만 start가 아직 호출되지 않았습니다.
TERMINATED:운영 체제의 스레드가 실행되어 삭제되었습니다. , 그러나 Thread 개체는 여전히 획득 상태입니다
RUNNABLE: Ready 상태, 이 상태의 스레드는 준비 대기열에 있으며 언제든지 CPU에 예약될 수 있습니다
TIME_WAITING: 슬립이 호출되면 이 상태로 들어가게 됩니다. 조인(타임아웃 시간) BLOCKED: 현재 스레드가 잠금 대기 중입니다. 대기 중으로 인해 차단이 발생합니다
WAITING:현재 스레드가 깨어나기를 기다리고 있습니다
상태 전환 다이어그램:
定义:操作系统中线程调度是随机的,导致程序的执行可能会出现一些bug。如果因为调度随机性引入了bug线程就是不安全的,反之则是安全的。
解决方法:加锁,给方法直接加上synchronized关键字,此时进入方法就会自动加锁,离开方法就会自动解锁。当一个线程加锁成功的时候,其他线程尝试加锁就会触发阻塞等待,阻塞会一直持续到占用锁的线程把锁释放为止。
synchronized public void increase() { count++; }
线程不安全产生的原因:
1.线程是抢占式执行,线程间的调度充满随机性。
2.多个线程对同一个变量进行修改操作
3.针对变量的操作不是原子的
4.内存可见性也会影响线程安全(针对同一个变量t1线程循环进行多次读操作,t2线程少次修改操作,t1就不会从内存读数据了而是从寄存器里读)
5.指令重排序,也是编译器优化的一种操作,保证逻辑不变的情况下调整顺序,解决方法synchronized。
内存可见性解决方法:
1.使用synchronized关键字 使用synchronized不光能保证指令的原子性,同时也能保证内存的可见性。被synchronized包裹起来的代码编译器就不会从寄存器里读。
2.使用volatile关键字 能够保证内存可见性,禁止编译器作出上述优化,编译器每次执行判定相等都会重新从内存读取。
在java中每个类都是继承自Object,每个new出来的实例里面一方面包含自己安排的属性,另一方面包含了“对象头”即对象的一些元数据。加锁操作就是在这个对象头里面设置一个标志位。
使用synchronized的时候本质上是对某个“对象”进行加锁,此时的锁对象就是this。加锁操作就是在设置this的对象头的标志位,当两个线程同时尝试对同一个对象加锁的时候才有竞争,如果是两个线程在针对两个不同对象加锁就没有竞争。
class Counter{ public int count; synchronized public void increase(){ count++; } }
需要显示制定针对那个对象加锁(java中的任意对象都可以作为锁对象)
public void increase(){ synchronized(this){ count++; } }
相当于针对当前类的类对象加锁,类对象就是运行程序的时候。class文件被加载到JVM内存中的模样。
synchronized public static void func(){ }
或者
public static void func(){ synchronized(Counter.class){ } }
可重入锁就是同一个线程针对同一个锁,连续加锁两次,如果出现死锁就是不可重入锁,如果不会死锁就是可重入的。因此就把synchronized实现为可重入锁,下面的例子里啊连续加锁操作不会导致死锁。可重入锁内部会记录所被哪个线程占用也会记录加锁次数,因此后续再加锁就不是真的加锁而是单纯地把技术给自增。
synchronized public void increase(){ synchronized(this){ count++; } }
1.一个线程一把锁
2.两个线程两把锁
3.N个线程M把锁(哲学家就餐问题,解决方法:先拿编号小的筷子)
死锁的四个必要条件(前三个都是锁本身的特点)
1.互斥使用,一个锁被另一个线程占用后其他线程就用不了(锁的本质,保证原子性)
2.不可抢占,一个锁被一个线程占用后其他线程不可把这个锁给挖走
3.请求和保持,当一个线程占据了多把锁之后,除非显示的释放否则这些锁中都是该线程持有的
4.环路等待,等待关系成环(解决:遵循固定的顺序加锁就不会出现环路等待)
java线程类:
不安全的:ArrayList,LinkedList,HashMap,TreeMap,HashSet,TreeSet,StringBuilder
安全的:Vector,HashTable,ConcurrentHashMap,StringBuffer,String
禁止编译器优化保证内存可见性,产生原因:计算机想执行一些计算就需要把内存的数据读到CPU寄存器中,然后再从寄存器中计算写回到内存中,因为CPU访问寄存器的速度比访问内存快很多,当CPU连续多次访问内存结果都一样,CPU就会选择访问寄存器。
JMM(Java Memory Model)Java内存模型
就是把硬件结构在java中用专业的术语又重新抽象封装了一遍。
工作内存(work memory)其实指的不是内存,而是CPU寄存器。
主内存(main memeory)这才是主内存。
原因:java作为一个跨平台编程语言要把硬件细节封装起来,假设某个计算机没有CPU或者内存同样可以套到上述模型中。
寄存器,缓存和内存之间的关系
CPU从内存取数据太慢,因此把数据直接放到寄存器里来读,但寄存器空间太紧张于是又搞了一个存储空间,比寄存器大比内存小速度比寄存器慢比内存快称为缓存。寄存器和缓存统称为工作内存。
寄存器,缓存和内存之间的关系图
存储空间:CPU
速度:CPU>L1>L2>L3>内存
成本:CPU>L1>L2>L3>内存
volatile和synchronized的区别
volatile只是保证可见性不保证原子性,只是处理一个线程读和一个线程写的过程。
synchronized都能处理
wait和notify
等待和通知处理线程调度随机性问题的,join也是一种控制顺序的方式更倾向于控制线程结束。wait和notify都是Object对象的方法,调用wait方法的线程就会陷入阻塞,阻塞到有线程通过notify来通知。
public class Demo9 { public static void main(String[] args) throws InterruptedException { Object object = new Object(); System.out.println("wait前"); object.wait(); System.out.println("wait后"); } }
wait内部会做三件事;
1.先释放锁
2.等待其他线程的通知
3.收到通知后重新获得锁并继续往下执行
因此想用wait/notify就得搭配synchronized
public class Demo9 { public static void main(String[] args) throws InterruptedException { Object object = new Object(); synchronized (object){ System.out.println("wait前"); object.wait(); System.out.println("wait后"); } } }
注意:wait notify都是针对同一对象来操作的,例如现在有一个对象o,有10个线程都调用了o.wait,此时10个线程都是阻塞状态。如果调用了o.notify就会把10个线程中的一个线程唤醒。而notifyAll就会把所有10个线程全都给唤醒,此时就会竞争锁。
위 내용은 Java 멀티스레딩: Thread 클래스 사용.의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!