第一 ,线程的使用创建方式 继承或者 实现
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--);
}
}}
打引出来的结果会出现 0 和-1 等错误的票。现在我们来分析这个错误的原因。 假设线程1 执行到if (tick>0) 此时票数为1,而没执行票数减少,之后 休眠,在线程1休眠的时候线程二进入了 此时的票数让然为1 然后线程2执行判断语句。符合条件,没执行减少语句,票数仍然为1,然后休眠,然后线程三进入 判断 条件符合,休眠票数仍然为一。然而此时 线程1 复活了, 直接执行了买票的 语句 此时为0,线程2 也直接执行了 票数减少语句 票数成了-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>