java 基础学习-04 线程学习案例

第一 ,线程的使用创建方式 继承或者 实现

package com.itheima;
/*******
 * 定义类 继承线程 重写 run方法
 */
class ti extends Thread{
private  static int tick=100;//若是不定义为静态变量  则 四个人卖四百张票  我们定义为静态使每个对象都使用一个静态变量
public void run() {
// TODO Auto-generated method stub
while (true) {if (tick>0) {
System.out.println("买票者 "+Thread.currentThread().getName()+"---"+tick--);
}
}
}
}
/*******
 * 1.定义类实现 runnable接口
 *2.实现 runnable接口  覆盖run 方法  将线程运行代码放入
 *3.通过thread 建立线程对象
 *4.将runnable接口的子类对象  作为实际参数传递给 thread类的构造函数
 *为什么要将runnable接口的子类对象传递给thread的构造函数
 *因为:自定义的 run方法 所属的对象是 runnable接口的子类对象
 *所以要让线程去指定对象的 run 方法 就必须明确该run方法的所属对象
 *5.调用thread类的start 开启线程并调用  runnable接口子类的 runnable 方法
 */
class ticketer implements Runnable{
private  int tick=100;
public void run() {
while (true) {if (tick>0) {
System.out.println("买票者 "+Thread.currentThread().getName()+"---"+tick--);
}
}
}
}
 
 
public class tivket {
public static void main(String[] args) {
// TODO Auto-generated method stub
//使用静态变量 这样不会出现问题
ti t1 = new ti();
ti t2 = new ti();
ti t3 = new ti();
ti t4 = new ti();
t1.start();
t2.start();
t3.start();
t4.start();
// 另外一个方法  不适用静态方法 只初始化一个对象 操作一个对象 启动四次 但是会提示线程 状态出问题 因为 启动多次
//t1.start();
//t1.start();
//t1.start();
/*************************************************************************/
//第二种 方法创建  这
ticketer ttt = new ticketer();
Thread th1 = new Thread(ttt);
Thread th2 = new Thread(ttt);
Thread th3 = new Thread(ttt);
Thread th4 = new Thread(ttt);
th2.start();
th1.start();
th3.start();
th4.start();
}
}

 

 2.同步代码块 

  就是讲某一块代码或者方法加锁  每次都只能一个线程来执行  只有一个线程执行完这部分代码  才能有下一个线程来执行这块的代码。

同步方法  同步代码块或者 同步方法

  线程出现的问题

当上例的 ticker 类 run方法

