线程
线程是进程中负责程序执行的最小单位,任何进程中都至少包括一个线程。
进程
什么是进程,我们打开任务管理器
上面任务管理器中每个独立运行的程序都是进程,进程之间是独立存在的
说完了线程和进程,我们也要了解一下他们的区别
线程是进程的子集,一个进程可以有很多线程,每条线程并行执行不同的任务。
不同的进程使用不同的内存空间,而所有的线程共享一片相同的内存空间。
别把它和栈内存搞混,每个线程都拥有单独的栈内存用来存储本地数据。
多线程
> 创建多线程的几种方法:
> 1.继承Thread类
> 2.实现Runnable接口
> 3.实先Callable接口通过FutureTask包装器来创建Thread线程
> 4.使用ExecutorService、Callable、Future实现有返回结果的多线程(也就是使用了 ExecutorService来管理前面三种方式)
> 我们这里主要介绍前两种方法
1.继承Thread类
启动线程:子类对象.start();
//总结:线程开启不一定立即执行,由CPU调度执行
public class ThreadTest extends Thread{
@Override
public void run() {
//run方法线程体
for (int i=0;i<10;i++){
System.out.println("我在看代码--"+i);
}
}
public static void main(String[] args) {
ThreadTest threadTest = new ThreadTest();
threadTest.start();
for (int i=0;i<10;i++){
System.out.printf("我在学习多线程--"+i+"\n");
}
}
}
结果:
我在学习多线程--0
我在看代码--0
我在学习多线程--1
我在学习多线程--2
我在学习多线程--3
我在看代码--1
我在学习多线程--4
我在看代码--2
我在学习多线程--5
我在学习多线程--6
我在学习多线程--7
我在学习多线程--8
我在学习多线程--9
我在看代码--3
我在看代码--4
我在看代码--5
我在看代码--6
我在看代码--7
我在看代码--8
我在看代码--9
2.实现Runnable接口
启动线程:传入目标对象+Thread对象.start()
package com.cui;
//实现Runnable接口
public class ThreadTest2 implements Runnable{
@Override
public void run() {
for (int i=0;i<10;i++){
System.out.println("我在看代码--"+i);
}
}
public static void main(String[] args) {
ThreadTest2 thread = new ThreadTest2();
new Thread(thread).start();
for (int i=0;i<10;i++){
System.out.printf("我在学习多线程--"+i+"\n");
}
}
}
结果和继承Thread类“一样”,两个线程在交替运行,谁获得时间片谁运行.
线程安全
既然有线程安全这个概念,那么线程安全一定是发生在多线程中的。
那么如何说明一个程序是安全的呢?
存在竞争的线程就是不安全的,不存在竞争的线程才是安全的!
借用一个百度百科对线程安全的定义:
在拥有共享数据的多条线程并行执行的程序中,线程安全的代码会通过同步机制保证各个线程都可以正常且正确的执行,不会出现数据污染等意外情况。
举个栗子:
ArrayList是非线程安全的,Vector是线程安全的;
ArrayList和Vector底层都是List数组,但是Vector底层方法是synchronized修饰的,保证在同一时间只有一个线程能够调用代码块或方法
HashMap是非线程安全的,HashTable是线程安全的;
HashMap继承自AbstractMap类,HashTable继承自Dictionary类,底层方法也是由synchronized修饰。HashMap允许key和value都为null,但是只能有一个key为null,value值可以为多个;HashTable不允许key和value为空。
StringBuilder是非线程安全的,StringBuffer是线程安全的。
StringBuilder和StringBuffer都是可变字符串,但是StringBuffer底层是synchronized方法修饰的