目录
(1)单线程:单线程就是进程中只有一个线程。单线程在程序执行时,所走的程序路径按照连续顺序排下来,前面的必须处理好,后面的才会执行。
(2)多线程:由一个以上的线程组成的程序称为多线程程序。Java中,一定是从主线程开始执行(main方法),然后在主线程的某个位置创建并启动新的线程。
(3)实现Callable接口(有返回值),允许子线程返回结果,抛出异常
(5)yield()方法:让出线程:当前线程愿意让出CPU给其他线程使用。
(7)守护线程(Daemon Thread):当其他的非守护线程执行完毕后,守护线程会陆陆续续结束
一、线程基础:
1、程序:
是指含有指令和数据的文件,被存储在磁盘或其他的数据存储设备中,可以理解为程序是包含静态代码的文件。例如:浏览器软件、音乐播放器软件等软件的安装目录和文件。
2、进程:
进程是程序的一次执行过程,是系统运行程序的基本单位。简单来说,一个进程就是一个执行中的程序,它在计算机中一个指令接着一个指令地执行着, 同时,每个进程还占有某些系统资源如 CPU时间,内存空间,文件,输入输 出设备的使用权等。
3、线程:
某些进程内部还需要同时执行多个子任务。例如,我们在使用WPS时,WPS可以让我们一边打字,一边进行拼写检查,同时还可以在后台进行自动保存和上传云文档,我们把子任务称为线程。线程是进程划分成的更小的运行单位。
4、进程和线程的关系:
一个进程可以包含一个或多个线程,但至少会有一个主线程。
5、JVM进程:
JVM进程启动时,自动创建Heap(堆区)和 Metaspace(元空间,JDK1.8以前叫Method Area方法区)。
多个线程共享JVM进程的Heap(堆区)和Metaspace(元空间)资源,但每个线程有自己的程序计数器、虚拟机栈和本地方法栈。系统在产生一个线程,或是在各个线程之间作切换工作时,负担要比进程小得多,因此,线程也被称为轻量级进程。