public void run() {
while (true) {if (tick>0) {
try {
Thread.sleep(15);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println("买票者 "+Thread.currentThread().getName()+"---"+tick--);
}
}}


 

打引出来的结果会出现 -1 等错误的票。现在我们来分析这个错误的原因。  假设线程执行到if (tick>0) 此时票数为1,而没执行票数减少,之后 休眠,在线程1休眠的时候线程二进入了  此时的票数让然为然后线程2执行判断语句。符合条件,没执行减少语句,票数仍然为1,然后休眠,然后线程三进入  判断 条件符合,休眠票数仍然为一。然而此时 线程复活了, 直接执行了买票的 语句 此时为0,线程也直接执行了 票数减少语句  票数成了-1 而线程三的执行的时候 ,票数则成了负2. 这样我们就出现了错票的问题  怎么解决呢 , 同步代码块

同步代码块的 要求

 

/************
*明确哪些代码是多线程运行的代码
*明确共享数据
*明确多线程运行代码哪些语句是操作共享数据的
*/
  	修改为一下代码  结果正确
public void run() {
while (true)
synchronized (ticketer.class) {
{if (tick>0) {
try {
Thread.sleep(15);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println("买票者 "+Thread.currentThread().getName()+"---"+tick--);
}
}
}

 案例二  同步函数案例

/******
 * 函数目的  两个储户 往银行中存钱 计算 银行金钱总数
 */
/******
 * 
找多线运行的代码
明确共享数据
明确操作共享数据的代码
 */
class bank{
private int suml;//用函数来同步方法 
public synchronized  void add(int a ){
suml=suml+a;
try {
Thread.sleep(20);//  若果不进行同步 此时睡眠的时间不同   打印的函数是不同的    还是不同线程操作代码块的问题
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("suml "+this.getClass()+"----"+ suml);
}
}
class cus implements Runnable{
private bank b=new bank();
public void run() {
for (int i = 0; i <3; i++) {
b.add(111);
}
}
}
public class bankdemo {
public static void main(String[] args) {
cus c=new cus();
Thread th1=new Thread(c);
Thread th2=new Thread(c);
th1.start();
th2.start();
}
 
}
 


继续分析买票程序改为同步出现的错误 

public synchronized void run() {
while (true) {
{if (tick>0) {
try {
Thread.sleep(15);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println("买票者 "+Thread.currentThread().getName()+"---"+tick--);
}}}


这样修改  我们发现这样执行 全是一个线程在操作 原因是 我们将锁加到了函数上  因为 某一个线程进入之后 执行完之后才放开锁 所以这么加是不合适的  所以我们  封装一下执行方法

public void run() {
// TODO Auto-generated method stub
//另外一个方法 只
// TODO Auto-generated method stub
while (true) {
Show();// 即可
}
}
Public synchronized  void show (){
if (tick>0) {
try {
Thread.sleep(15);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println("买票者 "+Thread.currentThread().getName()+"---"+tick--);
 
}}

同步函数需要被对象调用 既执行 show方法的时候  其实是  this.show();

所以同步函数使用的锁是 this

下面我们来验证这个使用的是this

 

一个线程在同步代码块中

一个线程在同步函数中

都执行买票动作

 

 
 
 
public class tivket {
public static void main(String[] args) {
ticketer ttt = new ticketer();
Thread th1 = new Thread(ttt);
Thread th2 = new Thread(ttt);
try {
Thread.sleep(30);
} catch (InterruptedException e) {
e.printStackTrace();
}
th1.start();
ttt.flag=false;
th2.start();
}
}
class ticketer implements Runnable{
private  int tick=100;//若是不定义为静态变量  则 四个人卖四百张票  我们定义为静态使每个对象
 boolean flag=true ;
public void run() {
if (flag) {
while (true)
synchronized (ticketer.class) {
{
if (tick > 0) {
try {
Thread.sleep(15);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println("买票者   代码块 "+ Thread.currentThread().getName() + "---"+ tick--);
}
}
}
}else {
while (true) {
shouw();
}
}
}
public synchronized  void shouw() {
// TODO Auto-generated method stub
if (tick>0) {
try {
Thread.sleep(15);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println("买票者   函数中的 "+Thread.currentThread().getName()+"---"+tick--);
}
}
}

然而 我们从运行结果可以看出 虽然是两个线程都执行去 却 都是执行的函数中的  同步代码块 ,然而 我们先赋值的是 true 理论上先执行的应该是 代码块中的  然而却不是  这时为什么。

我们有三个线程  主线程   线程一  线程二

/***这里我们的加锁出现了问题

***加锁的两个要求 

一是 必须两个线程

二是 使用 同一个锁   然而 第一个条件满足了,第二个没满足  函数中调用时对象调用  所以对象是this,而调用代码块中的确实调用的 ticketer.class 类对象。。。所以在此出现了问题 我们将ticketer.class改为 this 这样就可以了 这样我们验证了  同步函数中使用的对象是 this

**/

private static  int tick=100;//若是
public static synchronized  void shouw() {
// TODO Auto-generated method stub
if (tick>0) {
try {
Thread.sleep(15);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println("买票者   函数中的 "+Thread.currentThread().getName()+"---"+tick--);
}
}
}


被静态函数修饰后也出现错误,因为静态方法中没有对象  静态加载时没有对象只有类对象  所以这也是不可以的 然而 此时我么将synchronized (this) 还原为synchronized (ticketer.class) 函数又正确了,所以验证了静态函数使用的对象是类对象。

3. 这里我们在讲两个单例模式的例子

 

package com.itheima;
/*******
 * 饿汉式单例模式
 */
class  singleeh{
private static final singleeh s=new singleeh();
private singleeh(){}
public static singleeh getinstace(){
return s;
}
}
<span style="color:#ff0000;">/*******
 * 懒汉式单例模式
 * 特点  延迟加载  
 * 有问题  多线程 出现问题 安全问题 
 * 加同步 解决
 * 双重判断 提高效率
 * 使用  对象锁为 字节码对象  该类所属的字节码对象! 
 * 
 * 此为   延迟加载的单例设计模式
 */</span>
class  singlelan{
private static  singlelan ss=null;
private singlelan(){}
public static singlelan getinstace(){
if(ss==null){//这样可以提高 多次 判断效率 效率 不先判断锁  先判断是否为空
synchronized (singlelan.class) {
if(ss==null)
//然而这儿使用线程睡眠有可能出现问题  所以  多线程使用这种方法  所以
ss=new singlelan();
}
}
return ss;
}
}
 


 

4.下面我们再来学习一个死锁的示例

死锁通俗的讲 两个线程每个线程都有一个锁 而都不同时放开 所以造成了死锁  函数不在执行下面我们来写个例子  

演示死锁的存在

 

class ttt1 implements Runnable{
private boolean flag;
 
public ttt1(boolean flag) {
this.flag = flag;
}
 
public void run() {
if (flag) {
synchronized (mylock.loObject11) {
System.out.println("if 的锁  aaaaa");
synchronized (mylock.loObject22) {
System.out.println("if 的锁  bbbbbb");
}
}
}else {
 
synchronized (mylock.loObject22) {
System.out.println("else 的锁  bbbbbb");
synchronized (mylock.loObject11) {
System.out.println("else 的锁  aaaaa");
}
}
}
}
}
class mylock {
static Object loObject11=new Object();
 
static Object loObject22=new Object();
}
 
public class singledemo {
public static void main(String [] arg){
Thread t1=new Thread(new ttt1(false));
Thread t2=new Thread(new ttt1(true));
t1.start();
t2.start();
}
 
}
<span style="color:#ff0000;">/*****
 *  分析结果的出现   
 *  都进入了第一个锁  而进入第二个锁时 两边的第二个都锁着   所以不能继续往下执行  当然也不排除 不死锁的情况 但是死锁额情况会出现 ,我们要避免 
 *  
 *  */</span>
 

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值