1.相关概念
进程:
进程是指运行中的程序,比如我们使用QQ,就启动了一个进程,操作系统就会为该进程分配内存空间。当我们使用迅雷,又启动了一个进程,操作系统将为迅雷配新的内存空间。
进程是程序的一次执行过程,或是正在运行的一个程序。是动态过程:有它自身的产生、存在和消亡的过程
线程:
线程是由进程创建的。是进程的一个实体;一个进程可以有多个线程;
Java 中线程被认为是一个CPU、程序代码、和数据的封装体。java.lang.Thread类使用户可以创建和控制自己的线程。在Java中,虚拟CPU是自动封装进Thread类的实例中,而Code和Data要通过一个对象传给Thread类的构造函数。
单线程:同一个时刻,只允许执行一个线程
多线程:同一个时刻,可以执行多个线程
并发:
同一个时刻,多个任务交替执行 单核cpu实现的多任务就是并发 (造成一种"貌似同时"的错觉)
并行:
同一个时刻,多个任务同时执行。多核cpu可以实现并行
java从语言级别支持多线程 比如object中的wait(),notify()
线程体:
线程的Code和Data构成线程体。线程体决定了线程的行为
通过run()方法来实现的 线程启动后系统就自动调用run()方法,通常,run方法执行了一个时间较长的操作
2.对线程的基本控制:
a. 线程的启动: start()方法
该方法的调用把嵌入在线程中的虚拟CPU置为可运行(Runnable)状态。Runnable状态意味着该线程可以参加调度,被JVM运行,并不意味着线程会立即执行**。**
b.线程的结束:
设定一个标记变量,以结束相应的循环及方法
通知线程退出:
java
package xiancheng;
public class ThreadExit {
public static void main(String[] args) throws InterruptedException {
Cat1 cat = new Cat1();
cat.start();
//如果希望main线程去控制t1 线程的终止,必须可以修改loop
// 让t1 退出run方法,从而终止t1线程->通知方式
//让主线程休眠10秒,再通知t1线程退出
System.out.println("main线程休息10s");
Thread.sleep(10*1000);
cat.setLoop(false);
}
}
class Cat1 extends Thread{
int times = 0;
private boolean loop = true;
@Override
public void run(){
while (loop){
System.out.println("11"+(++times)+Thread.currentThread().getName());
//让该线程休眠一秒
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
if(times ==5){
break;
}
}
}
public void setLoop(boolean loop) {
this.loop = loop;
}
}
暂时阻止线程的执行:
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
c.设定线程的优先级 :
d.线程分类:
java
package xiancheng;
import java.util.*;
public class TestThreadDaemon {
public static void main(String args[]) {
Thread t = new MyThread();
t.setDaemon(true);
t.start();
//主线程结束后 t也结束 将子线程设置成守护线程
System.out.println( "Main--" + new Date());
try{ Thread.sleep(500); }
catch(InterruptedException ex){}
System.out.println("Main End");
}
}
class MyThread extends Thread {
public void run() {
for(int i=0; i<10; i++ ){
System.out.println( i + "--" + new Date());
try{ Thread.sleep(100); }
catch(InterruptedException ex){}
}
}
}//输出:
//Main--Wed Dec 04 19:01:01 CST 2024
//0--Wed Dec 04 19:01:01 CST 2024
//1--Wed Dec 04 19:01:01 CST 2024
//2--Wed Dec 04 19:01:01 CST 2024
//3--Wed Dec 04 19:01:02 CST 2024
//4--Wed Dec 04 19:01:02 CST 2024
//Main End
3.线程常用方法
a.第一组:
Thread类的静态方法currentThread**( )**返回当前线程。
当线程的状态未知时,用isAlive( )确定线程是否活着。返回true意味着线程已经启动,但还没有****运行结束。
**sleep方法:该方法用来使一个线程暂停运行一段固定的时间。在线程睡眠时间内,将运行别的线程。 **sleep( )结束后,线程将进入Runnable状态。
b.第二组
java
package xiancheng;
public class Threadjoin {
public static void main(String[] args) throws InterruptedException {
T3 t3 = new T3();
t3.start();
for (int i = 0; i <=6; i++) {
Thread.sleep(1000);
System.out.println("main------------"+i);
if(i==3){
System.out.println("主线程执行3次了,就让子线程先执行(之前是同时执行的)");
t3.join();
System.out.println("子线程执行完了,主线程继续执行");
}
}
}
}
class T3 extends Thread{
@Override
public void run(){
for (int i = 0; i <6 ; i++) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
System.out.println("子线程------------------------"+i);
}
}
}
//子线程------------------------0
//main------------0
//main------------1
//子线程------------------------1
//子线程------------------------2
//main------------2
//子线程------------------------3
//main------------3
//主线程执行3次了,就让子线程先执行(之前是同时执行的)
//子线程------------------------4
//子线程------------------------5
//子线程执行完了,主线程继续执行
//main------------4
//main------------5
//main------------6
//
//Process finished with exit code 0
4.创建线程
a.继承Thread类
java
package xiancheng;
public class Thread01 {
public static void main(String[] args) throws InterruptedException {
Cat cat = new Cat();
cat.start();//启动线程 会启动Cat的run方法
//main线程启动一个子线程 Thread-0,主线程不会阻塞,会继续执行
//主线程和子线程交替执行
System.out.println("主线程会继续执行"+Thread.currentThread().getName());
for (int i = 0; i <10 ; i++) {
System.out.println("主线程 i ="+i);
Thread.sleep(1000);
}
}
}
//1.当一个类继承了Thread类,该类就可以当成线程使用
//2.重写run方法,写上自己的代码
//3. run() 是Thread类实现了Runnable接口的run方法
class Cat extends Thread{
int times = 0;
@Override
public void run(){
while (true){
System.out.println("11"+(++times)+Thread.currentThread().getName());
//让该线程休眠一秒
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
if(times ==5){
break;
}
}
}
}
b.Runnable接口
一个线程就是Thread类的一个实例**。**
java
package xiancheng;
public class Thread02 {
public static void main(String[] args) {
Dog dog = new Dog();
//dog.start();//不能调用该方法
//创建Thread对象,把Dog对象(实现Runable接口),放入Thread
Thread thread = new Thread(dog);
thread.start();
}
}
class Dog implements Runnable{
int count = 0;
public void run(){
while (true){
System.out.println("hi" + (++count)+Thread.currentThread().getName());
//休眠一秒
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}if(count ==10){
break;
}
}
}
}
c.多线程案列
java
package xiancheng;
public class Thread03 {
public static void main(String[] args) {
T1 t1 = new T1();
T2 t2 = new T2();
Thread th1 = new Thread(t1);
Thread th2 = new Thread(t2);
th1.start();//启动第一个线程
th2.start();//启动第二个线程
}
}
class T1 implements Runnable{
int count = 0;
@Override
public void run() {
//每隔一秒输出he
while (true){
System.out.println("he"+"|"+(++count));
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}if(count ==10){
break;
}
}
}
}
class T2 implements Runnable{
int count = 0;
@Override
public void run() {
//每隔一秒输出he
while (true){
System.out.println("hello"+"|"+(++count));
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}if (count==10) {
break;
}
}
}
}
5.线程的生命周期
6.线程的同步
线程同步机制
在多线程编程,一些敏感数据不允许被多个线程同时访问,此时就使用同步访问技术,保证数据在任何同一时刻,最多有一个线程访问,以保证数据的完整性。
也可以这里理解:线程同步,即当有一个线程在对内存进行操作时,其他线程都不可以对这个内存地址进行操作,直到该线程完成操作,其他线程才能对该内存地址进行操作。
同步的具体方法------Synchronized
a.同步代码块
Synchronized(对象){得到对象的锁才能操作同步代码} 锁加在this对象上默认情况下
b.Synchronized放在方法声明中,相当于Synchronized(this),表示整个方法为同步方法
public Synchronized void m (String name){//需要被同步的代码}在同一时刻,只能有一个线程来执行该方锁加在this对象上
互斥锁
要求多个线程的锁对象为同一个!!
线程的死锁:
多个线程都占用了对方的锁资源,但不肯相让,导致了死锁,在编程是一定要避免死锁的发生.
java
package xiancheng;
class Worker
{
int id;
public Worker(int id){ this.id=id; }
synchronized void doTaskWithCooperator(Worker other){
try{ Thread.sleep(500); } catch(Exception e){}
synchronized(other){
System.out.println("doing" + id);
}
}
}
class DeadLockDemo{
public static void main(String[] args) {
Worker w1 = new Worker(1);
Worker w2 = new Worker(2);
Thread td1 = new Thread(()->{
w1.doTaskWithCooperator(w2);
});
Thread td2 = new Thread(()->{
w2.doTaskWithCooperator(w1);
});
td1.start();
td2.start();
}
}
//在方法内部,首先使用Thread.sleep(500)让当前线程休眠 500 毫秒,
// 然后又嵌套了一个synchronized块,它以传入的另一个Worker对象other作为锁对象。
// 在这个内部的synchronized块中,只是简单地输出了当前Worker对象的id值,表示正在执行相关任务。
//这里存在产生死锁的隐患,因为它获取了自身实例对应的锁(方法级别的synchronized隐式锁)后,
// 又尝试去获取另一个Worker实例对应的锁(通过synchronized(other)),
// 如果多个线程以交叉的方式去获取锁,就可能导致死锁情况发生。
释放锁:
1.当前线程的同步方法、同步代码块执行结束
2.同步方法中遇到break、return
3.当前线程在同步代码块,同步方法中出现了未处理的Error或Exception,导致异常结束
4.同步方法中执行了线程对象的wait()方法,当前线程暂停,并释当前线程在同步代码块、放锁。
线程执行同步代码块或同步方法时,程序调用Thread.sleep()、Thread.yield()方法暂停当前线程的执行,不会释放锁
线程执行同步代码块时,其他线程调用了该线程的suspend()方法将该线程挂起该线程不会释放锁。