二、线程
1、现成的基本概念:
(1)单线程:单线程就是进程中只有一个线程。单线程在程序执行时,所走的程序路径按照连续顺序排下来,前面的必须处理好,后面的才会执行。
public class SingleThread {
public static void main(String[] args) {
for (int i = 0; i < 10000; i++) {
System.out.print(i + " ");
}
}
}
(2)多线程:由一个以上的线程组成的程序称为多线程程序。Java中,一定是从主线程开始执行(main方法),然后在主线程的某个位置创建并启动新的线程。
public class MultiThread {
public static void main(String[] args) {
// 创建2个线程
Thread t1 = new Thread(new Runnable() {
@Override
public void run() {
for (int i = 0; i < 10000; i++) {
System.out.println("线程1:" + i + " ");
}
}
});
Thread t2 = new Thread(new Runnable() {
@Override
public void run() {
for (int i = 0; i < 10000; i++) {
System.out.println("线程2:" + i + " ");
}
}
});
// 启动2个线程
t1.start();
t2.start();
}
}
2、多线程的创建:
(1)继承Thread类(线程子类):
需要创建一个Thread的子类,重写run方法,再去创建子类对象,调用start()方法启动。
public class Demo01 {
public static void main(String[] args) {
MyThread t1=new MyThread();
t1.start();
for (int i = 0; i <10 ; i++) {
System.out.println("main线程执行任务"+i);
}
}
}
class MyThread extends Thread{
@Override
public void run() {
for (int i = 0; i <10 ; i++) {
System.out.println(getName()+"子线程执行任务"+i);
}
}
}
(2)实现Runnable接口(线程执行类):
需要创建Runnable的实现类,重写run方法 ,创建Runnable的实现类对象r1 ,创建Thread类的对象,将r1作为构造方法的参数进行传递 ,调用start方法启动线程。
public class Demo02 {
public static void main(String[] args) {
MyRun m1=new MyRun();
Thread r1=new Thread(m1);
r1.start();
for (int i = 0; i <10 ; i++) {
System.out.println("main执行任务"+i);
}
}
}
class MyRun implements Runnable{
@Override
public void run() {
for (int i = 0; i <10 ; i++) {
System.out.println("子线程执行任务"+i);
}
}
}
(3)实现Callable接口(有返回值),允许子线程返回结果,抛出异常
创建Callable的实现类,重写call()方法(有返回值) ,创建Callable的实现类对象 ,创建FutureTask对象,用来进行结果的管理操作 ,创建Thread类的队象 ,启动线程。
public class Demo03 {
public static void main(String[] args) throws ExecutionException, InterruptedException {
MyCall m1=new MyCall(1,10);
FutureTask<Integer> ft=new FutureTask<>(m1);
Thread t1 = new Thread(ft);
t1.start();
System.out.println("子线程1和为:"+ft.get());
MyCall m2=new MyCall(1,100);
FutureTask<Integer> ft2=new FutureTask<>(m2);
Thread t2 = new Thread(ft2);
t2.start();
System.out.println("子线程2和为:"+ft2.get());
}
}
class MyCall implements Callable<Integer> {
int begin,end;
public MyCall(int begin, int end) {
this.begin = begin;
this.end = end;
}
@Override
public Integer call() throws Exception {
int number=0;
for (int i = begin; i <=end ; i++) {
number+=i;
}
return number;
}
}
(4)创建线程池:
public class Demo04 {
public static void main(String[] args) {
//线程池: 使用线程池创建对象、
ExecutorService es= Executors.newCachedThreadPool(); //创建无上限个
es.execute(new MyRun());
es.execute(new MyRun());
}
}
class MyRun implements Runnable{
@Override
public void run() {
for (int i = 0; i <10 ; i++) {
System.out.println("子线程执行任务"+i);
}
}
}
3、线程常用的方法:
(1)setName()方法: 给线程设置名字
package com.yuan.threadMethed;
public class Demo01 {
public static void main(String[] args) {
//给线程设置名字
//setName()方式设置线程ming
//调用有参构造设置线程名
MyThread t1 = new MyThread();
t1.setName("土豆");
t1.start();
MyThread t2=new MyThread("洋芋");
t2.start();
//runnale作为参数
Runnable r1 = new Runnable(){
@Override
public void run() {
System.out.println("当前线程"+Thread.currentThread().getName()+"线程正在启动");
try {
Thread.sleep(3);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("当前线程"+Thread.currentThread().getName()+"线程正在运行");
}
};
Thread t3=new Thread(r1,"西红柿");
//t3.setName("西红柿");
t3.start();
}
}
(2)setPriority()方法:设置线程优先级
package com.yuan.threadMethed;
public class Demo02 {
public static void main(String[] args) {
//创建线程1
Thread t1 = new Thread(new Runnable() {
@Override
public void run() {
for (int i = 'a'; i <'z' ; i++) {
System.out.println(Thread.currentThread().getName()+":"+i);
}
}
},"线程一");
t1.setPriority(1);
Thread t2 = new Thread(new Runnable() {
@Override
public void run() {
for (int i = 1; i <26 ; i++) {
System.out.println(Thread.currentThread().getName()+":"+i);
}
}
},"线程二");
t2.setPriority(10);
t1.start();
t2.start();
//11获取线程统先级
//System.out.println(t1.getName()+"优先级为"+t1.getPriority());
System.out.println(Thread.currentThread().getName()+"优先级为"+ Thread.currentThread().getPriority());
}
}
(3)Thread.sleep()方法:线程的休眠:
在线程中,可以通过调用Thread.sleep(long millis),强迫当前线程按照指定毫秒值休眠。
public class Main {
public static void main(String[] args) {
System.out.println("main start...");
Thread t = new Thread() {
public void run() {
System.out.println("thread run...");
try {
Thread.sleep(10); // 子线程休眠10毫秒
} catch (InterruptedException e) {}
System.out.println("thread end.");
}
};
t.start();
try {
Thread.sleep(20); // 主线程休眠20毫秒
} catch (InterruptedException e) {}
System.out.println("main end...");
}
}
(4)interrupt()方法:中断线程
package com.yuan.threadMethed;
public class Demo05 {
public static void main(String[] args) throws InterruptedException {
System.out.println("main线程进入");
Thread t1 = new Thread(new Runnable() {
@Override
public void run() {
long starTime=System.currentTimeMillis();
System.out.println("进入t1线程");
try {
Thread.sleep(1000*10);
} catch (InterruptedException e) {
System.out.println("中断t1线程,消耗时间为:"+(System.currentTimeMillis()-starTime));
e.printStackTrace();
return;
}
System.out.println("结束t1线程,消耗时间为:"+(System.currentTimeMillis()-starTime));
}
},"线程1");
t1.start();
//让主线程休眠
Thread.sleep(1000*3);
System.out.println("main线程结束");
// main主线程修改t1线程的中断状态=true
// t1线程检测中断状态=true,则抛出InterruptedException,于线程执行结束
t1.interrupt();
}
}
(5)yield()方法:让出线程:当前线程愿意让出CPU给其他线程使用。
public static void main(String[] args) {
// 创建子线程1:打印字母A-Z
Thread thread1 = new Thread() {
public void run() {
for (char c = 'A'; c <= 'Z'; c++) {
System.out.println(c);
}
}
};
// 创建子线程2:打印数字65-90
Thread thread2 = new Thread() {
public void run() {
Thread.yield(); // 让当前线程让出CPU
for (int c = 65; c <= 90; c++) {
System.out.println(c);
// Thread.yield(); // 让当前线程让出CPU
}
}
};
// 启动子线程
thread1.start();
thread2.start();
}
(6)join()方法:插队:
t.join()方法会使当前线程( 主线程 或者调用t.join()的线程 )进入等待池,并等待 线程t 执行完毕后才会被唤醒。此时,并不影响同一时刻处在运行状态的其他线程。
public class Main {
public static void main(String[] args) throws InterruptedException {
System.out.println("主线程Main:开始执行,即将创建并调用子线程");
// 创建并启动子线程
MyThread myThread = new MyThread();
myThread.start();
// 主线程调用myThread子线程的join()方法
myThread.join(); // 子线程插队,插入到当前线程main的执行序列前
System.out.println("主线程Main:当子线程myThread执行完毕后,主线程Main再执行");
}
}
class MyThread extends Thread{
@Override
public void run() {
System.out.println("子线程:开始执行");
int sencondValue = (int)(Math.random()*1000);
try {
Thread.sleep(sencondValue);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("子线程:结束执行,耗时"+sencondValue+"毫秒");
}
}
(7)守护线程(Daemon Thread):当其他的非守护线程执行完毕后,守护线程会陆陆续续结束
package com.yuan.threadMethed;
public class Demo03 {
public static void main(String[] args) {
Thread t1 = new Thread("女神") {
@Override
public void run() {
for (int i = 'a'; i < 'z'; i++) {
System.out.println(getName() + " : " + i);
}
}
};
Thread t2 = new Thread("备胎") {
@Override
public void run() {
for (int i = 1; i < 100; i++) {
System.out.println(getName() + " : " + i);
}
}
};
//t2线程设置为守护线程
//细节:当其他的非守护线程执行完毕后,守护线程会陆陆续续结束
t2.setDaemon(true);
t1.start();
t2.start();
}
}
三、多线程的同步
1、锁:
每个Java对象都可以充当一个实现同步的锁,这些锁被称为内置锁(Intrinsic Lock)或者监视器锁(Monitor Lock)。
2、同步锁:
Synchronized同步锁,简单来说,使用Synchronized关键字将一段代码逻辑,用一把锁给锁起来,只有获得了这把锁的线程才访问。并且同一时刻, 只有一个线程能持有这把锁, 这样就保证了同一时刻只有一个线程能执行被锁住的代码,从而确保代码的线程安全。
3、多线程的数据不一致:
当多个线程同时运行时,线程的调度由操作系统决定,程序本身无法决定。因此,任何一个线程都有可能在任何指令处被操作系统暂停,然后在某个时间段后继续执行。这个时候,一个在单线程模型下不存在的问题就会发生:如果多个线程同时读写共享变量,会出现数据不一致的问题。
public class Main {
public static void main(String[] args) throws Exception {
Thread add = new AddThread();
Thread dec = new DecThread();
add.start();
dec.start();
add.join();
dec.join();
System.out.println(Counter.count);
}
}
class Counter {
public static int count = 0;
}
class AddThread extends Thread {
public void run() {
for (int i=0; i<10000; i++) { Counter.count += 1; }
}
}
class DecThread extends Thread {
public void run() {
for (int i=0; i<10000; i++) { Counter.count -= 1; }
}
}
在上述代码案例中,两个线程同时对一个int变量进行操作,一个加10000次,一个减10000次,最后结果应该是0,但是,每次运行,结果实际上都是不一样的。
这是因为对变量进行读取和写入时,结果要正确,必须保证是原子操作。原子操作是指不能被中断的一个或一系列操作。


通过加锁和解锁的操作,就能保证3条指令总是在一个线程执行期间,不会有其他线程会进入此指令区间。即使在执行期线程被操作系统中断执行,其他线程也会因为无法获得锁导致无法进入此指令区间。只有执行线程将锁释放后,其他线程才有机会获得锁并执行。这种加锁和解锁之间的代码块我们称之为临界区(Critical Section),任何时候临界区最多只有一个线程能执行。
4、synchronized 关键字的用法:
(1)修饰实例方法:
- 使用当前对象this充当锁,完成对当前方法的锁定,只有获取
this锁的线程才能访问当前方法; - 并发过程中,同一时刻,可以有
N个线程请求执行方法,但只有一个线程可以持有this锁,才能执行; - 不同线程,持有的对象,必须相同;
public class Foo {
// 实例方法
public synchronized void doSth1() {
// 获取this锁,才能执行该方法
}
// 实例方法
public void doSth2() {
synchronized(this) {
// 获取this锁,才能执行该代码块
}
}
}
(2)修饰静态代码块:
- 使用当前对象的
Class对象充当锁,完成对当前方法的锁定,只有获取Class锁的线程才能访问当前方法; - 不同线程,持有的对象,可以不同,但必须相同
class类型;
public static void main(String[] args) {
// 创建不同的对象(相同类型)
Foo fa = new Foo();
Foo fb = new Foo();
// 创建不同线程1
Thread thread01 = new Thread() {
public void run() {
// 使用不同的对象访问synchronized方法
fa.doSth2();
}
};
// 创建不同线程2
Thread thread02 = new Thread() {
public void run() {
// 使用不同的对象访问synchronized方法
fb.doSth2();
}
};
// 启动线程
thread01.start();
thread02.start();
}
class Foo {
// 静态方法
public synchronized static void doSth1() {
// 获取当前对象的Class对象锁,才能执行该方法
}
// 实例方法
public static void doSth2() {
synchronized(this.getClass()) {
// 获取当前对象的Class对象锁,才能执行该代码块
}
}
}
(3)修饰代码块:
/**1.锁代码块:
* 锁对象可以是任意类型的对象,必须要保证多条线程公用一个锁对象
* synchronized(锁对象) {
* 操作的共享的代码
* }
* 某条线程获取到了锁资源,锁关闭,当里面的任务执行完成,锁释放
* 默认情况下,锁是打开状态*/
package com.yuan.synchronizedclasss;
public class Demo02 {
public static void main(String[] args) {
ShoeMai t1=new ShoeMai("窗口1");
ShoeMai t2=new ShoeMai("窗口2");
ShoeMai t3=new ShoeMai("窗口3");
t1.start();
t2.start();
t3.start();
}
}
class ShoeMai extends Thread{
public static int ticket=0;
public static Object obj=new Object();
public ShoeMai(String name) {
super(name);
}
@Override
public void run() {
while (true){
synchronized (obj) {
if (ticket >= 100) {
break;
}
System.out.println(getName() + "正在售卖第" + ++ticket + "张票");
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}

被折叠的 条评论
为什么被折叠?



