多线程创建(二) 实现Runnable接口
注:重写Runnable接口的 run() 方法
public class test1 {
public static void main(String[] args) {
//4、创建线程对象
MyRunnable target = new MyRunnable();
//能不能 把 target和Thread 绑定关系--利用了Thread类的构造方法
Thread thread = new Thread(target);
//5、启动线程
thread.start();
//模拟多线程程序
Thread thread2 = new Thread(target);
//5、启动线程
thread2.start();
thread.setName("1号线程");
thread2.setName("2号线程");
}
}
//1、自定义多线程类 implements Runnable
//2、实现了接口以后的实现类,有两种选择:作为一个抽象类+把所有抽象方法都重写
class MyRunnable implements Runnable{
//3、把你自己的业务,都放在重写的run()
@Override
public void run() {
for( int i = 0 ; i < 10 ; i++ ) {
//Thread.currentThread()是获取正在执行任务的Thread对象。
System.out.println(i+"==="+Thread.currentThread().getName());
}
}
}
售票案例
需求:设计4个售票窗口,总计售票100张
方案1:继承Thread
public class test2 {
public static void main(String[] args) {
//3、创建线程对象
MyTicket target = new MyTicket();
//4、启动线程
target.start();
//问题1:我们总共要卖100张票,但是现在卖了400张票,为什么?-- 因为每个对象初始化时都初始化了自己的ticket100张票
//模拟多线程
MyTicket target2 = new MyTicket();
target2.start();
MyTicket target3 = new MyTicket();
target3.start();
MyTicket target4 = new MyTicket();
target4.start();
}
}
//1、自定义多线程类
class MyTicket extends Thread {
// int tickets = 100 ; //定义变量,记录票数 //成员变量 - 实例变量 ,这个资源会跟着对象存在,new几次就有几份
//问题1的解决方案:把资源变成共享资源,不跟随对象产生,保证程序中只有这1份资源,只有100张票。
static int tickets = 100 ;
//2、所有业务放入重写的run()
@Override
public void run() {
while(true) {//一直卖票
if( tickets>0 ) {
try {
//5、增加延迟性的考察。
//问题2:程序出现了超卖的现象:0 -1 -2张票。
//问题3:程序出现了重复卖的现象:一张票卖给多个人。
Thread.sleep(10);//让程序休眠10ms
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println( getName()+"="+tickets-- );
}else
break;//<=0时结束死循环
}
}
}
方案2:实现 Runnable 接口
public class test3 {
public static void main(String[] args) {
//3、创建线程对象
MyTickets2 target = new MyTickets2();
Thread t1 = new Thread(target);
//4、启动线程
t1.start();
//模拟多线程
Thread t2 = new Thread(target);
t2.start();
Thread t3 = new Thread(target);
t3.start();
Thread t4 = new Thread(target);
t4.start();
}
}
//1、自定义多线程类
class MyTickets2 implements Runnable{
int tickets = 100 ;
//2、重写run(),把业务放进来
@Override
public void run() {
while(true) {//一直卖100张票
if( tickets > 0 ) {
//多线程的延迟访问
try {
//问题1:出现了超卖现象:卖出了0 -1 -2张票
//问题2:出现了重卖现象:同一张票卖给了多个人
Thread.sleep(10);//睡10ms
} catch (InterruptedException e) {
e.printStackTrace();
}
//Thread.currentThread().getName() 正在执行任务的线程名称
System.out.println( Thread.currentThread().getName() +"="+ tickets-- );
}else {
break;
}
}
}
}
1、多线程安全问题是如何出现的?常见情况是由于线程的随机性 + 访问延迟sleep()。
2、以后如何判断程序有没有线程安全问题?在多线程程序中 + 有共享数据 + 多条语句操作 共享数据。
同步锁 synchronized关键字
同步是:共享资源同一时刻只有一个线程在操作,其他线程在等待。
异步是:多个线程一起操作了共享数据
synchronized 可以修饰方法称为同步方法。也可以修饰代码块称为同步代码块。
注:使用同步锁,需考虑两件事情: 使用锁的位置 + 使用锁的对象
如果共享的资源是静态资源,synchronized 锁对象为类的字节码,synchronized(类名.class)
售票案例解决
public class test4 {
public static void main(String[] args) {
// 3、创建线程对象
MyTickets2 target = new MyTickets2();
Thread t1 = new Thread(target);
// 4、启动线程
t1.start();
// 模拟多线程
Thread t2 = new Thread(target);
t2.start();
Thread t3 = new Thread(target);
t3.start();
Thread t4 = new Thread(target);
t4.start();
}
}
// 1、自定义多线程类
class MyTickets2 implements Runnable {
int tickets = 100;
// Object obj = new Object();
String s = "abc";
// 2、重写run(),把业务放进来
@Override
// synchronized public void run() {//8、同步方法
public void run() {
while (true) {// 一直卖100张票
// 5、 同步锁的位置:是从问题发生点开始,到结束为止。
// 6、 同步锁的对象:如果是同步代码块,根本不关心锁对象是谁,只要是同一个对象就可以。
// synchronized (new Object()) {//每个线程有自己的对象
// synchronized (obj) {//多个线程使用了同一个对象obj,可以解决问题
// 7、同步可以修饰方法,如果方法里的代码都有安全隐患,直接修饰成同步方法就可以了。
synchronized (s) {// 多个线程使用了同一个对象s,也可以解决问题
if (tickets > 0) {
// 多线程的延迟访问
try {
// 问题1:出现了超卖现象:卖出了0 -1 -2张票
// 问题2:出现了重卖现象:同一张票卖给了多个人
Thread.sleep(10);// 睡10ms
} catch (InterruptedException e) {
e.printStackTrace();
}
// Thread.currentThread().getName() 正在执行任务的线程名称
System.out.println(Thread.currentThread().getName() + "=" + tickets--);
} else {
break;
}
}
}
}
}
单例设计模式
一个类在内存中的对象只有一个
饿汉式
public class test5 {
public static void main(String[] args) {
//4、调用了MySingleton内部创建好的对象
MySingleton m1 = MySingleton.getSingle();
//5、全局真的只要一个对象吗?
MySingleton m2 = MySingleton.getSingle();
//==比较引用类型比较的是地址值
System.out.println(m1 == m2);
}
}
//自定义单例类
class MySingleton {
//1、私有化构造方法,目的是不让外界随意new
private MySingleton() {}
//2、在类的内部 ,提供创建好的对象
//static--因为静态只能调静态,所以想要被getSingle()调用,必须也是静态资源
static private MySingleton single = new MySingleton();
//3、提供公共的 ,访问方式,把single返回给外界调用位置
static public MySingleton getSingle(){
return single ;
}
}
懒汉式
public class test6{
public static void main(String[] args) {
MySingleton2 m = MySingleton2.getSingle();
MySingleton2 m2 = MySingleton2.getSingle();
System.out.println(m==m2);
}
}
class MySingleton2 {
// 1、私有化构造方法,目的是不让外界随意new
private MySingleton2() {}
// 2、在类的内部 ,提供创建好的对象
// static--因为静态只能调静态,所以想要被getSingle()调用,必须也是静态资源
static private MySingleton2 single; // 懒汉式
// 3、提供公共的 ,访问方式,把single返回给外界调用位置
synchronized static public MySingleton2 getSingle() {// 直接把方法同步了,默认使用的锁对象是this
// synchronized (MySingleton2.class){//如果你要锁的共享资源是静态的,此时,锁对象必须是MySingleton2的字节码对象
if (single == null) {
single = new MySingleton2();
}
return single;
// }
}
}