多线程的安全问题,单例模式和线程池的详解
多线程的实现方式
1.继承Thread
步骤:
定义一个类,继承Thread类
重写自定义的run()方法,用于定义新线程要运行的内容
创建自定义类型的对象
调用线程启动的方法:start();
package cn.ujiuye.threads;
/**
* @author liugang
*
*/
public class ThreadDemo01 {
public static void main(String[] args) {
//创建自定义类型了对象
MyThread myThread = new MyThread();
//开启线程
myThread.start();
for (int i = 0; i < 100; i++) {
System.out.println("主线程程执行了*****" + i);
}
}
}
//自定义线程类继承Thread
class MyThread extends Thread {
@Override
public void run() {
for (int i = 0; i < 100; i++) {
System.out.println("mythread线程执行了......" + i);
}
}
}
2.实现Runnable()接口
Runnable接口的实现类对象,表示一个具体的任务,将来创建一个线程对象之后,让线程执行这个任务
步骤:
1.重写一个任务类,实现Runnable接口
2.重写任务中的run()方法,用于定义任务类中的内容
3.创建任务类对象,表示任务
4.创建一个Thread类型的对象,用于执行类的对象
5.调用线程的start()方法,开启新线程
package cn.ujiuye.threads;
/**
* @author liugang
* 创建线程对象,实现接口的方式实现
*/
public class ThreadDemo02 {
public static void main(String[] args) {
//创建任务类对象
MyRunnable mr = new MyRunnable();
//运行时对象
Thread th = new Thread(mr);
th.start();
for (int i = 0; i <= 100; i++) {
System.out.println("主线程执行了......" + i);
}
}
}
//重写任务类对象
class MyRunnable implements Runnable{
//重写run()方法
@Override
public void run() {
for (int i = 0; i <= 100; i++) {
System.out.println("myrunnable线程执行了......" + i);
}
}
}
多线程中的线程安全问题
问题描述:
有些代码在没有执行完成的时候,cpu就可能被其他的线程抢走,结果导致当前代码的一些数据出现了错误
原因:没有保证某段代码的完整性
希望:这段代码块要么全部执行了,要不全部不执行
同步代码块:
使用的一种格式,达到让某段代码运行的时候,cpu不要切换影响到当前代码块的执行
使用格式:
synchronized (锁对象){
需要保证完整性,原子性的一段代码(需要同步的代码)
}
使用同步代码块的效果:
当cpu想去执行同步代码块的时候,需要先获取到锁对象,获取之后就可以运行代码块中的内容,当cpu正在执行当前代码块的时候,cpu可以切换到其他的代码块,但是不能够切换到具有相同锁对象的代码上
当cpu执行当前代码块之后,就会释放锁资源,cpu就可以运行到其他具有当前锁对象的同步的代码块了
同步方法
当前的代码在执行的时候,不希望cpu切换到其他的当前的线程的线程中去,就可以在这段代码块上加上同步代码块
同步代码块的格式:
权限修饰符 【静态修饰符】 synchrionized 返回值类型 方法名称 (参数列表){
需要同步的方法体
}
同步代码块的锁对象
如果是非静态的方法,同步方法的锁对象就是this,当前对象,那个对象调用这个同步方法,这个对象使用的锁就是这个对象
如果是静态方法,同步方法的锁对象就是当前的类的字节码对象,类名.class文件,那个类在调用这个方法,这个同步方法使用的锁就是那个类的字节码对象
线程池
在没有任务的时候,就先把线程对象创建好,储存到一个容器中,一旦有任务来了,就不需要创建对象,而是直接将对象获得的内容获取出来去执行任务
如果任务破坏力比较小,任务就可以完成,这个线程对象就不会进入死亡状态,而是被容器回收,继续活跃
如果任务破坏力比较大,任务会把线程杀死,线程池就会继续提供下一个线程,继续完成这个任务
使用:
1.步骤:获取线性池的对象,创建任务性对象,将任务性的对象提交到线程池中
2.获取线程池对象:
工具类Executors:生成线程池的工具类,根据需求生成之指定大小的线程池
Executors.newFixedThreadPool(int nthreads) 创建一个指定数据的线程池
Executors.newSingleThreadPool() 创建一个有单个线程对象的线程池
3.创建任务性对象,Runnable的实现类对象,用于定义任务对象
4.将任务类对象提交到线程池中
submit(Runnable r):可以将一个任务类对象,提交到线程池中,如果有空闲的线程,就可以马上运行的任务,如果空闲线程,那么这个任务就是需要不段的等待
shutDown():结束线程池,已经提交的的保证全部完成,但是没有还运行的,已经提交的,不给运行了,作为返回值范围,对应没有提交的,不准提交
package cn.ujiuye.safe;
import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
/**
* @author liugang
*
*/
public class ThreadDemo05 {
public static void main(String[] args) {
//准备两条线程
ExecutorService threadPool = Executors.newFixedThreadPool(2);
//开启三个对象,分别将一个线程对象提交到对应的每个任务型对象放到指定的线程中
threadPool.submit(new MyTask());
threadPool.submit(new MyTask());
threadPool.submit(new MyTask());
//结束线程池,提交的开始运行,保证完成,没有提交的就不运行了
List<Runnable> runnables = threadPool.shutdownNow();
System.out.println(runnables);
}
}
//创建任务类对象
class MyTask implements Runnable{
@Override
public void run() {
for(int i = 1;i <= 1000;i++) {
System.out.println(Thread.currentThread().getName()+"...."+i);
}
}
}
单例设计模式
在当前的系统中,某个类型的对象,最多有一个,就需要使用单例模式
单例模式的设计原则:
1.构造方法私有化
2.在类中创建好对象
3.在类中,给外界提供获取该对象的公有方式
饿汉式:
在加载类的同时,就要初始化成员变量,所以就同时将对象的创建出来
饿汉式:一有机会,马上就吃,不去等待(一旦加载对象,马上创建对象)
package cn.ujiuye.design;
/**
* @author liugang
* 饿汉式
*/
public class MySingleTon1 {
public static void main(String[] args) {
//访问对象
SingleDemo1 s1 = SingleDemo1.instence();
SingleDemo1 s2 = SingleDemo1.instence();
System.out.println(s1.equals(s2));
}
}
class SingleDemo1{
//私有化构造方法
private SingleDemo1(){
}
//私有化对象
private static SingleDemo1 sd = new SingleDemo1();
//提供外界的访问方式
public static SingleDemo1 instence() {
return sd;
}
}
懒汉式
1.在加载类的时候,不同时创建该类对象,等待需要这个对象的时候,才会创建这个对象
2.懒汉式:不能着急,能不创建的时候就不创建,能拖就拖
注意事项:
只有在s1 == null的时候,才会创建对象
s1的判断和s1的赋值,不希望被分开,否则在多线程的环境下,就会出现多个对象的状态,所以是s1的判断和赋值要在同一个代码块中,
同步代码块的效率很低,不是每次获得对象的时候,都需要判断锁对象,所有在s1==null的时候,才能判断锁对象,因此需要在外层嵌套一个if判断,判断是否是null
package cn.ujiuye.design;
/**
* @author liugang
* 懒汉式
*/
public class MySingleTon2 {
public static void main(String[] args) {
//创建对象
SingleDemo2 s1 = SingleDemo2.instence();
SingleDemo2 s2 = SingleDemo2.instence();
System.out.println(s1.equals(s2));
}
}
class SingleDemo2{
//私有化构造方法
private SingleDemo2() {
}
//创建对象
private static SingleDemo2 m;
//对外界提供一个访问的方法
public static SingleDemo2 instence() {
//加一个判断,则有可能有几个线程过后就再也不会出现null的机会了,就不会再判断了
if (m == null) {
//同时几个线程,有可能一个还是null的时候,线程还在外面等待 线程2.线程3,线程4在外面等待
synchronized (SingleDemo2.class) {
if (m == null) {
//如果真是null,则创建
return new SingleDemo2();
}
}
}
return m;
}
}