多线程基础知识的学习
- 基本概念
- 多线程的创建方式
- 多线程的常用方法
- 多线程的生命周期
- 线程的安全问题
-
- 解决线程的安全问题
- 线程安全的单例模式之懒汉式
- 线程中的死锁问题
- 线程的通信
基本概念
- 程序: 为完成特定任务,用某种语言编写的一组指令的集合,即指一段静态的代码,静态对象;
- 进程: 是程序的一次执行过程,或是正在运行的一个程序,是一个动态的过程:有它自身的产生、存在和消亡的过程----生命周期;
- 线程 : 进程可以进一步细化为线程,是程序内部的一条执行路径,一个程序可以有多条执行路径;
程序是静态的,进程是动态的;
进程作为资源分配单位,系统在运行时会为每个进程分配不同的内存区域;
线程作为调度和执行的单位,每个线程拥有独立的运行栈和程序计数器;
一个进程中的多个线程共享相同的内存单元/内存地址空间–它们从同一堆中分配对象,可以访问相同的变量和对象,这就是线程间的通信更加的简便、高效,但是多线程操作共享的系统资源可能带来安全隐患;
多线程的创建方式
面试题:创建多线程的方式有几种?
答:四种;
1). 继承Thread类的方式创建线程;
2). 实现Runnable接口创建线程;
3). 使用Callable和Future创建线程;
4). 使用线程池例如用Executor框架;
1.继承Thread类的方式创建线程:
public class Main01 {
public static void main(String[] args) {
//给主线程命名:Thread.currentThread()是获取当前线程
Thread.currentThread().setName("主线程");
//打印
System.out.println(Thread.currentThread().getName()+"线程执行了");
//创建一个线程
MyThread myThread = new MyThread();
//启动线程
myThread.start();
}
}
class MyThread extends Thread{
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"线程执行了");
}
}
//输出
主线程线程执行了
Thread-0线程执行了
2.实现Runnable接口的方式创建线程:
public class Main02 {
public static void main(String[] args) {
//给main线程命名
Thread.currentThread().setName("主线程");
//打印
System.out.println(Thread.currentThread().getName()+"执行了");
//创建线程
MyRunnable myRunnable = new MyRunnable();
//将myRunnable传入Thread;类中
Thread thread = new Thread(myRunnable);
thread.start();
}
}
class MyRunnable implements Runnable{
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"执行了");
}
}
//输出
主线程线程执行了
Thread-0线程执行了
3.使用Callable和FutureTask创建线程
public class Main03 {
public static void main(String[] args) throws ExecutionException, InterruptedException {
//创建实现类
MyCallable myCallable = new MyCallable();
//创建FutureTask类,该类实现了Runnable接口;
FutureTask stringFutureTask = new FutureTask(myCallable);
//将创建的FutureTask的实例传递给Thread类,并启动线程
new Thread(stringFutureTask).start();
Object o = stringFutureTask.get();
System.out.println(o);
}
}
//MyCallable接口实现Callable接口,其中的call方法相当于我们的run方法
class MyCallable implements Callable{
@Override
public Object call() throws Exception {
int h = 0;
for(int i = 1; i <100; i++){
if(i % 2 == 0){
h+=i;
}
}
return Thread.currentThread().getName()+"求和为:--"+h;
}
}
4.使用线程池
public class Main04 {
public static void main(String[] args) {
//1.提供指定线程数量的线程池;
ExecutorService executorService = Executors.newFixedThreadPool(10);
//2.执行指定的线程操作,需要提供实现Runnable接口和Callable接口的实现类对象;
executorService.execute(new MyRunnable1());
//关闭连接池;
executorService.shutdown();
}
}
class MyRunnable1 implements Runnable {
@Override
public void run() {
System.out.println("这是使用线程池创建线程");
}
}
使用线程池的好处:
1.提高响应速度(减少了线程的创建时间)
2.降低了资源消耗(重复利用线程池中的线程,不需要每次都创建)
3.便于管理:
corePoolSize:核心池的大小
maximumPoolSize:最大线程数;
keepAliveTime:线程没有任务是最多保持多长时间后终止
多线程的常用方法
1.start():启动当前线程;调用当前线程的run();
2.run(): 通常需要重写Thread类中的此方法,将要执行的操作写入该方法中;
3.currentThread(): 静态方法,返回执行当前代码的线程;
4.getName():获取当前线程的名字;
5.setName():设置当前线程的名字;
6.yield():释放当前cpu的执行权;
7.join():在线程A中调用线程B的join(),此时线程A就会进入阻塞状态,直到线程B完全执行完以后,线程A才结束阻塞状态;
8.stop():已过时,当执行此方法是,强制结束当前线程;
9.sleep(long millitime) : 使当前线程睡眠指定的毫秒数,在此期间,该线程处于阻塞状态;
10. isAlive(): 判断当前线程是否存活;
多线程的生命周期
线程的安全问题
1.多个线程执行的不确定性造成执行结果的不稳定
2.多个线程对账本的共享,会造成操作的不完整性,会破坏数据;
解决线程的安全问题
解决线程安全的方式有几种?
答:3种;
1.同步代码块—synchronized(同步监视器){需要被同步的代码}
public class SYNdemo01 {
public static void main(String[] args) {
MyRunnable myRunnable = new MyRunnable();
Thread thread1 = new Thread(myRunnable);
Thread thread2 = new Thread(myRunnable);
thread1.start();
thread2.start();
}
}
class MyRunnable implements Runnable{
private int x = 150;
@Override
public void run() {
while (true){
synchronized (this){
if(x>0){
try {
Thread.currentThread().sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"窗口卖出了:"+x+"号票");
x--;
}else{
break;
}
}
}
}
}
synchronized(锁){ 需要被同步的代码}
1.操作共享数据的代码,即需要被同步的代码;
2.共享数据,多个线程同时操作的变量,比如我们现实中的火车票
3.同步监视器:俗称:锁,任何一个类的对象,都可以充当锁对象;
要求:多个线程必须要共用一把锁,即这个锁对象在多线程操作时必须是同一个锁对象;
2.同步方法—被synchronized修饰的方法
public class SYNdemo01 {
public static void main(String[] args) {
MyRunnable myRunnable = new MyRunnable();
Thread thread1 = new Thread(myRunnable);
Thread thread2 = new Thread(myRunnable);
thread1.start();
thread2.start();
}
}
class MyRunnable implements Runnable{
private int x = 150;
@Override
public void run() {
while (true){
loket();
if(x<=0){
break;
}
}
}
//同步方法
public synchronized void loket(){
if(x>0){
try {
Thread.currentThread().sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"窗口卖出了:"+x+"号票");
x--;
}
}
}
3.Lock锁解决线程安全问题
Lock锁是JDK5.0后加入的,是一个接口,比较常用的是ReentrantLock类,他拥有与synchronized相同的并发性和内存语义,在实现线程安全的控制中,比较常用的是ReentrantLock,可以显式加锁、释放锁;
public class LockSuo {
public static void main(String[] args) {
Window window = new Window();
Thread thread1 = new Thread(window);
Thread thread2 = new Thread(window);
thread1.start();
thread2.start();
}
}
class Window implements Runnable{
private static int x = 150;
private ReentrantLock reentrantLock = new ReentrantLock();
@Override
public void run() {
while (true){
try{
reentrantLock.lock();
if(x>0){
try {
Thread.currentThread().sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"窗口出售了:"+x+"号票");
x--;
}else{
break;
}
}finally {
reentrantLock.unlock();
}
}
}
}
线程安全的单例模式之懒汉式
class BankTest{
private static BankTest bankTest;
private BankTest(){
}
public static BankTest getBankTest(){
synchronized (BankTest.class) {
if (bankTest == null) {
bankTest = new BankTest();
}
}
return bankTest;
}
}
线程中的死锁问题
死锁: 不同的线程分别占用对方需要的同步资源不放弃,都在等待对方放弃自己需要的同步资源,就形成了线程的死锁;
死锁出现后,不会出现异常,不会出现提示,只是所有的线程都处于阻塞状态,无法继续;
死锁演示:
public class SYNdemo02 {
public static void main(String[] args) {
StringBuffer s1 = new StringBuffer();
StringBuffer s2 = new StringBuffer();
new Thread(){
@Override
public void run() {
synchronized (s1){
s1.append("a");
s2.append("1");
try {
Thread.currentThread().sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (s2){
s1.append("b");
s2.append("2");
System.out.println(s1);
System.out.println(s2);
}
}
}
}.start();
new Thread(){
@Override
public void run() {
synchronized (s2){
s1.append("c");
s2.append("3");
try {
Thread.currentThread().sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (s1){
s1.append("d");
s2.append("4");
System.out.println(s1);
System.out.println(s2);
}
}
}
}.start();
}
}
线程的通信
例题:交替打印1-100;线程1和线程2交替打印;
public class JT {
public static void main(String[] args) {
Number number = new Number();
Thread thread1 = new Thread(number);
Thread thread2 = new Thread(number);
thread1.start();
thread2.start();
}
}
class Number implements Runnable {
private static int x = 1;
@Override
public void run() {
while (true) {
synchronized (this) {
if (x <= 100) {
notify();
try {
Thread.currentThread().sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "打印了:" + x);
x++;
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
} else {
break;
}
}
}
}
}