从零开始学Java-Day17
Posted 无声specialweek
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了从零开始学Java-Day17相关的知识,希望对你有一定的参考价值。
多线程编程的两种实现方式
- extends Thread
- 优点:
- 缺点:后续变化小,局限性大
- implement Runnable
- 优点:多实现,更加灵活且解耦
- 缺点:写法相对复杂,一些资源需要借助Thread
多线程数据安全隐患
- 怎么产生?线程的随机性+访问延迟
- 以后如何判断程序有没有线程安全问题
在多线程程序中 + 有共享数据 + 多条语句操作共享数据
- 单线程程序不会出现多线程抢占资源的情况
- 如果没有共享数据,互不干涉,也不会出现数据安全问题
- 多条语句操作了共享数据,而多条语句执行是需要时间的,存在延迟所以这个时间差导致了数据的安全问题
package cn.tedu.tickets;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class TestRunnableV2 {
public static void main(String[] args) {
TicketRunnable target = new TicketRunnable();
// Thread t1 = new Thread(target, "黄金船");
// Thread t2 = new Thread(target, "目白麦昆");
// Thread t3 = new Thread(target, "东海帝王");
// Thread t4 = new Thread(target, "小栗帽");
// t1.start();
// t2.start();
// t3.start();
// t4.start();
//线程池ExecutorService:用于存储线程的池子,把新建/启动/关闭线程都交给池来做
//Executors:用于创建线程池的工具类,newFixedThreadPool()
ExecutorService pool = Executors.newFixedThreadPool(5);
for (int i = 1; i <= 5; i++){
pool.execute(target);
}
pool.shutdown();
}
}
class TicketRunnable implements Runnable{
static int tickets = 100;
Object object = new Object();
@Override
public void run() {
while (true){
/*锁对象必须唯一*/
/*
如果一个方法中所有代码均需要被同步,那么可以用 synchronized 修饰
被synchronized关键字修饰的方法称为同步方法
*/
synchronized (object){
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
if (tickets > 0){
System.out.println(Thread.currentThread().getName() + "卖了第" + tickets-- + "张票" );
}
if (tickets <= 0){
break;
}
}
}
}
}
多线程安全问题解决
加锁:注意
- 加锁的位置
同步|亦步
同步:类似于排队效果
- 优点:线程安全
- 缺点:效率低
异步:不排队,多线程效果,各线程都抢占资源
- 优点:效率高
- 缺点:线程不安全
双重校验机制
-
增加判断控制--有票的时候在卖票
-
同步代码块,主要强调同步,同一时刻同一资源只能被一个线程对象
-
sychronized(锁对象){可能会发生安全问题的代码}
- 同步代码块使用的锁对象必须唯一
package cn.tedu.tickets;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class TestRunnableV2 {
public static void main(String[] args) {
TicketRunnable target = new TicketRunnable();
// Thread t1 = new Thread(target, "黄金船");
// Thread t2 = new Thread(target, "目白麦昆");
// Thread t3 = new Thread(target, "东海帝王");
// Thread t4 = new Thread(target, "小栗帽");
// t1.start();
// t2.start();
// t3.start();
// t4.start();
//线程池ExecutorService:用于存储线程的池子,把新建/启动/关闭线程都交给池来做
//Executors:用于创建线程池的工具类,newFixedThreadPool()
ExecutorService pool = Executors.newFixedThreadPool(5);
for (int i = 1; i <= 5; i++){
pool.execute(target);
}
pool.shutdown();
}
}
class TicketRunnable implements Runnable{
static int tickets = 100;
Object object = new Object();
@Override
public void run() {
while (true){
/*锁对象必须唯一*/
/*
如果一个方法中所有代码均需要被同步,那么可以用 synchronized 修饰
被synchronized关键字修饰的方法称为同步方法
*/
synchronized (object){
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
if (tickets > 0){
System.out.println(Thread.currentThread().getName() + "卖了第" + tickets-- + "张票" );
}
if (tickets <= 0){
break;
}
}
}
}
}
package cn.tedu.tickets;
//本类用于解决继承下多线程售票案例的数据安全问题
public class TestExtendV2 {
public static void main(String[] args) {
TicketThreadV2 t1 = new TicketThreadV2("小林");
TicketThreadV2 t2 = new TicketThreadV2("托尔");
TicketThreadV2 t3 = new TicketThreadV2("法夫涅尔");
TicketThreadV2 t4 = new TicketThreadV2("康纳");
// t1.setName("小林");
// t2.setName("托尔");
// t3.setName("法夫涅尔");
// t4.setName("康纳");
t1.start();
t2.start();
t3.start();
t4.start();
}
}
class TicketThreadV2 extends Thread{
private static int tickets = 100;
public TicketThreadV2() {
}
public TicketThreadV2(String name) {
setName(name);
}
@Override
public void run() {
while (true){
/*
* 双重校验二:使用同步代码块
* synchronized(锁对象){容易发生数据安全问题的代码}
* 在同步代码块中,同一时刻,同一资源只能被一个线程独享,排队
* 注意:锁对象必须唯一,如果不唯一,还会发生安全问题
* 如果是继承的方式,锁对象一般使用本类的字节码对象
*/
synchronized (TicketThreadV2.class){
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
if (tickets <= 0){
break;
}
if (tickets == 1){
System.out.println("票卖完了");
}
//双重校验一
if (tickets > 0){
System.out.println(getName() + "买到第" + tickets-- + "张票");
}
}
}
}
}
ExecutorService/Executors
ExecutorService:用来存储线程的池子,把新建线程/启动线程/关闭线程的任务都交给池来管理
- execute(Runnable任务对象) 把任务丢到线程池
Executors 辅助创建线程池的工具类
- newFixedThreadPool(int nThreads) 最多n个线程的线程池
- newCachedThreadPool() 足够多的线程,使任务不必等待
- newSingleThreadExecutor() 只有一个线程的线程池
线程池
ExecutorService:用来存储线程的池子,把新建线程/启动线程/关闭线程的任务都交给池来管理
创建线程池的工具类:Executors.newFixedThreadPool(int 自定义线程数)
启动线程池中的线程:pool.execute(target目标业务对象)
线程池会自动管理线程,线程池目前单机测试不关闭,需要手动关闭
线程锁
悲观锁(sychronized):也叫互斥锁
乐观锁
设计模式
单例模式可以说是大多数开发人员在实际中使用最多的,常见的Spring默认创建的bean就是单例模式的。
单例模式有很多好处,比如可节约系统内存空间,控制资源的使用。
其中单例模式最重要的是确保对象只有一个。
简单来说,保证一个类在内存中的对象就一个。
单例设计模式:
- 创建一个私有化静态方法
- 私有化的构造方法不让外部调用
- 通过自定义的静态方法获取实例
实现思路:
- 构造方法私有化--为了防止外部直接调用本类构造方法
- 本类调用构造方法创建私有对象--对象私有化是为了不让外界直接获取
- 提供公共的全局访问点--为了让外界按指定的方式获取对象
package cn.likou.demo06;
public class Test02 {
private Test02(){}
static private Test02 t = new Test02();
public static Test02 go(){
return t;
}
public static void main(String[] args) {
Test02 t1 = Test02.go();
Test02 t2 = Test02.go();
System.out.println(t1);
System.out.println(t2);
System.out.println(t1 == t2);
MySingle s1 = MySingle.go();
MySingle s2 = MySingle.go();
System.out.println(s1);
System.out.println(s2);
System.out.println(s1 == s2);
}
}
class MySingle{
private MySingle(){}
static private MySingle mySingle;
public static MySingle go(){
if (mySingle == null){
mySingle = new MySingle();
}
return mySingle;
}
}
以上是关于从零开始学Java-Day17的主要内容,如果未能解决你的问题,请参考以下文章
自学it18大数据笔记-第一阶段Java-day16-day17-day18-day19--day20-day21-day22——会持续更新